Merge branch 'master' into 'qtpy-pyside2'
This commit is contained in:
commit
1cc13a2c7d
@ -47,6 +47,7 @@ common:
|
|||||||
chmod 755 $(LIB_PATH)/syncplay/
|
chmod 755 $(LIB_PATH)/syncplay/
|
||||||
cp -r resources/hicolor $(SHARE_PATH)/icons/
|
cp -r resources/hicolor $(SHARE_PATH)/icons/
|
||||||
cp -r resources/*.png $(LIB_PATH)/syncplay/resources/
|
cp -r resources/*.png $(LIB_PATH)/syncplay/resources/
|
||||||
|
cp -r resources/*.lua $(LIB_PATH)/syncplay/resources/
|
||||||
cp -r resources/lua/intf/*.lua $(LIB_PATH)/syncplay/resources/lua/intf/
|
cp -r resources/lua/intf/*.lua $(LIB_PATH)/syncplay/resources/lua/intf/
|
||||||
cp resources/hicolor/48x48/apps/syncplay.png $(SHARE_PATH)/app-install/icons/
|
cp resources/hicolor/48x48/apps/syncplay.png $(SHARE_PATH)/app-install/icons/
|
||||||
cp resources/hicolor/48x48/apps/syncplay.png $(SHARE_PATH)/pixmaps/
|
cp resources/hicolor/48x48/apps/syncplay.png $(SHARE_PATH)/pixmaps/
|
||||||
|
|||||||
11
README.md
11
README.md
@ -1,6 +1,6 @@
|
|||||||
# Syncplay
|
# Syncplay
|
||||||
|
|
||||||
Solution to synchronize video playback across multiple instances of mplayer2, mpv, Media Player Classic (MPC-HC) and VLC over the Internet.
|
Solution to synchronize video playback across multiple instances of mpv, VLC, MPC-HC, MPC-BE and mplayer2 over the Internet.
|
||||||
|
|
||||||
## Official website
|
## Official website
|
||||||
http://syncplay.pl
|
http://syncplay.pl
|
||||||
@ -12,13 +12,14 @@ http://syncplay.pl/download/
|
|||||||
|
|
||||||
Syncplay synchronises the position and play state of multiple media players so that the viewers can watch the same thing at the same time.
|
Syncplay synchronises the position and play state of multiple media players so that the viewers can watch the same thing at the same time.
|
||||||
This means that when one person pauses/unpauses playback or seeks (jumps position) within their media player then this will be replicated across all media players connected to the same server and in the same 'room' (viewing session).
|
This means that when one person pauses/unpauses playback or seeks (jumps position) within their media player then this will be replicated across all media players connected to the same server and in the same 'room' (viewing session).
|
||||||
When a new person joins they will also be synchronised.
|
When a new person joins they will also be synchronised. Syncplay also includes text-based chat so you can discuss a video as you watch it (or you could use third-party Voice over IP software to talk over a video).
|
||||||
|
|
||||||
## What it doesn't do
|
## What it doesn't do
|
||||||
|
|
||||||
Syncplay does not use video streaming or file sharing so each user must have their own copy of the media to be played. Syncplay does not synchronise player configuration, audio/subtitle track choice, playback rate, volume or filters. Furthermore, users must manually choose what file to play as Syncplay does not synchronise which file is open. Finally, Syncplay does not provide a voice or text-based chat platform to allow for discussion during playback as Syncplay is intended to be used in conjunction with third-party communication solutions such as IRC and Mumble.
|
Syncplay is not a file sharing service.
|
||||||
|
|
||||||
## Authors
|
## Authors
|
||||||
* *Concept and principal Syncplay developer* - Uriziel.
|
* *Initial concept and core internals developer* - Uriziel.
|
||||||
* *Other Syncplay coders* - daniel-123, Et0h.
|
* *GUI design and current lead developer* - Et0h.
|
||||||
* *Original SyncPlay code* - Tomasz Kowalczyk (Fluxid), who developed SyncPlay at https://github.com/fluxid/syncplay
|
* *Original SyncPlay code* - Tomasz Kowalczyk (Fluxid), who developed SyncPlay at https://github.com/fluxid/syncplay
|
||||||
|
* *Other contributors* - See http://syncplay.pl/about/development/
|
||||||
|
|||||||
@ -671,6 +671,8 @@ guiIcons = ['resources/accept.png', 'resources/arrow_undo.png', 'resources/clock
|
|||||||
'resources/tick.png', 'resources/lock_open.png', 'resources/empty_checkbox.png', 'resources/tick_checkbox.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/arrow_switch.png',
|
'resources/world_explore.png', 'resources/application_get.png', 'resources/cog.png', 'resources/arrow_switch.png',
|
||||||
'resources/film_go.png', 'resources/world_go.png', 'resources/arrow_refresh.png', 'resources/bullet_right_grey.png',
|
'resources/film_go.png', 'resources/world_go.png', 'resources/arrow_refresh.png', 'resources/bullet_right_grey.png',
|
||||||
|
'resources/user_comment.png',
|
||||||
|
'resources/error.png',
|
||||||
'resources/film_folder_edit.png',
|
'resources/film_folder_edit.png',
|
||||||
'resources/film_edit.png',
|
'resources/film_edit.png',
|
||||||
'resources/folder_film.png',
|
'resources/folder_film.png',
|
||||||
@ -679,7 +681,7 @@ guiIcons = ['resources/accept.png', 'resources/arrow_undo.png', 'resources/clock
|
|||||||
'resources/email_go.png',
|
'resources/email_go.png',
|
||||||
'resources/world_add.png', 'resources/film_add.png', 'resources/delete.png', 'resources/spinner.mng'
|
'resources/world_add.png', 'resources/film_add.png', 'resources/delete.png', 'resources/spinner.mng'
|
||||||
]
|
]
|
||||||
resources = ["resources/icon.ico", "resources/syncplay.png", "resources/license.rtf", "resources/third-party-notices.rtf"]
|
resources = ["resources/icon.ico", "resources/syncplay.png", "resources/syncplayintf.lua", "resources/license.rtf", "resources/third-party-notices.rtf"]
|
||||||
resources.extend(guiIcons)
|
resources.extend(guiIcons)
|
||||||
intf_resources = ["resources/lua/intf/syncplay.lua"]
|
intf_resources = ["resources/lua/intf/syncplay.lua"]
|
||||||
|
|
||||||
|
|||||||
BIN
resources/error.png
Normal file
BIN
resources/error.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 666 B |
979
resources/syncplayintf.lua
Normal file
979
resources/syncplayintf.lua
Normal file
@ -0,0 +1,979 @@
|
|||||||
|
-- syncplayintf.lua -- An interface for communication between mpv and Syncplay
|
||||||
|
-- Author: Etoh, utilising repl.lua code by James Ross-Gowan (see below)
|
||||||
|
-- Thanks: RiCON, James Ross-Gowan, Argon-, wm4, uau
|
||||||
|
|
||||||
|
-- Includes code copied/adapted from repl.lua -- A graphical REPL for mpv input commands
|
||||||
|
--
|
||||||
|
-- c 2016, James Ross-Gowan
|
||||||
|
--
|
||||||
|
-- Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
-- purpose with or without fee is hereby granted, provided that the above
|
||||||
|
-- copyright notice and this permission notice appear in all copies.
|
||||||
|
--
|
||||||
|
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
-- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
-- SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
-- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||||
|
-- OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||||
|
-- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
-- See https://github.com/rossy/mpv-repl for a copy of repl.lua
|
||||||
|
|
||||||
|
local CANVAS_WIDTH = 1920
|
||||||
|
local CANVAS_HEIGHT = 1080
|
||||||
|
local ROW_HEIGHT = 100
|
||||||
|
local chat_format = "{\\fs50}{\an1}"
|
||||||
|
local max_scrolling_rows = 100
|
||||||
|
local MOVEMENT_PER_SECOND = 200
|
||||||
|
local TICK_INTERVAL = 0.01
|
||||||
|
local CHAT_MODE_CHATROOM = "Chatroom"
|
||||||
|
local CHAT_MODE_SUBTITLE = "Subtitle"
|
||||||
|
local CHAT_MODE_SCROLLING = "Scrolling"
|
||||||
|
local last_chat_time = 0
|
||||||
|
local use_alpha_rows_for_chat = true
|
||||||
|
local MOOD_NEUTRAL = 0
|
||||||
|
local MOOD_BAD = 1
|
||||||
|
local MOOD_GOOD = 2
|
||||||
|
local WORDWRAPIFY_MAGICWORD = "{\\\\fscx0} {\\\\fscx100}"
|
||||||
|
local SCROLLING_ADDITIONAL_BOTTOM_MARGIN = 75
|
||||||
|
local default_oscvisibility_state = "never"
|
||||||
|
|
||||||
|
local ALPHA_WARNING_TEXT_COLOUR = "FF00FF" -- RBG
|
||||||
|
local HINT_TEXT_COLOUR = "AAAAAA" -- RBG
|
||||||
|
local NEUTRAL_ALERT_TEXT_COLOUR = "FFFFFF" -- RBG
|
||||||
|
local BAD_ALERT_TEXT_COLOUR = "0000FF" -- RBG
|
||||||
|
local GOOD_ALERT_TEXT_COLOUR = "00FF00" -- RBG
|
||||||
|
local NOTIFICATION_TEXT_COLOUR = "FFFF00" -- RBG
|
||||||
|
|
||||||
|
local FONT_SIZE_MULTIPLIER = 2
|
||||||
|
|
||||||
|
local chat_log = {}
|
||||||
|
|
||||||
|
local assdraw = require "mp.assdraw"
|
||||||
|
|
||||||
|
local opt = require 'mp.options'
|
||||||
|
|
||||||
|
local repl_active = false
|
||||||
|
local insert_mode = false
|
||||||
|
local line = ''
|
||||||
|
local cursor = 1
|
||||||
|
local key_hints_enabled = false
|
||||||
|
|
||||||
|
function format_scrolling(xpos, ypos, text)
|
||||||
|
local chat_message = "\n"..chat_format .. "{\\pos("..xpos..","..ypos..")\\q2}"..text.."\\N\\n"
|
||||||
|
return string.format(chat_message)
|
||||||
|
end
|
||||||
|
|
||||||
|
function format_chatroom(text)
|
||||||
|
local chat_message = chat_format .. text .."\\N\\n"
|
||||||
|
return string.format(chat_message)
|
||||||
|
end
|
||||||
|
|
||||||
|
function clear_chat()
|
||||||
|
chat_log = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local alert_osd = ""
|
||||||
|
local last_alert_osd_time = nil
|
||||||
|
local alert_osd_mood = MOOD_NEUTRAL
|
||||||
|
|
||||||
|
local notification_osd = ""
|
||||||
|
local last_notification_osd_time = nil
|
||||||
|
local notification_osd_mood = MOOD_NEUTRAL
|
||||||
|
|
||||||
|
function set_alert_osd(osd_message, mood)
|
||||||
|
alert_osd = osd_message
|
||||||
|
last_alert_osd_time = mp.get_time()
|
||||||
|
alert_osd_mood = mood
|
||||||
|
end
|
||||||
|
|
||||||
|
function set_notification_osd(osd_message, mood)
|
||||||
|
notification_osd = osd_message
|
||||||
|
last_notification_osd_time = mp.get_time()
|
||||||
|
notification_osd_mood = mood
|
||||||
|
end
|
||||||
|
|
||||||
|
function add_chat(chat_message, mood)
|
||||||
|
last_chat_time = mp.get_time()
|
||||||
|
local entry = #chat_log+1
|
||||||
|
for i = 1, #chat_log do
|
||||||
|
if chat_log[i].text == '' then
|
||||||
|
entry = i
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local row = ((entry-1) % max_scrolling_rows)+1
|
||||||
|
if opts['chatOutputMode'] == CHAT_MODE_CHATROOM then
|
||||||
|
if entry > opts['chatMaxLines'] then
|
||||||
|
table.remove(chat_log, 1)
|
||||||
|
entry = entry - 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
chat_log[entry] = { xpos=CANVAS_WIDTH, timecreated=mp.get_time(), text=tostring(chat_message), row=row }
|
||||||
|
end
|
||||||
|
|
||||||
|
function chat_update()
|
||||||
|
local ass = assdraw.ass_new()
|
||||||
|
local chat_ass = ''
|
||||||
|
local rowsAdded = 0
|
||||||
|
local to_add = ''
|
||||||
|
local incrementRow = 0
|
||||||
|
if opts['chatOutputMode'] == CHAT_MODE_CHATROOM and chat_log ~= {} then
|
||||||
|
local timedelta = mp.get_time() - last_chat_time
|
||||||
|
if timedelta >= opts['chatTimeout'] then
|
||||||
|
clear_chat()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rowsAdded,to_add = process_alert_osd()
|
||||||
|
if to_add ~= nil and to_add ~= "" then
|
||||||
|
chat_ass = to_add
|
||||||
|
end
|
||||||
|
incrementRow,to_add = process_notification_osd(rowsAdded)
|
||||||
|
rowsAdded = rowsAdded + incrementRow
|
||||||
|
if to_add ~= nil and to_add ~= "" then
|
||||||
|
chat_ass = chat_ass .. to_add
|
||||||
|
end
|
||||||
|
|
||||||
|
if #chat_log > 0 then
|
||||||
|
for i = 1, #chat_log do
|
||||||
|
local to_add = process_chat_item(i,rowsAdded)
|
||||||
|
if to_add ~= nil and to_add ~= "" then
|
||||||
|
chat_ass = chat_ass .. to_add
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local xpos = opts['chatLeftMargin']
|
||||||
|
local ypos = opts['chatTopMargin']
|
||||||
|
chat_ass = "\n".."{\\pos("..xpos..","..ypos..")}".. chat_ass
|
||||||
|
|
||||||
|
if use_alpha_rows_for_chat == false and opts['chatDirectInput'] == true then
|
||||||
|
local alphawarning_ass = assdraw.ass_new()
|
||||||
|
alphawarning_ass = "{\\a6}{\\1c&H"..ALPHA_WARNING_TEXT_COLOUR.."}"..opts['alphakey-mode-warning-first-line'].."\n{\\a6}{\\1c&H"..ALPHA_WARNING_TEXT_COLOUR.."}"..opts['alphakey-mode-warning-second-line']
|
||||||
|
ass:append(alphawarning_ass)
|
||||||
|
elseif opts['chatOutputMode'] == CHAT_MODE_CHATROOM and opts['chatInputPosition'] == "Top" then
|
||||||
|
ass:append(chat_ass)
|
||||||
|
ass:append(input_ass())
|
||||||
|
else
|
||||||
|
ass:append(input_ass())
|
||||||
|
ass:append(chat_ass)
|
||||||
|
end
|
||||||
|
mp.set_osd_ass(CANVAS_WIDTH,CANVAS_HEIGHT, ass.text)
|
||||||
|
end
|
||||||
|
|
||||||
|
function process_alert_osd()
|
||||||
|
local rowsCreated = 0
|
||||||
|
local stringToAdd = ""
|
||||||
|
if alert_osd ~= "" and mp.get_time() - last_alert_osd_time < opts['alertTimeout'] and last_alert_osd_time ~= nil then
|
||||||
|
local messageColour
|
||||||
|
if alert_osd_mood == MOOD_NEUTRAL then
|
||||||
|
messageColour = "{\\1c&H"..NEUTRAL_ALERT_TEXT_COLOUR.."}"
|
||||||
|
elseif alert_osd_mood == MOOD_BAD then
|
||||||
|
messageColour = "{\\1c&H"..BAD_ALERT_TEXT_COLOUR.."}"
|
||||||
|
elseif alert_osd_mood == MOOD_GOOD then
|
||||||
|
messageColour = "{\\1c&H"..GOOD_ALERT_TEXT_COLOUR.."}"
|
||||||
|
end
|
||||||
|
local messageString = wordwrapify_string(alert_osd)
|
||||||
|
local startRow = 0
|
||||||
|
if messageString ~= '' and messageString ~= nil then
|
||||||
|
local toDisplay
|
||||||
|
rowsCreated = rowsCreated + 1
|
||||||
|
messageString = messageColour..messageString
|
||||||
|
if stringToAdd ~= "" then
|
||||||
|
stringToAdd = stringToAdd .. format_chatroom(messageString)
|
||||||
|
else
|
||||||
|
stringToAdd = format_chatroom(messageString)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return rowsCreated, stringToAdd
|
||||||
|
end
|
||||||
|
|
||||||
|
function process_notification_osd(startRow)
|
||||||
|
local rowsCreated = 0
|
||||||
|
local startRow = startRow
|
||||||
|
local stringToAdd = ""
|
||||||
|
if notification_osd ~= "" and mp.get_time() - last_notification_osd_time < opts['alertTimeout'] and last_notification_osd_time ~= nil then
|
||||||
|
local messageColour
|
||||||
|
messageColour = "{\\1c&H"..NOTIFICATION_TEXT_COLOUR.."}"
|
||||||
|
local messageString
|
||||||
|
messageString = wordwrapify_string(notification_osd)
|
||||||
|
messageString = messageColour..messageString
|
||||||
|
messageString = format_chatroom(messageString)
|
||||||
|
stringToAdd = messageString
|
||||||
|
rowsCreated = 1
|
||||||
|
end
|
||||||
|
return rowsCreated, stringToAdd
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function process_chat_item(i, rowsAdded)
|
||||||
|
if opts['chatOutputMode'] == CHAT_MODE_CHATROOM then
|
||||||
|
return process_chat_item_chatroom(i, rowsAdded)
|
||||||
|
elseif opts['chatOutputMode'] == CHAT_MODE_SCROLLING then
|
||||||
|
return process_chat_item_scrolling(i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function process_chat_item_scrolling(i)
|
||||||
|
local timecreated = chat_log[i].timecreated
|
||||||
|
local timedelta = mp.get_time() - timecreated
|
||||||
|
local xpos = CANVAS_WIDTH - (timedelta*MOVEMENT_PER_SECOND)
|
||||||
|
local text = chat_log[i].text
|
||||||
|
if text ~= '' then
|
||||||
|
local roughlen = string.len(text) * (opts['chatOutputRelativeFontSize']*FONT_SIZE_MULTIPLIER) * 1.5
|
||||||
|
if xpos > (-1*roughlen) then
|
||||||
|
local row = chat_log[i].row-1+opts['scrollingFirstRowOffset']
|
||||||
|
local ypos = opts['chatTopMargin']+(row * (opts['chatOutputRelativeFontSize']*FONT_SIZE_MULTIPLIER))
|
||||||
|
return format_scrolling(xpos,ypos,text)
|
||||||
|
else
|
||||||
|
chat_log[i].text = ''
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function process_chat_item_chatroom(i, startRow)
|
||||||
|
local text = chat_log[i].text
|
||||||
|
if text ~= '' then
|
||||||
|
local text = wordwrapify_string(text)
|
||||||
|
local rowNumber = i+startRow-1
|
||||||
|
return(format_chatroom(text))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function process_chat_item_subtitle(i)
|
||||||
|
local timecreated = chat_log[i].timecreated
|
||||||
|
local timedelta = mp.get_time() - timecreated
|
||||||
|
local xpos = CANVAS_WIDTH - (timedelta*MOVEMENT_PER_SECOND)
|
||||||
|
local text = chat_log[i].text
|
||||||
|
if text ~= '' then
|
||||||
|
local roughlen = string.len(text) * (opts['chatOutputRelativeFontSize']*FONT_SIZE_MULTIPLIER)
|
||||||
|
if xpos > (-1*roughlen) then
|
||||||
|
local row = chat_log[i].row
|
||||||
|
local ypos = row * (opts['chatOutputRelativeFontSize']*FONT_SIZE_MULTIPLIER)
|
||||||
|
return(format_scrolling(xpos,ypos,text))
|
||||||
|
else
|
||||||
|
chat_log[i].text = ''
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
chat_timer=mp.add_periodic_timer(TICK_INTERVAL, chat_update)
|
||||||
|
|
||||||
|
mp.register_script_message('chat', function(e)
|
||||||
|
add_chat(e)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Chat OSD
|
||||||
|
|
||||||
|
mp.register_script_message('chat-osd-neutral', function(e)
|
||||||
|
add_chat(e,MOOD_NEUTRAL)
|
||||||
|
end)
|
||||||
|
|
||||||
|
mp.register_script_message('chat-osd-bad', function(e)
|
||||||
|
add_chat(e,MOOD_BAD)
|
||||||
|
end)
|
||||||
|
|
||||||
|
mp.register_script_message('chat-osd-good', function(e)
|
||||||
|
add_chat(e,MOOD_GOOD)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Alert OSD
|
||||||
|
|
||||||
|
mp.register_script_message('alert-osd-neutral', function(e)
|
||||||
|
set_alert_osd(e,MOOD_NEUTRAL)
|
||||||
|
end)
|
||||||
|
|
||||||
|
mp.register_script_message('alert-osd-bad', function(e)
|
||||||
|
set_alert_osd(e,MOOD_BAD)
|
||||||
|
end)
|
||||||
|
|
||||||
|
mp.register_script_message('alert-osd-good', function(e)
|
||||||
|
set_alert_osd(e,MOOD_GOOD)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Notification OSD
|
||||||
|
|
||||||
|
mp.register_script_message('notification-osd-neutral', function(e)
|
||||||
|
set_notification_osd(e,MOOD_NEUTRAL)
|
||||||
|
end)
|
||||||
|
|
||||||
|
mp.register_script_message('notification-osd-bad', function(e)
|
||||||
|
set_notification_osd(e,MOOD_BAD)
|
||||||
|
end)
|
||||||
|
|
||||||
|
mp.register_script_message('notification-osd-good', function(e)
|
||||||
|
set_notification_osd(e,MOOD_GOOD)
|
||||||
|
end)
|
||||||
|
|
||||||
|
--
|
||||||
|
|
||||||
|
mp.register_script_message('set_syncplayintf_options', function(e)
|
||||||
|
set_syncplayintf_options(e)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Default options
|
||||||
|
local utils = require 'mp.utils'
|
||||||
|
local options = require 'mp.options'
|
||||||
|
opts = {
|
||||||
|
|
||||||
|
-- All drawing is scaled by this value, including the text borders and the
|
||||||
|
-- cursor. Change it if you have a high-DPI display.
|
||||||
|
scale = 1,
|
||||||
|
-- Set the font used for the REPL and the console. This probably doesn't
|
||||||
|
-- have to be a monospaced font.
|
||||||
|
['chatInputFontFamily'] = 'monospace',
|
||||||
|
-- Enable/Disable
|
||||||
|
['chatInputEnabled'] = true,
|
||||||
|
['chatOutputEnabled'] = true,
|
||||||
|
['OscVisibilityChangeCompatible'] = false,
|
||||||
|
-- Set the font size used for the REPL and the console. This will be
|
||||||
|
-- multiplied by "scale."
|
||||||
|
['chatInputRelativeFontSize'] = 14,
|
||||||
|
['chatInputFontWeight'] = 1,
|
||||||
|
['chatInputFontUnderline'] = false,
|
||||||
|
['chatInputFontColor'] = "#000000",
|
||||||
|
['chatInputPosition'] = "Top",
|
||||||
|
['MaxChatMessageLength'] = 500,
|
||||||
|
['chatOutputFontFamily'] = "sans serif",
|
||||||
|
['chatOutputFontSize'] = 50,
|
||||||
|
['chatOutputFontWeight'] = 1,
|
||||||
|
['chatOutputFontUnderline'] = false,
|
||||||
|
['chatOutputFontColor'] = "#FFFFFF",
|
||||||
|
['chatOutputMode'] = "Chatroom",
|
||||||
|
['scrollingFirstRowOffset'] = 2,
|
||||||
|
-- Can be "Chatroom", "Subtitle" or "Scrolling" style
|
||||||
|
['chatMaxLines'] = 7,
|
||||||
|
['chatTopMargin'] = 25,
|
||||||
|
['chatLeftMargin'] = 20,
|
||||||
|
['chatDirectInput'] = true,
|
||||||
|
--
|
||||||
|
['notificationTimeout'] = 3,
|
||||||
|
['alertTimeout'] = 5,
|
||||||
|
['chatTimeout'] = 7,
|
||||||
|
--
|
||||||
|
['inputPromptStartCharacter'] = ">",
|
||||||
|
['inputPromptEndCharacter'] = "<",
|
||||||
|
['backslashSubstituteCharacter'] = "|",
|
||||||
|
--Lang:
|
||||||
|
['mpv-key-tab-hint'] = "[TAB] to toggle access to alphabet row key shortcuts.",
|
||||||
|
['mpv-key-hint'] = "[ENTER] to send message. [ESC] to escape chat mode.",
|
||||||
|
['alphakey-mode-warning-first-line'] = "You can temporarily use old mpv bindings with a-z keys.",
|
||||||
|
['alphakey-mode-warning-second-line'] = "Press [TAB] to return to Syncplay chat mode.",
|
||||||
|
}
|
||||||
|
|
||||||
|
function detect_platform()
|
||||||
|
local o = {}
|
||||||
|
-- Kind of a dumb way of detecting the platform but whatever
|
||||||
|
if mp.get_property_native('options/vo-mmcss-profile', o) ~= o then
|
||||||
|
return 'windows'
|
||||||
|
elseif mp.get_property_native('options/input-app-events', o) ~= o then
|
||||||
|
return 'macos'
|
||||||
|
end
|
||||||
|
return 'linux'
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Pick a better default font for Windows and macOS
|
||||||
|
local platform = detect_platform()
|
||||||
|
if platform == 'windows' then
|
||||||
|
opts.font = 'Consolas'
|
||||||
|
elseif platform == 'macos' then
|
||||||
|
opts.font = 'Menlo'
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Apply user-set options
|
||||||
|
options.read_options(opts)
|
||||||
|
|
||||||
|
-- Escape a string for verbatim display on the OSD
|
||||||
|
function ass_escape(str)
|
||||||
|
-- There is no escape for '\' in ASS (I think?) but '\' is used verbatim if
|
||||||
|
-- it isn't followed by a recognised character, so add a zero-width
|
||||||
|
-- non-breaking space
|
||||||
|
str = str:gsub('\\', '\\\239\187\191')
|
||||||
|
str = str:gsub('{', '\\{')
|
||||||
|
str = str:gsub('}', '\\}')
|
||||||
|
-- Precede newlines with a ZWNBSP to prevent ASS's weird collapsing of
|
||||||
|
-- consecutive newlines
|
||||||
|
str = str:gsub('\n', '\239\187\191\n')
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
|
||||||
|
function update()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
function input_ass()
|
||||||
|
if not repl_active then
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
last_chat_time = mp.get_time() -- to keep chat messages showing while entering input
|
||||||
|
local bold
|
||||||
|
if opts['chatInputFontWeight'] < 75 then
|
||||||
|
bold = 0
|
||||||
|
else
|
||||||
|
bold = 1
|
||||||
|
end
|
||||||
|
local underline = opts['chatInputFontUnderline'] and 1 or 0
|
||||||
|
local red = string.sub(opts['chatInputFontColor'],2,3)
|
||||||
|
local green = string.sub(opts['chatInputFontColor'],4,5)
|
||||||
|
local blue = string.sub(opts['chatInputFontColor'],6,7)
|
||||||
|
local fontColor = blue .. green .. red
|
||||||
|
local style = '{\\r' ..
|
||||||
|
'\\1a&H00&\\3a&H00&\\4a&H99&' ..
|
||||||
|
'\\1c&H'..fontColor..'&\\3c&H111111&\\4c&H000000&' ..
|
||||||
|
'\\fn' .. opts['chatInputFontFamily'] .. '\\fs' .. (opts['chatInputRelativeFontSize']*FONT_SIZE_MULTIPLIER) .. '\\b' .. bold ..
|
||||||
|
'\\bord2\\xshad0\\yshad1\\fsp0\\q1}'
|
||||||
|
|
||||||
|
local after_style = '{\\u' .. underline .. '}'
|
||||||
|
local cheight = opts['chatInputRelativeFontSize'] * FONT_SIZE_MULTIPLIER * 8
|
||||||
|
local cglyph = '_'
|
||||||
|
local before_cur = wordwrapify_string(ass_escape(line:sub(1, cursor - 1)))
|
||||||
|
local after_cur = wordwrapify_string(ass_escape(line:sub(cursor)))
|
||||||
|
local secondary_pos = "10,"..tostring(10+(opts['chatInputRelativeFontSize']*FONT_SIZE_MULTIPLIER))
|
||||||
|
|
||||||
|
local alignment = 7
|
||||||
|
local position = "5,5"
|
||||||
|
local start_marker = opts['inputPromptStartCharacter']
|
||||||
|
local end_marker = ""
|
||||||
|
if opts['chatInputPosition'] == "Middle" then
|
||||||
|
alignment = 5
|
||||||
|
position = tostring(CANVAS_WIDTH/2)..","..tostring(CANVAS_HEIGHT/2)
|
||||||
|
secondary_pos = tostring(CANVAS_WIDTH/2)..","..tostring((CANVAS_HEIGHT/2)+20+(opts['chatInputRelativeFontSize']*FONT_SIZE_MULTIPLIER))
|
||||||
|
end_marker = "{\\u0}"..opts['inputPromptEndCharacter']
|
||||||
|
elseif opts['chatInputPosition'] == "Bottom" then
|
||||||
|
alignment = 1
|
||||||
|
position = tostring(5)..","..tostring(CANVAS_HEIGHT-5)
|
||||||
|
secondary_pos = "10,"..tostring(CANVAS_HEIGHT-(20+(opts['chatInputRelativeFontSize']*FONT_SIZE_MULTIPLIER)))
|
||||||
|
end
|
||||||
|
|
||||||
|
local osd_help_message = opts['mpv-key-hint']
|
||||||
|
if opts['chatDirectInput'] then
|
||||||
|
osd_help_message = opts['mpv-key-tab-hint'] .. " " .. osd_help_message
|
||||||
|
end
|
||||||
|
local help_prompt = '\\N\\n{\\an'..alignment..'\\pos('..secondary_pos..')\\fn' .. opts['chatOutputFontFamily'] .. '\\fs' .. ((opts['chatInputRelativeFontSize']*FONT_SIZE_MULTIPLIER)/1.25) .. '\\1c&H'..HINT_TEXT_COLOUR..'}' .. osd_help_message
|
||||||
|
|
||||||
|
local firststyle = "{\\an"..alignment.."}{\\pos("..position..")}"
|
||||||
|
if opts['chatOutputEnabled'] and opts['chatOutputMode'] == CHAT_MODE_CHATROOM and opts['chatInputPosition'] == "Top" then
|
||||||
|
firststyle = get_output_style().."{'\\1c&H'"..fontColor.."}"
|
||||||
|
before_cur = before_cur .. firststyle
|
||||||
|
after_cur = after_cur .. firststyle
|
||||||
|
help_prompt = '\\N\\n'..firststyle..'{\\1c&H'..HINT_TEXT_COLOUR..'}' .. osd_help_message .. '\\N\\n'
|
||||||
|
end
|
||||||
|
if key_hints_enabled == false then help_prompt = "" end
|
||||||
|
|
||||||
|
return firststyle..style..start_marker.." "..after_style..before_cur..style..cglyph..style..after_style..after_cur..end_marker..help_prompt
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_output_style()
|
||||||
|
local bold
|
||||||
|
if opts['chatOutputFontWeight'] < 75 then
|
||||||
|
bold = 0
|
||||||
|
else
|
||||||
|
bold = 1
|
||||||
|
end
|
||||||
|
local underline = opts['chatOutputFontUnderline'] and 1 or 0
|
||||||
|
local red = string.sub(opts['chatOutputFontColor'],2,3)
|
||||||
|
local green = string.sub(opts['chatOutputFontColor'],4,5)
|
||||||
|
local blue = string.sub(opts['chatOutputFontColor'],6,7)
|
||||||
|
local fontColor = blue .. green .. red
|
||||||
|
local style = '{\\r' ..
|
||||||
|
'\\1a&H00&\\3a&H00&\\4a&H99&' ..
|
||||||
|
'\\1c&H'..fontColor..'&\\3c&H111111&\\4c&H000000&' ..
|
||||||
|
'\\fn' .. opts['chatOutputFontFamily'] .. '\\fs' .. (opts['chatOutputRelativeFontSize']*FONT_SIZE_MULTIPLIER) .. '\\b' .. bold ..
|
||||||
|
'\\u' .. underline .. '\\a5\\MarginV=500' .. '}'
|
||||||
|
|
||||||
|
--mp.osd_message("",0)
|
||||||
|
return style
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function escape()
|
||||||
|
set_active(false)
|
||||||
|
clear()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set the REPL visibility (`, Esc)
|
||||||
|
function set_active(active)
|
||||||
|
if use_alpha_rows_for_chat == false then active = false end
|
||||||
|
if active == repl_active then return end
|
||||||
|
if active then
|
||||||
|
repl_active = true
|
||||||
|
insert_mode = false
|
||||||
|
mp.enable_key_bindings('repl-input', 'allow-hide-cursor+allow-vo-dragging')
|
||||||
|
else
|
||||||
|
repl_active = false
|
||||||
|
mp.disable_key_bindings('repl-input')
|
||||||
|
end
|
||||||
|
if default_oscvisibility_state ~= "never" and opts['OscVisibilityChangeCompatible'] == true then
|
||||||
|
if active then
|
||||||
|
mp.commandv("script-message", "osc-visibility","never", "no-osd")
|
||||||
|
else
|
||||||
|
mp.commandv("script-message", "osc-visibility",default_oscvisibility_state, "no-osd")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Show the repl if hidden and replace its contents with 'text'
|
||||||
|
-- (script-message-to repl type)
|
||||||
|
function show_and_type(text)
|
||||||
|
text = text or ''
|
||||||
|
|
||||||
|
line = text
|
||||||
|
cursor = line:len() + 1
|
||||||
|
insert_mode = false
|
||||||
|
if repl_active then
|
||||||
|
update()
|
||||||
|
else
|
||||||
|
set_active(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Naive helper function to find the next UTF-8 character in 'str' after 'pos'
|
||||||
|
-- by skipping continuation bytes. Assumes 'str' contains valid UTF-8.
|
||||||
|
function next_utf8(str, pos)
|
||||||
|
if pos > str:len() then return pos end
|
||||||
|
repeat
|
||||||
|
pos = pos + 1
|
||||||
|
until pos > str:len() or str:byte(pos) < 0x80 or str:byte(pos) > 0xbf
|
||||||
|
return pos
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Naive helper function to find the next UTF-8 character in 'str' after 'pos'
|
||||||
|
-- by skipping continuation bytes. Assumes 'str' contains valid UTF-8.
|
||||||
|
|
||||||
|
|
||||||
|
-- As above, but finds the previous UTF-8 charcter in 'str' before 'pos'
|
||||||
|
function prev_utf8(str, pos)
|
||||||
|
if pos <= 1 then return pos end
|
||||||
|
repeat
|
||||||
|
pos = pos - 1
|
||||||
|
until pos <= 1 or str:byte(pos) < 0x80 or str:byte(pos) > 0xbf
|
||||||
|
return pos
|
||||||
|
end
|
||||||
|
|
||||||
|
function trim_string(line,maxCharacters)
|
||||||
|
-- Naive helper function to find the next UTF-8 character in 'str' after 'pos'
|
||||||
|
-- by skipping continuation bytes. Assumes 'str' contains valid UTF-8.
|
||||||
|
|
||||||
|
local str = line
|
||||||
|
if str == nil or str == "" or str:len() <= maxCharacters then
|
||||||
|
return str, ""
|
||||||
|
end
|
||||||
|
local pos = 0
|
||||||
|
local oldPos = -1
|
||||||
|
local chars = 0
|
||||||
|
|
||||||
|
repeat
|
||||||
|
oldPos = pos
|
||||||
|
pos = next_utf8(str, pos)
|
||||||
|
chars = chars + 1
|
||||||
|
until pos == oldPos or chars > maxCharacters
|
||||||
|
return str:sub(1,pos-1), str:sub(pos)
|
||||||
|
end
|
||||||
|
|
||||||
|
function wordwrapify_string(line)
|
||||||
|
-- Used to ensure characters wrap on a per-character rather than per-word basis
|
||||||
|
-- to avoid issues with long filenames, etc.
|
||||||
|
|
||||||
|
local str = line
|
||||||
|
if str == nil or str == "" then
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
local newstr = ""
|
||||||
|
local currentChar = 0
|
||||||
|
local nextChar = 0
|
||||||
|
local chars = 0
|
||||||
|
local maxChars = str:len()
|
||||||
|
|
||||||
|
repeat
|
||||||
|
nextChar = next_utf8(str, currentChar)
|
||||||
|
if nextChar == currentChar then
|
||||||
|
return newstr
|
||||||
|
end
|
||||||
|
local charToTest = str:sub(currentChar,nextChar-1)
|
||||||
|
if charToTest ~= "\\" and charToTest ~= "{" and charToTest ~= "}" and charToTest ~= "%" then
|
||||||
|
newstr = newstr .. WORDWRAPIFY_MAGICWORD .. str:sub(currentChar,nextChar-1)
|
||||||
|
else
|
||||||
|
newstr = newstr .. str:sub(currentChar,nextChar-1)
|
||||||
|
end
|
||||||
|
currentChar = nextChar
|
||||||
|
until currentChar > maxChars
|
||||||
|
newstr = string.gsub(newstr,opts['backslashSubstituteCharacter'], '\\\239\187\191') -- Workaround for \ escape issues
|
||||||
|
return newstr
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function trim_input()
|
||||||
|
-- Naive helper function to find the next UTF-8 character in 'str' after 'pos'
|
||||||
|
-- by skipping continuation bytes. Assumes 'str' contains valid UTF-8.
|
||||||
|
|
||||||
|
local str = line
|
||||||
|
if str == nil or str == "" or str:len() <= opts['MaxChatMessageLength'] then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local pos = 0
|
||||||
|
local oldPos = -1
|
||||||
|
local chars = 0
|
||||||
|
|
||||||
|
repeat
|
||||||
|
oldPos = pos
|
||||||
|
pos = next_utf8(str, pos)
|
||||||
|
chars = chars + 1
|
||||||
|
until pos == oldPos or chars > opts['MaxChatMessageLength']
|
||||||
|
line = line:sub(1,pos-1)
|
||||||
|
if cursor > pos then
|
||||||
|
cursor = pos
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Insert a character at the current cursor position (' '-'~', Shift+Enter)
|
||||||
|
function handle_char_input(c)
|
||||||
|
if c == nil then return end
|
||||||
|
if c == "\\" then c = opts['backslashSubstituteCharacter'] end
|
||||||
|
if key_hints_enabled and (string.len(line) > 0 or opts['chatDirectInput'] == false) then
|
||||||
|
key_hints_enabled = false
|
||||||
|
end
|
||||||
|
set_active(true)
|
||||||
|
if insert_mode then
|
||||||
|
line = line:sub(1, cursor - 1) .. c .. line:sub(next_utf8(line, cursor))
|
||||||
|
else
|
||||||
|
line = line:sub(1, cursor - 1) .. c .. line:sub(cursor)
|
||||||
|
end
|
||||||
|
cursor = cursor + c:len()
|
||||||
|
trim_input()
|
||||||
|
update()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Remove the character behind the cursor (Backspace)
|
||||||
|
function handle_backspace()
|
||||||
|
if cursor <= 1 then return end
|
||||||
|
local prev = prev_utf8(line, cursor)
|
||||||
|
line = line:sub(1, prev - 1) .. line:sub(cursor)
|
||||||
|
cursor = prev
|
||||||
|
update()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Remove the character in front of the cursor (Del)
|
||||||
|
function handle_del()
|
||||||
|
if cursor > line:len() then return end
|
||||||
|
line = line:sub(1, cursor - 1) .. line:sub(next_utf8(line, cursor))
|
||||||
|
update()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Toggle insert mode (Ins)
|
||||||
|
function handle_ins()
|
||||||
|
insert_mode = not insert_mode
|
||||||
|
end
|
||||||
|
|
||||||
|
--local was_active_before_tab = false
|
||||||
|
|
||||||
|
function handle_tab()
|
||||||
|
use_alpha_rows_for_chat = not use_alpha_rows_for_chat
|
||||||
|
if use_alpha_rows_for_chat then
|
||||||
|
mp.enable_key_bindings('repl-alpha-input')
|
||||||
|
--set_active(was_active_before_tab)
|
||||||
|
else
|
||||||
|
mp.disable_key_bindings('repl-alpha-input')
|
||||||
|
--was_active_before_tab = repl_active
|
||||||
|
--set_active(false)
|
||||||
|
escape()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Move the cursor to the next character (Right)
|
||||||
|
function next_char(amount)
|
||||||
|
cursor = next_utf8(line, cursor)
|
||||||
|
update()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Move the cursor to the previous character (Left)
|
||||||
|
function prev_char(amount)
|
||||||
|
cursor = prev_utf8(line, cursor)
|
||||||
|
update()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Clear the current line (Ctrl+C)
|
||||||
|
function clear()
|
||||||
|
line = ''
|
||||||
|
cursor = 1
|
||||||
|
insert_mode = false
|
||||||
|
update()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Close the REPL if the current line is empty, otherwise do nothing (Ctrl+D)
|
||||||
|
function maybe_exit()
|
||||||
|
if line == '' then
|
||||||
|
set_active(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Run the current command and clear the line (Enter)
|
||||||
|
function handle_enter()
|
||||||
|
if not repl_active then
|
||||||
|
set_active(true)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
set_active(false)
|
||||||
|
|
||||||
|
if line == '' then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
key_hints_enabled = false
|
||||||
|
line = string.gsub(line,"\\", "\\\\")
|
||||||
|
line = string.gsub(line,"\"", "\\\"")
|
||||||
|
mp.command('print-text "<chat>'..line..'</chat>"')
|
||||||
|
clear()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Move the cursor to the beginning of the line (HOME)
|
||||||
|
function go_home()
|
||||||
|
cursor = 1
|
||||||
|
update()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Move the cursor to the end of the line (END)
|
||||||
|
function go_end()
|
||||||
|
cursor = line:len() + 1
|
||||||
|
update()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Delete from the cursor to the end of the line (Ctrl+K)
|
||||||
|
function del_to_eol()
|
||||||
|
line = line:sub(1, cursor - 1)
|
||||||
|
update()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Delete from the cursor back to the start of the line (Ctrl+U)
|
||||||
|
function del_to_start()
|
||||||
|
line = line:sub(cursor)
|
||||||
|
cursor = 1
|
||||||
|
update()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Returns a string of UTF-8 text from the clipboard (or the primary selection)
|
||||||
|
function get_clipboard(clip)
|
||||||
|
if platform == 'linux' then
|
||||||
|
local res = utils.subprocess({ args = {
|
||||||
|
'xclip', '-selection', clip and 'clipboard' or 'primary', '-out'
|
||||||
|
} })
|
||||||
|
if not res.error then
|
||||||
|
return res.stdout
|
||||||
|
end
|
||||||
|
elseif platform == 'windows' then
|
||||||
|
local res = utils.subprocess({ args = {
|
||||||
|
'powershell', '-NoProfile', '-Command', [[& {
|
||||||
|
Trap {
|
||||||
|
Write-Error -ErrorRecord $_
|
||||||
|
Exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$clip = ""
|
||||||
|
if (Get-Command "Get-Clipboard" -errorAction SilentlyContinue) {
|
||||||
|
$clip = Get-Clipboard -Raw -Format Text -TextFormatType UnicodeText
|
||||||
|
} else {
|
||||||
|
Add-Type -AssemblyName PresentationCore
|
||||||
|
$clip = [Windows.Clipboard]::GetText()
|
||||||
|
}
|
||||||
|
|
||||||
|
$clip = $clip -Replace "`r",""
|
||||||
|
$u8clip = [System.Text.Encoding]::UTF8.GetBytes($clip)
|
||||||
|
[Console]::OpenStandardOutput().Write($u8clip, 0, $u8clip.Length)
|
||||||
|
}]]
|
||||||
|
} })
|
||||||
|
if not res.error then
|
||||||
|
return res.stdout
|
||||||
|
end
|
||||||
|
elseif platform == 'macos' then
|
||||||
|
local res = utils.subprocess({ args = { 'pbpaste' } })
|
||||||
|
if not res.error then
|
||||||
|
return res.stdout
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return ''
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Paste text from the window-system's clipboard. 'clip' determines whether the
|
||||||
|
-- clipboard or the primary selection buffer is used (on X11 only.)
|
||||||
|
function paste(clip)
|
||||||
|
local text = get_clipboard(clip)
|
||||||
|
local before_cur = line:sub(1, cursor - 1)
|
||||||
|
local after_cur = line:sub(cursor)
|
||||||
|
line = before_cur .. text .. after_cur
|
||||||
|
cursor = cursor + text:len()
|
||||||
|
trim_input()
|
||||||
|
update()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- The REPL has pretty specific requirements for key bindings that aren't
|
||||||
|
-- really satisified by any of mpv's helper methods, since they must be in
|
||||||
|
-- their own input section, but they must also raise events on key-repeat.
|
||||||
|
-- Hence, this function manually creates an input section and puts a list of
|
||||||
|
-- bindings in it.
|
||||||
|
function add_repl_bindings(bindings)
|
||||||
|
local cfg = ''
|
||||||
|
for i, binding in ipairs(bindings) do
|
||||||
|
local key = binding[1]
|
||||||
|
local fn = binding[2]
|
||||||
|
local name = '__repl_binding_' .. i
|
||||||
|
mp.add_forced_key_binding(nil, name, fn, 'repeatable')
|
||||||
|
cfg = cfg .. key .. ' script-binding ' .. mp.script_name .. '/' ..
|
||||||
|
name .. '\n'
|
||||||
|
end
|
||||||
|
mp.commandv('define-section', 'repl-input', cfg, 'force')
|
||||||
|
end
|
||||||
|
|
||||||
|
function add_repl_alpharow_bindings(bindings)
|
||||||
|
local cfg = ''
|
||||||
|
for i, binding in ipairs(bindings) do
|
||||||
|
local key = binding[1]
|
||||||
|
local fn = binding[2]
|
||||||
|
local name = '__repl_alpha_binding_' .. i
|
||||||
|
mp.add_forced_key_binding(nil, name, fn, 'repeatable')
|
||||||
|
cfg = cfg .. key .. ' script-binding ' .. mp.script_name .. '/' ..
|
||||||
|
name .. '\n'
|
||||||
|
end
|
||||||
|
mp.commandv('define-section', 'repl-alpha-input', cfg, 'force')
|
||||||
|
mp.enable_key_bindings('repl-alpha-input')
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Mapping from characters to mpv key names
|
||||||
|
local binding_name_map = {
|
||||||
|
[' '] = 'SPACE',
|
||||||
|
['#'] = 'SHARP',
|
||||||
|
}
|
||||||
|
|
||||||
|
-- List of input bindings. This is a weird mashup between common GUI text-input
|
||||||
|
-- bindings and readline bindings.
|
||||||
|
local bindings = {
|
||||||
|
{ 'esc', function() escape() end },
|
||||||
|
{ 'bs', handle_backspace },
|
||||||
|
{ 'shift+bs', handle_backspace },
|
||||||
|
{ 'del', handle_del },
|
||||||
|
{ 'shift+del', handle_del },
|
||||||
|
{ 'ins', handle_ins },
|
||||||
|
{ 'left', function() prev_char() end },
|
||||||
|
{ 'right', function() next_char() end },
|
||||||
|
{ 'up', function() clear() end },
|
||||||
|
{ 'home', go_home },
|
||||||
|
{ 'end', go_end },
|
||||||
|
{ 'ctrl+c', clear },
|
||||||
|
{ 'ctrl+d', maybe_exit },
|
||||||
|
{ 'ctrl+k', del_to_eol },
|
||||||
|
{ 'ctrl+l', clear_log_buffer },
|
||||||
|
{ 'ctrl+u', del_to_start },
|
||||||
|
{ 'ctrl+v', function() paste(true) end },
|
||||||
|
{ 'meta+v', function() paste(true) end },
|
||||||
|
}
|
||||||
|
local alpharowbindings = {}
|
||||||
|
-- Add bindings for all the printable US-ASCII characters from ' ' to '~'
|
||||||
|
-- inclusive. Note, this is a pretty hacky way to do text input. mpv's input
|
||||||
|
-- system was designed for single-key key bindings rather than text input, so
|
||||||
|
-- things like dead-keys and non-ASCII input won't work. This is probably okay
|
||||||
|
-- though, since all mpv's commands and properties can be represented in ASCII.
|
||||||
|
for b = (' '):byte(), ('~'):byte() do
|
||||||
|
local c = string.char(b)
|
||||||
|
local binding = binding_name_map[c] or c
|
||||||
|
bindings[#bindings + 1] = {binding, function() handle_char_input(c) end}
|
||||||
|
end
|
||||||
|
|
||||||
|
function add_alpharowbinding(firstchar,lastchar)
|
||||||
|
for b = (firstchar):byte(), (lastchar):byte() do
|
||||||
|
local c = string.char(b)
|
||||||
|
local alphabinding = binding_name_map[c] or c
|
||||||
|
alpharowbindings[#alpharowbindings + 1] = {alphabinding, function() handle_char_input(c) end}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function add_specialalphabindings(charinput)
|
||||||
|
local alphabindingarray = charinput
|
||||||
|
for i, alphabinding in ipairs(alphabindingarray) do
|
||||||
|
alpharowbindings[#alpharowbindings + 1] = {alphabinding, function() handle_char_input(alphabinding) end }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
add_alpharowbinding('a','z')
|
||||||
|
add_alpharowbinding('A','Z')
|
||||||
|
add_alpharowbinding('/','/')
|
||||||
|
add_alpharowbinding(':',':')
|
||||||
|
add_alpharowbinding('(',')')
|
||||||
|
add_alpharowbinding('{','}')
|
||||||
|
add_alpharowbinding(':',';')
|
||||||
|
add_alpharowbinding('<','>')
|
||||||
|
add_alpharowbinding(',','.')
|
||||||
|
add_alpharowbinding('|','|')
|
||||||
|
add_alpharowbinding('\\','\\')
|
||||||
|
add_alpharowbinding('?','?')
|
||||||
|
add_alpharowbinding('[',']')
|
||||||
|
add_alpharowbinding('#','#')
|
||||||
|
add_alpharowbinding('~','~')
|
||||||
|
add_alpharowbinding('\'','\'')
|
||||||
|
add_alpharowbinding('@','@')
|
||||||
|
|
||||||
|
add_specialalphabindings({'à','è','ì','ò','ù','À','È','Ì','Ò','Ù'})
|
||||||
|
add_specialalphabindings({'á', 'é', 'í', 'ó', 'ú', 'ý', 'Á', 'É', 'Í', 'Ó', 'Ú', 'Ý'})
|
||||||
|
add_specialalphabindings({'â', 'ê', 'î', 'ô', 'û', 'Â', 'Ê', 'Î', 'Ô', 'Û'})
|
||||||
|
add_specialalphabindings({'ã', 'ñ', 'õ', 'Ã', 'Ñ', 'Õ'})
|
||||||
|
add_specialalphabindings({'ä', 'ë', 'ï', 'ö', 'ü', 'ÿ', 'Ä', 'Ë', 'Ï', 'Ö', 'Ü', 'Ÿ'})
|
||||||
|
add_specialalphabindings({'å', 'Å','æ','Æ','œ','Œ','ç','Ç','ð','Ð','ø','Ø','¿','¡','ß'})
|
||||||
|
add_specialalphabindings({'¤','†','×','÷','‡','±','—','–','¶','§','ˆ','˜','«','»','¦','‰','©','®','™'})
|
||||||
|
add_specialalphabindings({'ž','Ž'})
|
||||||
|
add_specialalphabindings({'ª','Þ','þ','ƒ','µ','°','º','•','„','“','…','¬','¥','£','€','¢','¹','²','³','½','¼','¾'})
|
||||||
|
add_specialalphabindings({'·','Ĉ','ĉ','Ĝ','ĝ','Ĥ','ĥ','Ĵ','ĵ','Ŝ','ŝ','Ŭ','ŭ'})
|
||||||
|
add_specialalphabindings({'Б','б','В','в','Г','г','Д','д','Е','е','Ё','ё','Ж','ж','З','з'})
|
||||||
|
add_specialalphabindings({'И','и','Й','й','К','к','Л','л','М','м','Н','н','О','о','П','п'})
|
||||||
|
add_specialalphabindings({'Р','р','С','с','Т','т','У','у','Ф','ф','Х','х','Ц','ц','Ч','ч'})
|
||||||
|
add_specialalphabindings({'Ш','ш','Щ','щ','Ъ','ъ','Ы','ы','Ь','ь','Э','э','Ю','ю','Я','я'})
|
||||||
|
add_specialalphabindings({'≥','≠'})
|
||||||
|
|
||||||
|
add_repl_bindings(bindings)
|
||||||
|
|
||||||
|
-- Add a script-message to show the REPL and fill it with the provided text
|
||||||
|
mp.register_script_message('type', function(text)
|
||||||
|
show_and_type(text)
|
||||||
|
end)
|
||||||
|
|
||||||
|
local syncplayintfSet = false
|
||||||
|
mp.command('print-text "<get_syncplayintf_options>"')
|
||||||
|
|
||||||
|
function readyMpvAfterSettingsKnown()
|
||||||
|
if syncplayintfSet == false then
|
||||||
|
local vertical_output_area = CANVAS_HEIGHT-(opts['chatTopMargin']+opts['chatBottomMargin']+((opts['chatOutputRelativeFontSize']*FONT_SIZE_MULTIPLIER)*opts['scrollingFirstRowOffset'])+SCROLLING_ADDITIONAL_BOTTOM_MARGIN)
|
||||||
|
max_scrolling_rows = math.floor(vertical_output_area/(opts['chatOutputRelativeFontSize']*FONT_SIZE_MULTIPLIER))
|
||||||
|
local user_opts = { visibility = "auto", }
|
||||||
|
opt.read_options(user_opts, "osc")
|
||||||
|
default_oscvisibility_state = user_opts.visibility
|
||||||
|
if opts['chatInputEnabled'] == true then
|
||||||
|
key_hints_enabled = true
|
||||||
|
mp.add_forced_key_binding('enter', handle_enter)
|
||||||
|
mp.add_forced_key_binding('kp_enter', handle_enter)
|
||||||
|
if opts['chatDirectInput'] == true then
|
||||||
|
add_repl_alpharow_bindings(alpharowbindings)
|
||||||
|
mp.add_forced_key_binding('tab', handle_tab)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
syncplayintfSet = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function set_syncplayintf_options(input)
|
||||||
|
--mp.command('print-text "<chat>...'..input..'</chat>"')
|
||||||
|
for option, value in string.gmatch(input, "([^ ,=]+)=([^,]+)") do
|
||||||
|
local valueType = type(opts[option])
|
||||||
|
if valueType == "number" then
|
||||||
|
value = tonumber(value)
|
||||||
|
elseif valueType == "boolean" then
|
||||||
|
if value == "True" then
|
||||||
|
value = true
|
||||||
|
else
|
||||||
|
value = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
opts[option] = value
|
||||||
|
--mp.command('print-text "<chat>'..option.."="..tostring(value).." - "..valueType..'</chat>"')
|
||||||
|
end
|
||||||
|
chat_format = get_output_style()
|
||||||
|
readyMpvAfterSettingsKnown()
|
||||||
|
end
|
||||||
BIN
resources/user_comment.png
Normal file
BIN
resources/user_comment.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 743 B |
@ -1,4 +1,4 @@
|
|||||||
version = '1.5.1'
|
version = '1.5.2'
|
||||||
milestone = 'Yoitsu'
|
milestone = 'Yoitsu'
|
||||||
release_number = '51'
|
release_number = '55'
|
||||||
projectURL = 'http://syncplay.pl/'
|
projectURL = 'http://syncplay.pl/'
|
||||||
|
|||||||
@ -145,8 +145,8 @@ class SyncplayClient(object):
|
|||||||
|
|
||||||
def initPlayer(self, player):
|
def initPlayer(self, player):
|
||||||
self._player = player
|
self._player = player
|
||||||
if not self._player.secondaryOSDSupported:
|
if not self._player.alertOSDSupported:
|
||||||
constants.OSD_WARNING_MESSAGE_DURATION = constants.NO_SECONDARY_OSD_WARNING_DURATION
|
constants.OSD_WARNING_MESSAGE_DURATION = constants.NO_ALERT_OSD_WARNING_DURATION
|
||||||
self.scheduleAskPlayer()
|
self.scheduleAskPlayer()
|
||||||
self.__playerReady.callback(player)
|
self.__playerReady.callback(player)
|
||||||
|
|
||||||
@ -205,7 +205,7 @@ class SyncplayClient(object):
|
|||||||
if pauseChange and paused and currentLength > constants.PLAYLIST_LOAD_NEXT_FILE_MINIMUM_LENGTH\
|
if pauseChange and paused and currentLength > constants.PLAYLIST_LOAD_NEXT_FILE_MINIMUM_LENGTH\
|
||||||
and abs(position - currentLength ) < constants.PLAYLIST_LOAD_NEXT_FILE_TIME_FROM_END_THRESHOLD:
|
and abs(position - currentLength ) < constants.PLAYLIST_LOAD_NEXT_FILE_TIME_FROM_END_THRESHOLD:
|
||||||
self.playlist.advancePlaylistCheck()
|
self.playlist.advancePlaylistCheck()
|
||||||
elif pauseChange and self.serverFeatures["readiness"]:
|
elif pauseChange and self.serverFeatures.has_key("readiness") and self.serverFeatures["readiness"]:
|
||||||
if currentLength == 0 or currentLength == -1 or\
|
if currentLength == 0 or currentLength == -1 or\
|
||||||
not (not self.playlist.notJustChangedPlaylist() and abs(position - currentLength ) < constants.PLAYLIST_LOAD_NEXT_FILE_TIME_FROM_END_THRESHOLD):
|
not (not self.playlist.notJustChangedPlaylist() and abs(position - currentLength ) < constants.PLAYLIST_LOAD_NEXT_FILE_TIME_FROM_END_THRESHOLD):
|
||||||
pauseChange = self._toggleReady(pauseChange, paused)
|
pauseChange = self._toggleReady(pauseChange, paused)
|
||||||
@ -546,15 +546,32 @@ class SyncplayClient(object):
|
|||||||
"sharedPlaylists": utils.meetsMinVersion(self.serverVersion, constants.SHARED_PLAYLIST_MIN_VERSION),
|
"sharedPlaylists": utils.meetsMinVersion(self.serverVersion, constants.SHARED_PLAYLIST_MIN_VERSION),
|
||||||
"chat": utils.meetsMinVersion(self.serverVersion, constants.CHAT_MIN_VERSION),
|
"chat": utils.meetsMinVersion(self.serverVersion, constants.CHAT_MIN_VERSION),
|
||||||
"readiness": utils.meetsMinVersion(self.serverVersion, constants.USER_READY_MIN_VERSION),
|
"readiness": utils.meetsMinVersion(self.serverVersion, constants.USER_READY_MIN_VERSION),
|
||||||
"managedRooms": utils.meetsMinVersion(self.serverVersion, constants.CONTROLLED_ROOMS_MIN_VERSION)
|
"managedRooms": utils.meetsMinVersion(self.serverVersion, constants.CONTROLLED_ROOMS_MIN_VERSION),
|
||||||
|
"maxChatMessageLength": constants.FALLBACK_MAX_CHAT_MESSAGE_LENGTH,
|
||||||
|
"maxUsernameLength": constants.FALLBACK_MAX_USERNAME_LENGTH,
|
||||||
|
"maxRoomNameLength": constants.FALLBACK_MAX_ROOM_NAME_LENGTH,
|
||||||
|
"maxFilenameLength": constants.FALLBACK_MAX_FILENAME_LENGTH
|
||||||
}
|
}
|
||||||
|
|
||||||
if featureList:
|
if featureList:
|
||||||
self.serverFeatures.update(featureList)
|
self.serverFeatures.update(featureList)
|
||||||
if not utils.meetsMinVersion(self.serverVersion, constants.SHARED_PLAYLIST_MIN_VERSION):
|
if not utils.meetsMinVersion(self.serverVersion, constants.SHARED_PLAYLIST_MIN_VERSION):
|
||||||
self.ui.showErrorMessage(getMessage("shared-playlists-not-supported-by-server-error").format(constants.SHARED_PLAYLIST_MIN_VERSION, self.serverVersion))
|
self.ui.showErrorMessage(getMessage("shared-playlists-not-supported-by-server-error").format(constants.SHARED_PLAYLIST_MIN_VERSION, self.serverVersion))
|
||||||
elif not self.serverFeatures["sharedPlaylists"]:
|
elif not self.serverFeatures["sharedPlaylists"]:
|
||||||
self.ui.showErrorMessage(getMessage("shared-playlists-disabled-by-server-error"))
|
self.ui.showErrorMessage(getMessage("shared-playlists-disabled-by-server-error"))
|
||||||
|
# TODO: Have messages for all unsupported & disabled features
|
||||||
|
constants.MAX_CHAT_MESSAGE_LENGTH = self.serverFeatures["maxChatMessageLength"]
|
||||||
|
constants.MAX_USERNAME_LENGTH = self.serverFeatures["maxUsernameLength"]
|
||||||
|
constants.MAX_ROOM_NAME_LENGTH = self.serverFeatures["maxRoomNameLength"]
|
||||||
|
constants.MAX_FILENAME_LENGTH = self.serverFeatures["maxFilenameLength"]
|
||||||
|
constants.MPV_SYNCPLAYINTF_CONSTANTS_TO_SEND = ["MaxChatMessageLength={}".format(constants.MAX_CHAT_MESSAGE_LENGTH),
|
||||||
|
u"inputPromptStartCharacter={}".format(constants.MPV_INPUT_PROMPT_START_CHARACTER),
|
||||||
|
u"inputPromptEndCharacter={}".format(constants.MPV_INPUT_PROMPT_END_CHARACTER),
|
||||||
|
u"backslashSubstituteCharacter={}".format(
|
||||||
|
constants.MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER)]
|
||||||
self.ui.setFeatures(self.serverFeatures)
|
self.ui.setFeatures(self.serverFeatures)
|
||||||
|
if self._player:
|
||||||
|
self._player.setFeatures(self.serverFeatures)
|
||||||
|
|
||||||
def getSanitizedCurrentUserFile(self):
|
def getSanitizedCurrentUserFile(self):
|
||||||
if self.userlist.currentUser.file:
|
if self.userlist.currentUser.file:
|
||||||
@ -583,16 +600,29 @@ class SyncplayClient(object):
|
|||||||
def getUsername(self):
|
def getUsername(self):
|
||||||
return self.userlist.currentUser.username
|
return self.userlist.currentUser.username
|
||||||
|
|
||||||
|
def chatIsEnabled(self):
|
||||||
|
return True
|
||||||
|
# TODO: Allow chat to be disabled
|
||||||
|
|
||||||
|
def getFeatures(self):
|
||||||
|
features = dict()
|
||||||
|
|
||||||
|
# Can change during runtime:
|
||||||
|
features["sharedPlaylists"] = self.sharedPlaylistIsEnabled() # Can change during runtime
|
||||||
|
features["chat"] = self.chatIsEnabled() # Can change during runtime
|
||||||
|
|
||||||
|
# Static for this version/release of Syncplay:
|
||||||
|
features["featureList"] = True
|
||||||
|
features["readiness"] = True
|
||||||
|
features["managedRooms"] = True
|
||||||
|
|
||||||
|
return features
|
||||||
|
|
||||||
def setRoom(self, roomName, resetAutoplay=False):
|
def setRoom(self, roomName, resetAutoplay=False):
|
||||||
self.userlist.currentUser.room = roomName
|
self.userlist.currentUser.room = roomName
|
||||||
if resetAutoplay:
|
if resetAutoplay:
|
||||||
self.resetAutoPlayState()
|
self.resetAutoPlayState()
|
||||||
|
|
||||||
def sendChat(self,message):
|
|
||||||
if self._protocol and self._protocol.logged:
|
|
||||||
message = utils.truncateText(message,constants.MAX_CHAT_MESSAGE_LENGTH)
|
|
||||||
self._protocol.sendChatMessage(message)
|
|
||||||
|
|
||||||
def sendRoom(self):
|
def sendRoom(self):
|
||||||
room = self.userlist.currentUser.room
|
room = self.userlist.currentUser.room
|
||||||
if self._protocol and self._protocol.logged and room:
|
if self._protocol and self._protocol.logged and room:
|
||||||
@ -632,8 +662,8 @@ class SyncplayClient(object):
|
|||||||
if self._protocol and self._protocol.logged:
|
if self._protocol and self._protocol.logged:
|
||||||
self._protocol.sendList()
|
self._protocol.sendList()
|
||||||
|
|
||||||
def showUserList(self):
|
def showUserList(self, altUI=None):
|
||||||
self.userlist.showUserList()
|
self.userlist.showUserList(altUI)
|
||||||
|
|
||||||
def getPassword(self):
|
def getPassword(self):
|
||||||
if self.thisIsPublicServer():
|
if self.thisIsPublicServer():
|
||||||
@ -700,19 +730,30 @@ class SyncplayClient(object):
|
|||||||
if promptForAction:
|
if promptForAction:
|
||||||
self.ui.promptFor(getMessage("enter-to-exit-prompt"))
|
self.ui.promptFor(getMessage("enter-to-exit-prompt"))
|
||||||
|
|
||||||
def requireMinServerVersion(minVersion):
|
def requireServerFeature(featureRequired):
|
||||||
def requireMinVersionDecorator(f):
|
def requireServerFeatureDecorator(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def wrapper(self, *args, **kwds):
|
def wrapper(self, *args, **kwds):
|
||||||
if not utils.meetsMinVersion(self.serverVersion,minVersion):
|
if self.serverVersion == "0.0.0":
|
||||||
if self.serverVersion != "0.0.0":
|
self.ui.showDebugMessage(
|
||||||
self.ui.showErrorMessage(getMessage("not-supported-by-server-error").format(minVersion, self.serverVersion))
|
"Tried to check server version too soon (testing support for: {})".format(featureRequired))
|
||||||
else:
|
return None
|
||||||
self.ui.showDebugMessage("Tried to check server version too soon (threshold: {})".format(minVersion))
|
if not self.serverFeatures.has_key(featureRequired) or not self.serverFeatures[featureRequired]:
|
||||||
|
featureName = getMessage(u"feature-{}".format(featureRequired))
|
||||||
|
self.ui.showErrorMessage(getMessage("not-supported-by-server-error").format(featureName))
|
||||||
return
|
return
|
||||||
return f(self, *args, **kwds)
|
return f(self, *args, **kwds)
|
||||||
return wrapper
|
return wrapper
|
||||||
return requireMinVersionDecorator
|
return requireServerFeatureDecorator
|
||||||
|
|
||||||
|
@requireServerFeature("chat")
|
||||||
|
def sendChat(self,message):
|
||||||
|
if self._protocol and self._protocol.logged:
|
||||||
|
message = utils.truncateText(message,constants.MAX_CHAT_MESSAGE_LENGTH)
|
||||||
|
self._protocol.sendChatMessage(message)
|
||||||
|
|
||||||
|
def sendFeaturesUpdate(self, features):
|
||||||
|
self._protocol.sendFeaturesUpdate(features)
|
||||||
|
|
||||||
def changePlaylistEnabledState(self, newState):
|
def changePlaylistEnabledState(self, newState):
|
||||||
oldState = self.sharedPlaylistIsEnabled()
|
oldState = self.sharedPlaylistIsEnabled()
|
||||||
@ -780,7 +821,7 @@ class SyncplayClient(object):
|
|||||||
allReadyMessage = getMessage("all-users-ready").format(self.userlist.readyUserCount())
|
allReadyMessage = getMessage("all-users-ready").format(self.userlist.readyUserCount())
|
||||||
autoplayingMessage = getMessage("autoplaying-notification").format(int(self.autoplayTimeLeft))
|
autoplayingMessage = getMessage("autoplaying-notification").format(int(self.autoplayTimeLeft))
|
||||||
countdownMessage = u"{}{}{}".format(allReadyMessage,self._player.osdMessageSeparator, autoplayingMessage)
|
countdownMessage = u"{}{}{}".format(allReadyMessage,self._player.osdMessageSeparator, autoplayingMessage)
|
||||||
self.ui.showOSDMessage(countdownMessage, 1, secondaryOSD=True)
|
self.ui.showOSDMessage(countdownMessage, 1, OSDType=constants.OSD_ALERT, mood=constants.MESSAGE_GOODNEWS)
|
||||||
if self.autoplayTimeLeft <= 0:
|
if self.autoplayTimeLeft <= 0:
|
||||||
self.setPaused(False)
|
self.setPaused(False)
|
||||||
self.stopAutoplayCountdown()
|
self.stopAutoplayCountdown()
|
||||||
@ -792,11 +833,11 @@ class SyncplayClient(object):
|
|||||||
self.ui.updateAutoPlayState(False)
|
self.ui.updateAutoPlayState(False)
|
||||||
self.stopAutoplayCountdown()
|
self.stopAutoplayCountdown()
|
||||||
|
|
||||||
@requireMinServerVersion(constants.USER_READY_MIN_VERSION)
|
@requireServerFeature("readiness")
|
||||||
def toggleReady(self, manuallyInitiated=True):
|
def toggleReady(self, manuallyInitiated=True):
|
||||||
self._protocol.setReady(not self.userlist.currentUser.isReady(), manuallyInitiated)
|
self._protocol.setReady(not self.userlist.currentUser.isReady(), manuallyInitiated)
|
||||||
|
|
||||||
@requireMinServerVersion(constants.USER_READY_MIN_VERSION)
|
@requireServerFeature("readiness")
|
||||||
def changeReadyState(self, newState, manuallyInitiated=True):
|
def changeReadyState(self, newState, manuallyInitiated=True):
|
||||||
oldState = self.userlist.currentUser.isReady()
|
oldState = self.userlist.currentUser.isReady()
|
||||||
if newState != oldState:
|
if newState != oldState:
|
||||||
@ -811,7 +852,12 @@ class SyncplayClient(object):
|
|||||||
if oldReadyState != isReady:
|
if oldReadyState != isReady:
|
||||||
self._warnings.checkReadyStates()
|
self._warnings.checkReadyStates()
|
||||||
|
|
||||||
@requireMinServerVersion(constants.CONTROLLED_ROOMS_MIN_VERSION)
|
@requireServerFeature("managedRooms")
|
||||||
|
def setUserFeatures(self, username, features):
|
||||||
|
self.userlist.setFeatures(username, features)
|
||||||
|
self.ui.userListChange()
|
||||||
|
|
||||||
|
@requireServerFeature("managedRooms")
|
||||||
def createControlledRoom(self, roomName):
|
def createControlledRoom(self, roomName):
|
||||||
controlPassword = utils.RandomStringGenerator.generate_room_password()
|
controlPassword = utils.RandomStringGenerator.generate_room_password()
|
||||||
self.lastControlPasswordAttempt = controlPassword
|
self.lastControlPasswordAttempt = controlPassword
|
||||||
@ -830,7 +876,7 @@ class SyncplayClient(object):
|
|||||||
else:
|
else:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@requireMinServerVersion(constants.CONTROLLED_ROOMS_MIN_VERSION)
|
@requireServerFeature("managedRooms")
|
||||||
def identifyAsController(self, controlPassword):
|
def identifyAsController(self, controlPassword):
|
||||||
controlPassword = self.stripControlPassword(controlPassword)
|
controlPassword = self.stripControlPassword(controlPassword)
|
||||||
self.ui.showMessage(getMessage("identifying-as-controller-notification").format(controlPassword))
|
self.ui.showMessage(getMessage("identifying-as-controller-notification").format(controlPassword))
|
||||||
@ -924,7 +970,7 @@ class SyncplayClient(object):
|
|||||||
|
|
||||||
def _checkIfYouReAloneInTheRoom(self, OSDOnly):
|
def _checkIfYouReAloneInTheRoom(self, OSDOnly):
|
||||||
if self._userlist.areYouAloneInRoom():
|
if self._userlist.areYouAloneInRoom():
|
||||||
self._ui.showOSDMessage(getMessage("alone-in-the-room"), constants.WARNING_OSD_MESSAGES_LOOP_INTERVAL, secondaryOSD=True)
|
self._ui.showOSDMessage(getMessage("alone-in-the-room"), constants.WARNING_OSD_MESSAGES_LOOP_INTERVAL, OSDType=constants.OSD_ALERT, mood=constants.MESSAGE_BADNEWS)
|
||||||
if not OSDOnly:
|
if not OSDOnly:
|
||||||
self._ui.showMessage(getMessage("alone-in-the-room"), True)
|
self._ui.showMessage(getMessage("alone-in-the-room"), True)
|
||||||
if constants.SHOW_OSD_WARNINGS and not self._warnings["alone-in-the-room"]['timer'].running:
|
if constants.SHOW_OSD_WARNINGS and not self._warnings["alone-in-the-room"]['timer'].running:
|
||||||
@ -954,8 +1000,10 @@ class SyncplayClient(object):
|
|||||||
if not self._client._player or self._client.autoplayTimerIsRunning():
|
if not self._client._player or self._client.autoplayTimerIsRunning():
|
||||||
return
|
return
|
||||||
osdMessage = None
|
osdMessage = None
|
||||||
|
messageMood = constants.MESSAGE_GOODNEWS
|
||||||
fileDifferencesForRoom = self._userlist.getFileDifferencesForRoom()
|
fileDifferencesForRoom = self._userlist.getFileDifferencesForRoom()
|
||||||
if not self._userlist.areAllFilesInRoomSame() and fileDifferencesForRoom is not None:
|
if not self._userlist.areAllFilesInRoomSame() and fileDifferencesForRoom is not None:
|
||||||
|
messageMood = constants.MESSAGE_BADNEWS
|
||||||
fileDifferencesMessage = getMessage("room-file-differences").format(fileDifferencesForRoom)
|
fileDifferencesMessage = getMessage("room-file-differences").format(fileDifferencesForRoom)
|
||||||
if self._userlist.currentUser.canControl() and self._userlist.isReadinessSupported():
|
if self._userlist.currentUser.canControl() and self._userlist.isReadinessSupported():
|
||||||
if self._userlist.areAllUsersInRoomReady():
|
if self._userlist.areAllUsersInRoomReady():
|
||||||
@ -970,9 +1018,10 @@ class SyncplayClient(object):
|
|||||||
if self._userlist.areAllUsersInRoomReady():
|
if self._userlist.areAllUsersInRoomReady():
|
||||||
osdMessage = getMessage("all-users-ready").format(self._userlist.readyUserCount())
|
osdMessage = getMessage("all-users-ready").format(self._userlist.readyUserCount())
|
||||||
else:
|
else:
|
||||||
|
messageMood = constants.MESSAGE_BADNEWS
|
||||||
osdMessage = getMessage("not-all-ready").format(self._userlist.usersInRoomNotReady())
|
osdMessage = getMessage("not-all-ready").format(self._userlist.usersInRoomNotReady())
|
||||||
if osdMessage:
|
if osdMessage:
|
||||||
self._ui.showOSDMessage(osdMessage, constants.WARNING_OSD_MESSAGES_LOOP_INTERVAL, secondaryOSD=True)
|
self._ui.showOSDMessage(osdMessage, constants.WARNING_OSD_MESSAGES_LOOP_INTERVAL, OSDType=constants.OSD_ALERT, mood=messageMood)
|
||||||
|
|
||||||
def __displayMessageOnOSD(self, warningName, warningFunction):
|
def __displayMessageOnOSD(self, warningName, warningFunction):
|
||||||
if constants.OSD_WARNING_MESSAGE_DURATION > self._warnings[warningName]["displayedFor"]:
|
if constants.OSD_WARNING_MESSAGE_DURATION > self._warnings[warningName]["displayedFor"]:
|
||||||
@ -1001,6 +1050,7 @@ class SyncplayUser(object):
|
|||||||
self.room = room
|
self.room = room
|
||||||
self.file = file_
|
self.file = file_
|
||||||
self._controller = False
|
self._controller = False
|
||||||
|
self._features = {}
|
||||||
|
|
||||||
def setFile(self, filename, duration, size, path=None):
|
def setFile(self, filename, duration, size, path=None):
|
||||||
file_ = {
|
file_ = {
|
||||||
@ -1054,6 +1104,9 @@ class SyncplayUser(object):
|
|||||||
def setReady(self, ready):
|
def setReady(self, ready):
|
||||||
self.ready = ready
|
self.ready = ready
|
||||||
|
|
||||||
|
def setFeatures(self, features):
|
||||||
|
self._features = features
|
||||||
|
|
||||||
class SyncplayUserlist(object):
|
class SyncplayUserlist(object):
|
||||||
def __init__(self, ui, client):
|
def __init__(self, ui, client):
|
||||||
self.currentUser = SyncplayUser()
|
self.currentUser = SyncplayUser()
|
||||||
@ -1132,7 +1185,7 @@ class SyncplayUserlist(object):
|
|||||||
if differentDuration: differences.append(getMessage("file-difference-duration"))
|
if differentDuration: differences.append(getMessage("file-difference-duration"))
|
||||||
return ", ".join(differences)
|
return ", ".join(differences)
|
||||||
|
|
||||||
def addUser(self, username, room, file_, noMessage=False, isController=None, isReady=None):
|
def addUser(self, username, room, file_, noMessage=False, isController=None, isReady=None, features={}):
|
||||||
if username == self.currentUser.username:
|
if username == self.currentUser.username:
|
||||||
if isController is not None:
|
if isController is not None:
|
||||||
self.currentUser.setControllerStatus(isController)
|
self.currentUser.setControllerStatus(isController)
|
||||||
@ -1143,7 +1196,7 @@ class SyncplayUserlist(object):
|
|||||||
user.setControllerStatus(isController)
|
user.setControllerStatus(isController)
|
||||||
self._users[username] = user
|
self._users[username] = user
|
||||||
user.setReady(isReady)
|
user.setReady(isReady)
|
||||||
|
user.setFeatures(features)
|
||||||
if not noMessage:
|
if not noMessage:
|
||||||
self.__showUserChangeMessage(username, room, file_)
|
self.__showUserChangeMessage(username, room, file_)
|
||||||
self.userListChange(room)
|
self.userListChange(room)
|
||||||
@ -1310,7 +1363,7 @@ class SyncplayUserlist(object):
|
|||||||
def hasRoomStateChanged(self):
|
def hasRoomStateChanged(self):
|
||||||
return self._roomUsersChanged
|
return self._roomUsersChanged
|
||||||
|
|
||||||
def showUserList(self):
|
def showUserList(self, altUI=None):
|
||||||
rooms = {}
|
rooms = {}
|
||||||
for user in self._users.itervalues():
|
for user in self._users.itervalues():
|
||||||
if user.room not in rooms:
|
if user.room not in rooms:
|
||||||
@ -1320,7 +1373,10 @@ class SyncplayUserlist(object):
|
|||||||
rooms[self.currentUser.room] = []
|
rooms[self.currentUser.room] = []
|
||||||
rooms[self.currentUser.room].append(self.currentUser)
|
rooms[self.currentUser.room].append(self.currentUser)
|
||||||
rooms = self.sortList(rooms)
|
rooms = self.sortList(rooms)
|
||||||
self.ui.showUserList(self.currentUser, rooms)
|
if altUI:
|
||||||
|
altUI.showUserList(self.currentUser, rooms)
|
||||||
|
else:
|
||||||
|
self.ui.showUserList(self.currentUser, rooms)
|
||||||
self._client.autoplayCheck()
|
self._client.autoplayCheck()
|
||||||
|
|
||||||
def clearList(self):
|
def clearList(self):
|
||||||
@ -1336,10 +1392,10 @@ class UiManager(object):
|
|||||||
def __init__(self, client, ui):
|
def __init__(self, client, ui):
|
||||||
self._client = client
|
self._client = client
|
||||||
self.__ui = ui
|
self.__ui = ui
|
||||||
self.lastPrimaryOSDMessage = None
|
self.lastNotificatinOSDMessage = None
|
||||||
self.lastPrimaryOSDEndTime = None
|
self.lastNotificationOSDEndTime = None
|
||||||
self.lastSecondaryOSDMessage = None
|
self.lastAlertOSDMessage = None
|
||||||
self.lastSecondaryOSDEndTime = None
|
self.lastAlertOSDEndTime = None
|
||||||
self.lastError = ""
|
self.lastError = ""
|
||||||
|
|
||||||
def setPlaylist(self, newPlaylist, newIndexFilename=None):
|
def setPlaylist(self, newPlaylist, newIndexFilename=None):
|
||||||
@ -1358,8 +1414,16 @@ class UiManager(object):
|
|||||||
if constants.DEBUG_MODE and message.rstrip():
|
if constants.DEBUG_MODE and message.rstrip():
|
||||||
sys.stderr.write("{}{}\n".format(time.strftime(constants.UI_TIME_FORMAT, time.localtime()).decode('utf-8'),message.rstrip()))
|
sys.stderr.write("{}{}\n".format(time.strftime(constants.UI_TIME_FORMAT, time.localtime()).decode('utf-8'),message.rstrip()))
|
||||||
|
|
||||||
def showMessage(self, message, noPlayer=False, noTimestamp=False, secondaryOSD=False):
|
def showChatMessage(self, username, userMessage):
|
||||||
if not noPlayer: self.showOSDMessage(message, duration=constants.OSD_DURATION, secondaryOSD=secondaryOSD)
|
messageString = u"<{}> {}".format(username, userMessage)
|
||||||
|
if self._client._player.chatOSDSupported and self._client._config["chatOutputEnabled"]:
|
||||||
|
self._client._player.displayChatMessage(username,userMessage)
|
||||||
|
else:
|
||||||
|
self.showOSDMessage(messageString, duration=constants.OSD_DURATION)
|
||||||
|
self.__ui.showMessage(messageString)
|
||||||
|
|
||||||
|
def showMessage(self, message, noPlayer=False, noTimestamp=False, OSDType=constants.OSD_NOTIFICATION,mood=constants.MESSAGE_NEUTRAL):
|
||||||
|
if not noPlayer: self.showOSDMessage(message, duration=constants.OSD_DURATION, OSDType=OSDType, mood=mood)
|
||||||
self.__ui.showMessage(message, noTimestamp)
|
self.__ui.showMessage(message, noTimestamp)
|
||||||
|
|
||||||
def updateAutoPlayState(self, newState):
|
def updateAutoPlayState(self, newState):
|
||||||
@ -1368,28 +1432,28 @@ class UiManager(object):
|
|||||||
def showUserList(self, currentUser, rooms):
|
def showUserList(self, currentUser, rooms):
|
||||||
self.__ui.showUserList(currentUser, rooms)
|
self.__ui.showUserList(currentUser, rooms)
|
||||||
|
|
||||||
def showOSDMessage(self, message, duration=constants.OSD_DURATION, secondaryOSD=False):
|
def showOSDMessage(self, message, duration=constants.OSD_DURATION, OSDType=constants.OSD_NOTIFICATION, mood=constants.MESSAGE_NEUTRAL):
|
||||||
autoplayConditionsMet = self._client.autoplayConditionsMet()
|
autoplayConditionsMet = self._client.autoplayConditionsMet()
|
||||||
if secondaryOSD and not constants.SHOW_OSD_WARNINGS and not self._client.autoplayTimerIsRunning():
|
if OSDType == constants.OSD_ALERT and not constants.SHOW_OSD_WARNINGS and not self._client.autoplayTimerIsRunning():
|
||||||
return
|
return
|
||||||
if not self._client._player:
|
if not self._client._player:
|
||||||
return
|
return
|
||||||
if constants.SHOW_OSD and self._client and self._client._player:
|
if constants.SHOW_OSD and self._client and self._client._player:
|
||||||
if not self._client._player.secondaryOSDSupported:
|
if not self._client._player.alertOSDSupported:
|
||||||
if secondaryOSD:
|
if OSDType == constants.OSD_ALERT:
|
||||||
self.lastSecondaryOSDMessage = message
|
self.lastAlertOSDMessage = message
|
||||||
if autoplayConditionsMet:
|
if autoplayConditionsMet:
|
||||||
self.lastSecondaryOSDEndTime = time.time() + 1.0
|
self.lastAlertOSDEndTime = time.time() + 1.0
|
||||||
else:
|
else:
|
||||||
self.lastSecondaryOSDEndTime = time.time() + constants.NO_SECONDARY_OSD_WARNING_DURATION
|
self.lastAlertOSDEndTime = time.time() + constants.NO_ALERT_OSD_WARNING_DURATION
|
||||||
if self.lastPrimaryOSDEndTime and time.time() < self.lastPrimaryOSDEndTime:
|
if self.lastNotificationOSDEndTime and time.time() < self.lastNotificationOSDEndTime:
|
||||||
message = u"{}{}{}".format(message, self._client._player.osdMessageSeparator, self.lastPrimaryOSDMessage)
|
message = u"{}{}{}".format(message, self._client._player.osdMessageSeparator, self.lastNotificatinOSDMessage)
|
||||||
else:
|
else:
|
||||||
self.lastPrimaryOSDMessage = message
|
self.lastNotificatinOSDMessage = message
|
||||||
self.lastPrimaryOSDEndTime = time.time() + constants.OSD_DURATION
|
self.lastNotificationOSDEndTime = time.time() + constants.OSD_DURATION
|
||||||
if self.lastSecondaryOSDEndTime and time.time() < self.lastSecondaryOSDEndTime:
|
if self.lastAlertOSDEndTime and time.time() < self.lastAlertOSDEndTime:
|
||||||
message = u"{}{}{}".format(self.lastSecondaryOSDMessage, self._client._player.osdMessageSeparator, message)
|
message = u"{}{}{}".format(self.lastAlertOSDMessage, self._client._player.osdMessageSeparator, message)
|
||||||
self._client._player.displayMessage(message, int(duration * 1000), secondaryOSD)
|
self._client._player.displayMessage(message, int(duration * 1000), OSDType, mood)
|
||||||
|
|
||||||
def setControllerStatus(self, username, isController):
|
def setControllerStatus(self, username, isController):
|
||||||
self.__ui.setControllerStatus(username, isController)
|
self.__ui.setControllerStatus(username, isController)
|
||||||
@ -1411,6 +1475,9 @@ class UiManager(object):
|
|||||||
def updateRoomName(self, room=""):
|
def updateRoomName(self, room=""):
|
||||||
self.__ui.updateRoomName(room)
|
self.__ui.updateRoomName(room)
|
||||||
|
|
||||||
|
def executeCommand(self, command):
|
||||||
|
self.__ui.executeCommand(command)
|
||||||
|
|
||||||
def drop(self):
|
def drop(self):
|
||||||
self.__ui.drop()
|
self.__ui.drop()
|
||||||
|
|
||||||
@ -1799,7 +1866,7 @@ class FileSwitchManager(object):
|
|||||||
if os.path.isfile(filepath):
|
if os.path.isfile(filepath):
|
||||||
return filepath
|
return filepath
|
||||||
|
|
||||||
if highPriority and self.folderSearchEnabled:
|
if highPriority and self.folderSearchEnabled and self.mediaDirectories is not None:
|
||||||
directoryList = self.mediaDirectories
|
directoryList = self.mediaDirectories
|
||||||
# Spin up hard drives to prevent premature timeout
|
# Spin up hard drives to prevent premature timeout
|
||||||
randomFilename = u"RandomFile"+unicode(random.randrange(10000, 99999))+u".txt"
|
randomFilename = u"RandomFile"+unicode(random.randrange(10000, 99999))+u".txt"
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
|
# coding:utf8
|
||||||
# You might want to change these
|
# You might want to change these
|
||||||
DEFAULT_PORT = 8999
|
DEFAULT_PORT = 8999
|
||||||
OSD_DURATION = 3.0
|
OSD_DURATION = 3.0
|
||||||
OSD_WARNING_MESSAGE_DURATION = 5.0
|
OSD_WARNING_MESSAGE_DURATION = 5.0
|
||||||
NO_SECONDARY_OSD_WARNING_DURATION = 13.0
|
NO_ALERT_OSD_WARNING_DURATION = 13.0
|
||||||
MPC_OSD_POSITION = 1 #Right corner, 1 for left
|
MPC_OSD_POSITION = 1 #Right corner, 1 for left
|
||||||
MPLAYER_OSD_LEVEL = 1
|
MPLAYER_OSD_LEVEL = 1
|
||||||
UI_TIME_FORMAT = "[%X] "
|
UI_TIME_FORMAT = "[%X] "
|
||||||
@ -56,12 +57,23 @@ PLAYLIST_MAX_CHARACTERS = 10000
|
|||||||
PLAYLIST_MAX_ITEMS = 250
|
PLAYLIST_MAX_ITEMS = 250
|
||||||
MAXIMUM_TAB_WIDTH = 350
|
MAXIMUM_TAB_WIDTH = 350
|
||||||
TAB_PADDING = 30
|
TAB_PADDING = 30
|
||||||
|
DEFAULT_WINDOWS_MONOSPACE_FONT = "Consolas"
|
||||||
|
DEFAULT_OSX_MONOSPACE_FONT = "Menlo"
|
||||||
|
FALLBACK_MONOSPACE_FONT = "Monospace"
|
||||||
|
DEFAULT_CHAT_FONT_SIZE = 24
|
||||||
|
DEFAULT_CHAT_INPUT_FONT_COLOR = "#FFFF00"
|
||||||
|
DEFAULT_CHAT_OUTPUT_FONT_COLOR = "#FFFF00"
|
||||||
|
DEFAULT_CHAT_FONT_WEIGHT = 1
|
||||||
|
|
||||||
# Maximum character lengths (for client and server)
|
# Max numbers are used by server (and client pre-connection). Once connected client gets values from server featureList (or uses 'fallback' versions for old servers)
|
||||||
MAX_CHAT_MESSAGE_LENGTH = 50 # Number of displayed characters
|
MAX_CHAT_MESSAGE_LENGTH = 125 # Number of displayed characters
|
||||||
MAX_USERNAME_LENGTH = 16 # Number of displayed characters
|
MAX_USERNAME_LENGTH = 20 # Number of displayed characters
|
||||||
MAX_ROOM_NAME_LENGTH = 35 # Number of displayed characters
|
MAX_ROOM_NAME_LENGTH = 35 # Number of displayed characters
|
||||||
MAX_FILENAME_LENGTH = 250 # Number of displayed characters
|
MAX_FILENAME_LENGTH = 250 # Number of displayed characters
|
||||||
|
FALLBACK_MAX_CHAT_MESSAGE_LENGTH = 50 # Number of displayed characters
|
||||||
|
FALLBACK_MAX_USERNAME_LENGTH = 16 # Number of displayed characters
|
||||||
|
FALLBACK_MAX_ROOM_NAME_LENGTH = 35 # Number of displayed characters
|
||||||
|
FALLBACK_MAX_FILENAME_LENGTH = 250 # Number of displayed characters
|
||||||
|
|
||||||
# Options for the File Switch feature:
|
# Options for the File Switch feature:
|
||||||
FOLDER_SEARCH_FIRST_FILE_TIMEOUT = 25.0 # Secs - How long to wait to find the first file in folder search (to take account of HDD spin up)
|
FOLDER_SEARCH_FIRST_FILE_TIMEOUT = 25.0 # Secs - How long to wait to find the first file in folder search (to take account of HDD spin up)
|
||||||
@ -133,7 +145,7 @@ MPC_ICONPATH = "mpc-hc.png"
|
|||||||
MPC64_ICONPATH = "mpc-hc64.png"
|
MPC64_ICONPATH = "mpc-hc64.png"
|
||||||
MPC_BE_ICONPATH = "mpc-be.png"
|
MPC_BE_ICONPATH = "mpc-be.png"
|
||||||
|
|
||||||
MPV_ERROR_MESSAGES_TO_REPEAT = ['[ytdl_hook] Your version of youtube-dl is too old', '[ytdl_hook] youtube-dl failed', 'Failed to recognize file format.']
|
MPV_ERROR_MESSAGES_TO_REPEAT = ['[ytdl_hook] Your version of youtube-dl is too old', '[ytdl_hook] youtube-dl failed', 'Failed to recognize file format.', '[syncplayintf] Lua error']
|
||||||
|
|
||||||
#Changing these is usually not something you're looking for
|
#Changing these is usually not something you're looking for
|
||||||
PLAYER_ASK_DELAY = 0.1
|
PLAYER_ASK_DELAY = 0.1
|
||||||
@ -179,6 +191,19 @@ MPV_ARGS = ['--force-window', '--idle', '--hr-seek=always', '--keep-open']
|
|||||||
MPV_SLAVE_ARGS = ['--msg-level=all=error,cplayer=info,term-msg=info', '--input-terminal=no', '--input-file=/dev/stdin']
|
MPV_SLAVE_ARGS = ['--msg-level=all=error,cplayer=info,term-msg=info', '--input-terminal=no', '--input-file=/dev/stdin']
|
||||||
MPV_SLAVE_ARGS_NEW = ['--term-playing-msg=<SyncplayUpdateFile>\nANS_filename=${filename}\nANS_length=${=duration:${=length:0}}\nANS_path=${path}\n</SyncplayUpdateFile>', '--terminal=yes']
|
MPV_SLAVE_ARGS_NEW = ['--term-playing-msg=<SyncplayUpdateFile>\nANS_filename=${filename}\nANS_length=${=duration:${=length:0}}\nANS_path=${path}\n</SyncplayUpdateFile>', '--terminal=yes']
|
||||||
MPV_NEW_VERSION = False
|
MPV_NEW_VERSION = False
|
||||||
|
MPV_OSC_VISIBILITY_CHANGE_VERSION = False
|
||||||
|
MPV_INPUT_PROMPT_START_CHARACTER = u"〉"
|
||||||
|
MPV_INPUT_PROMPT_END_CHARACTER = u" 〈"
|
||||||
|
MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER = u"\"
|
||||||
|
MPV_SYNCPLAYINTF_OPTIONS_TO_SEND = ["chatInputEnabled","chatInputFontFamily", "chatInputRelativeFontSize", "chatInputFontWeight","chatInputFontUnderline",
|
||||||
|
"chatInputFontColor", "chatInputPosition","chatOutputFontFamily","chatOutputRelativeFontSize",
|
||||||
|
"chatOutputFontWeight","chatOutputFontUnderline","chatOutputMode","chatMaxLines",
|
||||||
|
"chatTopMargin","chatLeftMargin","chatBottomMargin","chatDirectInput",
|
||||||
|
"notificationTimeout","alertTimeout","chatTimeout","chatOutputEnabled"]
|
||||||
|
|
||||||
|
MPV_SYNCPLAYINTF_CONSTANTS_TO_SEND = ["MaxChatMessageLength={}".format(MAX_CHAT_MESSAGE_LENGTH),u"inputPromptStartCharacter={}".format(MPV_INPUT_PROMPT_START_CHARACTER),u"inputPromptEndCharacter={}".format(MPV_INPUT_PROMPT_END_CHARACTER),u"backslashSubstituteCharacter={}".format(MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER)]
|
||||||
|
# Note: Constants updated in client.py->checkForFeatureSupport
|
||||||
|
MPV_SYNCPLAYINTF_LANGUAGE_TO_SEND = ["mpv-key-tab-hint","mpv-key-hint", "alphakey-mode-warning-first-line", "alphakey-mode-warning-second-line"]
|
||||||
VLC_SLAVE_ARGS = ['--extraintf=luaintf', '--lua-intf=syncplay', '--no-quiet', '--no-input-fast-seek',
|
VLC_SLAVE_ARGS = ['--extraintf=luaintf', '--lua-intf=syncplay', '--no-quiet', '--no-input-fast-seek',
|
||||||
'--play-and-pause', '--start-time=0']
|
'--play-and-pause', '--start-time=0']
|
||||||
VLC_SLAVE_MACOS_ARGS = ['--verbose=2', '--no-file-logging']
|
VLC_SLAVE_MACOS_ARGS = ['--verbose=2', '--no-file-logging']
|
||||||
@ -200,6 +225,9 @@ UNPAUSE_IFALREADYREADY_MODE = "IfAlreadyReady"
|
|||||||
UNPAUSE_IFOTHERSREADY_MODE = "IfOthersReady"
|
UNPAUSE_IFOTHERSREADY_MODE = "IfOthersReady"
|
||||||
UNPAUSE_IFMINUSERSREADY_MODE = "IfMinUsersReady"
|
UNPAUSE_IFMINUSERSREADY_MODE = "IfMinUsersReady"
|
||||||
UNPAUSE_ALWAYS_MODE = "Always"
|
UNPAUSE_ALWAYS_MODE = "Always"
|
||||||
|
INPUT_POSITION_TOP = "Top"
|
||||||
|
INPUT_POSITION_MIDDLE = "Middle"
|
||||||
|
INPUT_POSITION_BOTTOM = "Bottom"
|
||||||
|
|
||||||
VLC_EOF_DURATION_THRESHOLD = 2.0
|
VLC_EOF_DURATION_THRESHOLD = 2.0
|
||||||
|
|
||||||
@ -217,6 +245,17 @@ FILEITEM_SWITCH_FILE_SWITCH = 1
|
|||||||
FILEITEM_SWITCH_STREAM_SWITCH = 2
|
FILEITEM_SWITCH_STREAM_SWITCH = 2
|
||||||
PLAYLISTITEM_CURRENTLYPLAYING_ROLE = 3
|
PLAYLISTITEM_CURRENTLYPLAYING_ROLE = 3
|
||||||
|
|
||||||
|
MESSAGE_NEUTRAL = "neutral"
|
||||||
|
MESSAGE_BADNEWS = "bad"
|
||||||
|
MESSAGE_GOODNEWS = "good"
|
||||||
|
|
||||||
|
OSD_NOTIFICATION = "notification" # Also known as PrimaryOSD
|
||||||
|
OSD_ALERT = "alert" # Also known as SecondaryOSD
|
||||||
|
OSD_CHAT = "chat"
|
||||||
|
|
||||||
|
CHATROOM_MODE = "Chatroom"
|
||||||
|
SCROLLING_MODE = "Scrolling"
|
||||||
|
|
||||||
SYNCPLAY_UPDATE_URL = u"http://syncplay.pl/checkforupdate?{}" # Params
|
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
|
SYNCPLAY_PUBLIC_SERVER_LIST_URL = u"http://syncplay.pl/listpublicservers?{}" # Params
|
||||||
|
|||||||
@ -66,4 +66,6 @@ def getMessage(type_, locale=None):
|
|||||||
if messages["en"].has_key(type_):
|
if messages["en"].has_key(type_):
|
||||||
return unicode(messages["en"][type_])
|
return unicode(messages["en"][type_])
|
||||||
else:
|
else:
|
||||||
raise KeyError(type_)
|
print u"WARNING: Cannot find message '{}'!".format(type_)
|
||||||
|
return "!{}".format(type_) # TODO: Remove
|
||||||
|
#raise KeyError(type_)
|
||||||
@ -17,23 +17,23 @@ de = {
|
|||||||
"connected-successful-notification" : u"Erfolgreich mit Server verbunden",
|
"connected-successful-notification" : u"Erfolgreich mit Server verbunden",
|
||||||
"retrying-notification" : u"%s, versuche erneut in %d Sekunden...", # Seconds
|
"retrying-notification" : u"%s, versuche erneut in %d Sekunden...", # Seconds
|
||||||
|
|
||||||
"rewind-notification" : u"Zurückgespult wegen Zeitdifferenz mit <{}>", # User
|
"rewind-notification" : u"Zurückgespult wegen Zeitdifferenz mit {}", # User
|
||||||
"fastforward-notification" : u"Vorgespult wegen Zeitdifferenz mit <{}>", # User
|
"fastforward-notification" : u"Vorgespult wegen Zeitdifferenz mit {}", # User
|
||||||
"slowdown-notification" : u"Verlangsamt wegen Zeitdifferenz mit <{}>", # User
|
"slowdown-notification" : u"Verlangsamt wegen Zeitdifferenz mit {}", # User
|
||||||
"revert-notification" : u"Normalgeschwindigkeit",
|
"revert-notification" : u"Normalgeschwindigkeit",
|
||||||
|
|
||||||
"pause-notification" : u"<{}> pausierte", # User
|
"pause-notification" : u"{} pausierte", # User
|
||||||
"unpause-notification" : u"<{}> startete", # User
|
"unpause-notification" : u"{} startete", # User
|
||||||
"seek-notification" : u"<{}> sprang von {} nach {}", # User, from time, to time
|
"seek-notification" : u"{} sprang von {} nach {}", # User, from time, to time
|
||||||
|
|
||||||
"current-offset-notification" : u"Aktueller Offset: {} Sekunden", # Offset
|
"current-offset-notification" : u"Aktueller Offset: {} Sekunden", # Offset
|
||||||
|
|
||||||
"media-directory-list-updated-notification" : u"Syncplay media directories have been updated.", # TODO: Translate
|
"media-directory-list-updated-notification" : u"Syncplay media directories have been updated.", # TODO: Translate
|
||||||
|
|
||||||
"room-join-notification" : u"<{}> hat den Raum '{}' betreten", # User
|
"room-join-notification" : u"{} hat den Raum '{}' betreten", # User
|
||||||
"left-notification" : u"<{}> ist gegangen", # User
|
"left-notification" : u"{} ist gegangen", # User
|
||||||
"left-paused-notification" : u"<{}> ist gegangen, <{}> pausierte", # User who left, User who paused
|
"left-paused-notification" : u"{} ist gegangen, {} pausierte", # User who left, User who paused
|
||||||
"playing-notification" : u"<{}> spielt '{}' ({})", # User, file, duration
|
"playing-notification" : u"{} spielt '{}' ({})", # User, file, duration
|
||||||
"playing-notification/room-addendum" : u" in Raum: '{}'", # Room
|
"playing-notification/room-addendum" : u" in Raum: '{}'", # Room
|
||||||
|
|
||||||
"not-all-ready" : u"Noch nicht bereit: {}", # Usernames
|
"not-all-ready" : u"Noch nicht bereit: {}", # Usernames
|
||||||
@ -44,11 +44,11 @@ de = {
|
|||||||
"autoplaying-notification" : u"Starte in {}...", # Number of seconds until playback will start
|
"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"
|
"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.",
|
"failed-to-identify-as-controller-notification" : u"{} konnte sich nicht als Raumleiter identifizieren.",
|
||||||
"authenticated-as-controller-notification" : u"<{}> authentifizierte sich als Raumleiter",
|
"authenticated-as-controller-notification" : u"{} authentifizierte sich als Raumleiter",
|
||||||
"created-controlled-room-notification" : u"Gesteuerten Raum '{}' mit Passwort '{}' erstellt. Bitte diese Informationen für die Zukunft aufheben!", # RoomName, operatorPassword
|
"created-controlled-room-notification" : u"Gesteuerten Raum '{}' mit Passwort '{}' erstellt. Bitte diese Informationen für die Zukunft aufheben!", # RoomName, operatorPassword
|
||||||
|
|
||||||
"file-different-notification" : u"Deine Datei scheint sich von <{}>s zu unterscheiden", # User
|
"file-different-notification" : u"Deine Datei scheint sich von {}s zu unterscheiden", # User
|
||||||
"file-differences-notification" : u"Deine Datei unterscheidet sich auf folgende Art: {}",
|
"file-differences-notification" : u"Deine Datei unterscheidet sich auf folgende Art: {}",
|
||||||
"room-file-differences" : u"Unterschiedlich in: {}", # File differences (filename, size, and/or duration)
|
"room-file-differences" : u"Unterschiedlich in: {}", # File differences (filename, size, and/or duration)
|
||||||
"file-difference-filename" : u"Name",
|
"file-difference-filename" : u"Name",
|
||||||
@ -225,6 +225,7 @@ de = {
|
|||||||
"messages-label" : u"Nachrichten",
|
"messages-label" : u"Nachrichten",
|
||||||
"messages-osd-title" : u"OSD-(OnScreenDisplay)-Einstellungen",
|
"messages-osd-title" : u"OSD-(OnScreenDisplay)-Einstellungen",
|
||||||
"messages-other-title" : u"Weitere Display-Einstellungen",
|
"messages-other-title" : u"Weitere Display-Einstellungen",
|
||||||
|
"chat-label" : u"Chat", # TODO: Translate
|
||||||
"privacy-label" : u"Privatsphäre",
|
"privacy-label" : u"Privatsphäre",
|
||||||
"privacy-title" : u"Privatsphäreneinstellungen",
|
"privacy-title" : u"Privatsphäreneinstellungen",
|
||||||
"unpause-title" : u"Wenn du Play drückst, auf Bereit setzen und:",
|
"unpause-title" : u"Wenn du Play drückst, auf Bereit setzen und:",
|
||||||
@ -234,6 +235,27 @@ de = {
|
|||||||
"unpause-always" : u"Immer wiedergeben",
|
"unpause-always" : u"Immer wiedergeben",
|
||||||
"syncplay-trusteddomains-title": u"Trusted domains (for streaming services and hosted content)", # TODO: Translate into German
|
"syncplay-trusteddomains-title": u"Trusted domains (for streaming services and hosted content)", # TODO: Translate into German
|
||||||
|
|
||||||
|
"chat-title": u"Chat message input", # TODO: Translate
|
||||||
|
"chatinputenabled-label": u"Enable chat input via mpv (using enter key)", # TODO: Translate
|
||||||
|
"chatdirectinput-label" : u"Allow instant chat input (bypass having to press enter key to chat)", # TODO: Translate
|
||||||
|
"chatinputfont-label": u"Chat input font", # TODO: Translate
|
||||||
|
"chatfont-label": u"Set font", # TODO: Translate
|
||||||
|
"chatcolour-label": u"Set colour", # TODO: Translate
|
||||||
|
"chatinputposition-label": u"Position of message input area in mpv", # TODO: Translate
|
||||||
|
"chat-top-option": u"Top", # TODO: Translate
|
||||||
|
"chat-middle-option": u"Middle", # TODO: Translate
|
||||||
|
"chat-bottom-option": u"Bottom", # TODO: Translate
|
||||||
|
"chatoutputfont-label": u"Chat output font", # TODO: Translate
|
||||||
|
"chatoutputenabled-label": u"Enable chat output in media player (mpv only for now)", # TODO: Translate
|
||||||
|
"chatoutputposition-label": u"Output mode", # TODO: Translate
|
||||||
|
"chat-chatroom-option": u"Chatroom style", # TODO: Translate
|
||||||
|
"chat-scrolling-option": u"Scrolling style", # TODO: Translate
|
||||||
|
|
||||||
|
"mpv-key-tab-hint": u"[TAB] to toggle access to alphabet row key shortcuts.", # TODO: Translate
|
||||||
|
"mpv-key-hint": u"[ENTER] to send message. [ESC] to escape chat mode.", # TODO: Translate
|
||||||
|
"alphakey-mode-warning-first-line": u"You can temporarily use old mpv bindings with a-z keys.", # TODO: Translate
|
||||||
|
"alphakey-mode-warning-second-line": u"Press [TAB] to return to Syncplay chat mode.", # TODO: Translate
|
||||||
|
|
||||||
"help-label" : u"Hilfe",
|
"help-label" : u"Hilfe",
|
||||||
"reset-label" : u"Standardwerte zurücksetzen",
|
"reset-label" : u"Standardwerte zurücksetzen",
|
||||||
"run-label" : u"Syncplay starten",
|
"run-label" : u"Syncplay starten",
|
||||||
@ -357,6 +379,22 @@ de = {
|
|||||||
"unpause-ifminusersready-tooltip" : u"Wenn du Play drückst und nicht bereit bist, wird nur gestartet, wenn die minimale Anzahl anderer Benutzer bereit ist.",
|
"unpause-ifminusersready-tooltip" : u"Wenn du Play drückst und nicht bereit bist, wird nur gestartet, wenn die minimale Anzahl anderer Benutzer bereit ist.",
|
||||||
"trusteddomains-arguments-tooltip" : u"Domains that it is okay for Syncplay to automatically switch to when shared playlists is enabled.", # TODO: Translate into German
|
"trusteddomains-arguments-tooltip" : u"Domains that it is okay for Syncplay to automatically switch to when shared playlists is enabled.", # TODO: Translate into German
|
||||||
|
|
||||||
|
"chatinputenabled-tooltip": u"Enable chat input in mpv (press enter to chat, enter to send, escape to cancel)", # TODO: Translate
|
||||||
|
"chatdirectinput-tooltip" : u"Skip having to press 'enter' to go into chat input mode in mpv. Press TAB in mpv to temporarily disable this feature.", # TODO: Translate
|
||||||
|
"font-label-tooltip": u"Font used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.", # TODO: Translate
|
||||||
|
"set-input-font-tooltip": u"Font family used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.", # TODO: Translate
|
||||||
|
"set-input-colour-tooltip": u"Font colour used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.", # TODO: Translate
|
||||||
|
"chatinputposition-tooltip": u"Location in mpv where chat input text will appear when you press enter and type.", # TODO: Translate
|
||||||
|
"chatinputposition-top-tooltip": u"Place chat input at top of mpv window.", # TODO: Translate
|
||||||
|
"chatinputposition-middle-tooltip": u"Place chat input in dead centre of mpv window.", # TODO: Translate
|
||||||
|
"chatinputposition-bottom-tooltip": u"Place chat input at bottom of mpv window.", # TODO: Translate
|
||||||
|
"chatoutputenabled-tooltip": u"Show chat messages in OSD (if supported by media player).", # TODO: Translate
|
||||||
|
"font-output-label-tooltip": u"Chat output font.", # TODO: Translate
|
||||||
|
"set-output-font-tooltip": u"Font used for when displaying chat messages.", # TODO: Translate
|
||||||
|
"chatoutputmode-tooltip": u"How chat messages are displayed.", # TODO: Translate
|
||||||
|
"chatoutputmode-chatroom-tooltip": u"Display new lines of chat directly below previous line.", # TODO: Translate
|
||||||
|
"chatoutputmode-scrolling-tooltip": u"Scroll chat text from right to left.", # TODO: Translate
|
||||||
|
|
||||||
"help-tooltip" : u"Öffnet Hilfe auf syncplay.pl [Englisch]",
|
"help-tooltip" : u"Öffnet Hilfe auf syncplay.pl [Englisch]",
|
||||||
"reset-tooltip" : u"Alle Einstellungen auf Standardwerte zurücksetzen.",
|
"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.",
|
"update-server-list-tooltip" : u"Mit syncplay.pl verbinden um die Liste öffentlicher Server zu aktualisieren.",
|
||||||
@ -420,6 +458,7 @@ de = {
|
|||||||
"addurlstoplaylist-menu-label" : u"Add URL(s) to bottom of playlist",
|
"addurlstoplaylist-menu-label" : u"Add URL(s) to bottom of playlist",
|
||||||
"editplaylist-menu-label": u"Edit playlist",
|
"editplaylist-menu-label": u"Edit playlist",
|
||||||
|
|
||||||
|
"open-containing-folder": u"Open folder containing this file",
|
||||||
"addusersfiletoplaylist-menu-label" : u"Add {} file to playlist", # item owner indicator
|
"addusersfiletoplaylist-menu-label" : u"Add {} file to playlist", # item owner indicator
|
||||||
"addusersstreamstoplaylist-menu-label" : u"Add {} stream to playlist", # item owner indicator
|
"addusersstreamstoplaylist-menu-label" : u"Add {} stream to playlist", # item owner indicator
|
||||||
"openusersstream-menu-label" : u"Open {} stream", # [username]'s
|
"openusersstream-menu-label" : u"Open {} stream", # [username]'s
|
||||||
|
|||||||
@ -17,23 +17,23 @@ en = {
|
|||||||
"connected-successful-notification" : "Successfully connected to server",
|
"connected-successful-notification" : "Successfully connected to server",
|
||||||
"retrying-notification" : "%s, Retrying in %d seconds...", # Seconds
|
"retrying-notification" : "%s, Retrying in %d seconds...", # Seconds
|
||||||
|
|
||||||
"rewind-notification" : "Rewinded due to time difference with <{}>", # User
|
"rewind-notification" : "Rewinded due to time difference with {}", # User
|
||||||
"fastforward-notification" : "Fast-forwarded due to time difference with <{}>", # User
|
"fastforward-notification" : "Fast-forwarded due to time difference with {}", # User
|
||||||
"slowdown-notification" : "Slowing down due to time difference with <{}>", # User
|
"slowdown-notification" : "Slowing down due to time difference with {}", # User
|
||||||
"revert-notification" : "Reverting speed back to normal",
|
"revert-notification" : "Reverting speed back to normal",
|
||||||
|
|
||||||
"pause-notification" : u"<{}> paused", # User
|
"pause-notification" : u"{} paused", # User
|
||||||
"unpause-notification" : u"<{}> unpaused", # User
|
"unpause-notification" : u"{} unpaused", # User
|
||||||
"seek-notification" : u"<{}> jumped from {} to {}", # User, from time, to time
|
"seek-notification" : u"{} jumped from {} to {}", # User, from time, to time
|
||||||
|
|
||||||
"current-offset-notification" : "Current offset: {} seconds", # Offset
|
"current-offset-notification" : "Current offset: {} seconds", # Offset
|
||||||
|
|
||||||
"media-directory-list-updated-notification" : u"Syncplay media directories have been updated.",
|
"media-directory-list-updated-notification" : u"Syncplay media directories have been updated.",
|
||||||
|
|
||||||
"room-join-notification" : u"<{}> has joined the room: '{}'", # User
|
"room-join-notification" : u"{} has joined the room: '{}'", # User
|
||||||
"left-notification" : u"<{}> has left", # User
|
"left-notification" : u"{} has left", # User
|
||||||
"left-paused-notification" : u"<{}> left, <{}> paused", # User who left, User who paused
|
"left-paused-notification" : u"{} left, {} paused", # User who left, User who paused
|
||||||
"playing-notification" : u"<{}> is playing '{}' ({})", # User, file, duration
|
"playing-notification" : u"{} is playing '{}' ({})", # User, file, duration
|
||||||
"playing-notification/room-addendum" : u" in room: '{}'", # Room
|
"playing-notification/room-addendum" : u" in room: '{}'", # Room
|
||||||
|
|
||||||
"not-all-ready" : u"Not ready: {}", # Usernames
|
"not-all-ready" : u"Not ready: {}", # Usernames
|
||||||
@ -44,11 +44,11 @@ en = {
|
|||||||
"autoplaying-notification" : u"Auto-playing in {}...", # Number of seconds until playback will start
|
"autoplaying-notification" : u"Auto-playing in {}...", # Number of seconds until playback will start
|
||||||
|
|
||||||
"identifying-as-controller-notification" : u"Identifying as room operator with password '{}'...",
|
"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.",
|
"failed-to-identify-as-controller-notification" : u"{} failed to identify as a room operator.",
|
||||||
"authenticated-as-controller-notification" : u"<{}> authenticated as a room operator",
|
"authenticated-as-controller-notification" : u"{} authenticated as a room operator",
|
||||||
"created-controlled-room-notification" : u"Created managed room '{}' with password '{}'. Please save this information for future reference!", # RoomName, operatorPassword
|
"created-controlled-room-notification" : u"Created managed room '{}' with password '{}'. Please save this information for future reference!", # RoomName, operatorPassword
|
||||||
|
|
||||||
"file-different-notification" : "File you are playing appears to be different from <{}>'s", # User
|
"file-different-notification" : "File you are playing appears to be different from {}'s", # User
|
||||||
"file-differences-notification" : u"Your file differs in the following way(s): {}", # Differences
|
"file-differences-notification" : u"Your file differs in the following way(s): {}", # Differences
|
||||||
"room-file-differences" : u"File differences: {}", # File differences (filename, size, and/or duration)
|
"room-file-differences" : u"File differences: {}", # File differences (filename, size, and/or duration)
|
||||||
"file-difference-filename" : u"name",
|
"file-difference-filename" : u"name",
|
||||||
@ -133,7 +133,12 @@ en = {
|
|||||||
"vlc-failed-noscript": "VLC has reported that the syncplay.lua interface script has not been installed. Please refer to http://syncplay.pl/LUA/ for instructions.",
|
"vlc-failed-noscript": "VLC has reported that the syncplay.lua interface script has not been installed. Please refer to http://syncplay.pl/LUA/ for instructions.",
|
||||||
"vlc-failed-versioncheck": "This version of VLC is not supported by Syncplay.",
|
"vlc-failed-versioncheck": "This version of VLC is not supported by Syncplay.",
|
||||||
|
|
||||||
"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
|
"feature-sharedPlaylists" : u"shared playlists", # used for not-supported-by-server-error
|
||||||
|
"feature-chat" : u"chat", # used for not-supported-by-server-error
|
||||||
|
"feature-readiness" : u"readiness", # used for not-supported-by-server-error
|
||||||
|
"feature-managedRooms" : u"managed rooms", # used for not-supported-by-server-error
|
||||||
|
|
||||||
|
"not-supported-by-server-error" : u"The {} feature is not supported by this server..", #feature
|
||||||
"shared-playlists-not-supported-by-server-error" : "The shared playlists feature may not be supported by the server. To ensure that it works correctly requires a server running Syncplay {}+, but the server is running Syncplay {}.", #minVersion, serverVersion
|
"shared-playlists-not-supported-by-server-error" : "The shared playlists feature may not be supported by the server. To ensure that it works correctly requires a server running Syncplay {}+, but the server is running Syncplay {}.", #minVersion, serverVersion
|
||||||
"shared-playlists-disabled-by-server-error" : "The shared playlist feature has been disabled in the server configuration. To use this feature you will need to connect to a different server.",
|
"shared-playlists-disabled-by-server-error" : "The shared playlist feature has been disabled in the server configuration. To use this feature you will need to connect to a different server.",
|
||||||
|
|
||||||
@ -225,6 +230,7 @@ en = {
|
|||||||
"messages-label" : "Messages",
|
"messages-label" : "Messages",
|
||||||
"messages-osd-title" : "On-screen Display settings",
|
"messages-osd-title" : "On-screen Display settings",
|
||||||
"messages-other-title" : "Other display settings",
|
"messages-other-title" : "Other display settings",
|
||||||
|
"chat-label" : u"Chat",
|
||||||
"privacy-label" : "Privacy", # Currently unused, but will be brought back if more space is needed in Misc tab
|
"privacy-label" : "Privacy", # Currently unused, but will be brought back if more space is needed in Misc tab
|
||||||
"privacy-title" : "Privacy settings",
|
"privacy-title" : "Privacy settings",
|
||||||
"unpause-title" : u"If you press play, set as ready and:",
|
"unpause-title" : u"If you press play, set as ready and:",
|
||||||
@ -234,6 +240,27 @@ en = {
|
|||||||
"unpause-always" : u"Always unpause",
|
"unpause-always" : u"Always unpause",
|
||||||
"syncplay-trusteddomains-title": u"Trusted domains (for streaming services and hosted content)",
|
"syncplay-trusteddomains-title": u"Trusted domains (for streaming services and hosted content)",
|
||||||
|
|
||||||
|
"chat-title" : u"Chat message input",
|
||||||
|
"chatinputenabled-label" : u"Enable chat input via mpv",
|
||||||
|
"chatdirectinput-label" : u"Allow instant chat input (bypass having to press enter key to chat)",
|
||||||
|
"chatinputfont-label" : u"Chat input font",
|
||||||
|
"chatfont-label" : u"Set font",
|
||||||
|
"chatcolour-label" : u"Set colour",
|
||||||
|
"chatinputposition-label" : u"Position of message input area in mpv",
|
||||||
|
"chat-top-option" : u"Top",
|
||||||
|
"chat-middle-option" : u"Middle",
|
||||||
|
"chat-bottom-option" : u"Bottom",
|
||||||
|
"chatoutputfont-label": u"Chat output font",
|
||||||
|
"chatoutputenabled-label": u"Enable chat output in media player (mpv only for now)",
|
||||||
|
"chatoutputposition-label": u"Output mode",
|
||||||
|
"chat-chatroom-option": u"Chatroom style",
|
||||||
|
"chat-scrolling-option": u"Scrolling style",
|
||||||
|
|
||||||
|
"mpv-key-tab-hint": u"[TAB] to toggle access to alphabet row key shortcuts.",
|
||||||
|
"mpv-key-hint": u"[ENTER] to send message. [ESC] to escape chat mode.",
|
||||||
|
"alphakey-mode-warning-first-line": u"You can temporarily use old mpv bindings with a-z keys.",
|
||||||
|
"alphakey-mode-warning-second-line": u"Press [TAB] to return to Syncplay chat mode.",
|
||||||
|
|
||||||
"help-label" : "Help",
|
"help-label" : "Help",
|
||||||
"reset-label" : "Restore defaults",
|
"reset-label" : "Restore defaults",
|
||||||
"run-label" : "Run Syncplay",
|
"run-label" : "Run Syncplay",
|
||||||
@ -355,6 +382,22 @@ en = {
|
|||||||
"unpause-ifminusersready-tooltip" : u"If you press unpause when not ready, it will only unpause if others are ready and minimum users threshold is met.",
|
"unpause-ifminusersready-tooltip" : u"If you press unpause when not ready, it will only unpause if others are ready and minimum users threshold is met.",
|
||||||
"trusteddomains-arguments-tooltip" : u"Domains that it is okay for Syncplay to automatically switch to when shared playlists is enabled.",
|
"trusteddomains-arguments-tooltip" : u"Domains that it is okay for Syncplay to automatically switch to when shared playlists is enabled.",
|
||||||
|
|
||||||
|
"chatinputenabled-tooltip" : u"Enable chat input in mpv (press enter to chat, enter to send, escape to cancel)",
|
||||||
|
"chatdirectinput-tooltip" : u"Skip having to press 'enter' to go into chat input mode in mpv. Press TAB in mpv to temporarily disable this feature.",
|
||||||
|
"font-label-tooltip" : u"Font used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.",
|
||||||
|
"set-input-font-tooltip" : u"Font family used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.",
|
||||||
|
"set-input-colour-tooltip" : u"Font colour used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.",
|
||||||
|
"chatinputposition-tooltip" : u"Location in mpv where chat input text will appear when you press enter and type.",
|
||||||
|
"chatinputposition-top-tooltip" : u"Place chat input at top of mpv window.",
|
||||||
|
"chatinputposition-middle-tooltip" : u"Place chat input in dead centre of mpv window.",
|
||||||
|
"chatinputposition-bottom-tooltip" : u"Place chat input at bottom of mpv window.",
|
||||||
|
"chatoutputenabled-tooltip": u"Show chat messages in OSD (if supported by media player).",
|
||||||
|
"font-output-label-tooltip": u"Chat output font.",
|
||||||
|
"set-output-font-tooltip": u"Font used for when displaying chat messages.",
|
||||||
|
"chatoutputmode-tooltip": u"How chat messages are displayed.",
|
||||||
|
"chatoutputmode-chatroom-tooltip": u"Display new lines of chat directly below previous line.",
|
||||||
|
"chatoutputmode-scrolling-tooltip": u"Scroll chat text from right to left.",
|
||||||
|
|
||||||
"help-tooltip" : "Opens the Syncplay.pl user guide.",
|
"help-tooltip" : "Opens the Syncplay.pl user guide.",
|
||||||
"reset-tooltip" : "Reset all settings to the default configuration.",
|
"reset-tooltip" : "Reset all settings to the default configuration.",
|
||||||
"update-server-list-tooltip" : u"Connect to syncplay.pl to update list of public servers.",
|
"update-server-list-tooltip" : u"Connect to syncplay.pl to update list of public servers.",
|
||||||
|
|||||||
@ -17,23 +17,23 @@ ru = {
|
|||||||
"connected-successful-notification" : u"Соединение с сервером установлено",
|
"connected-successful-notification" : u"Соединение с сервером установлено",
|
||||||
"retrying-notification" : u"%s, следующая попытка через %d секунд(ы)...", # Seconds
|
"retrying-notification" : u"%s, следующая попытка через %d секунд(ы)...", # Seconds
|
||||||
|
|
||||||
"rewind-notification" : u"Перемотано из-за разницы во времени с <{}>", # User
|
"rewind-notification" : u"Перемотано из-за разницы во времени с {}", # User
|
||||||
"fastforward-notification" : u"Ускорено из-за разницы во времени с <{}>", # User
|
"fastforward-notification" : u"Ускорено из-за разницы во времени с {}", # User
|
||||||
"slowdown-notification" : u"Воспроизведение замедлено из-за разницы во времени с <{}>", # User
|
"slowdown-notification" : u"Воспроизведение замедлено из-за разницы во времени с {}", # User
|
||||||
"revert-notification" : u"Возвращаемся к нормальной скорости воспроизведения",
|
"revert-notification" : u"Возвращаемся к нормальной скорости воспроизведения",
|
||||||
|
|
||||||
"pause-notification" : u"<{}> приостановил воспроизведение", # User
|
"pause-notification" : u"{} приостановил воспроизведение", # User
|
||||||
"unpause-notification" : u"<{}> возобновил воспроизведение", # User
|
"unpause-notification" : u"{} возобновил воспроизведение", # User
|
||||||
"seek-notification" : u"<{}> перемотал с {} на {}", # User, from time, to time
|
"seek-notification" : u"{} перемотал с {} на {}", # User, from time, to time
|
||||||
|
|
||||||
"current-offset-notification" : u"Текущее смещение: {} секунд(ы)", # Offset
|
"current-offset-notification" : u"Текущее смещение: {} секунд(ы)", # Offset
|
||||||
|
|
||||||
"media-directory-list-updated-notification" : u"Папки воспроизведения обновлены.",
|
"media-directory-list-updated-notification" : u"Папки воспроизведения обновлены.",
|
||||||
|
|
||||||
"room-join-notification" : u"<{}> зашел в комнату: '{}'", # User
|
"room-join-notification" : u"{} зашел в комнату: '{}'", # User
|
||||||
"left-notification" : u"<{}> покинул комнату", # User
|
"left-notification" : u"{} покинул комнату", # User
|
||||||
"left-paused-notification" : u"<{}> покинул комнату, <{}> приостановил воспроизведение", # User who left, User who paused
|
"left-paused-notification" : u"{} покинул комнату, {} приостановил воспроизведение", # User who left, User who paused
|
||||||
"playing-notification" : u"<{}> включил '{}' ({})", # User, file, duration
|
"playing-notification" : u"{} включил '{}' ({})", # User, file, duration
|
||||||
"playing-notification/room-addendum" : u" в комнате: '{}'", # Room
|
"playing-notification/room-addendum" : u" в комнате: '{}'", # Room
|
||||||
|
|
||||||
"not-all-ready" : u"Не готовы: {}", # Usernames
|
"not-all-ready" : u"Не готовы: {}", # Usernames
|
||||||
@ -44,11 +44,11 @@ ru = {
|
|||||||
"autoplaying-notification" : u"Автовоспроизведение через {}...", # Number of seconds until playback will start
|
"autoplaying-notification" : u"Автовоспроизведение через {}...", # Number of seconds until playback will start
|
||||||
|
|
||||||
"identifying-as-controller-notification" : u"Идентификация как оператора комнаты с паролем '{}'...",
|
"identifying-as-controller-notification" : u"Идентификация как оператора комнаты с паролем '{}'...",
|
||||||
"failed-to-identify-as-controller-notification" : u"<{}> не прошел идентификацию в качестве оператора комнаты.",
|
"failed-to-identify-as-controller-notification" : u"{} не прошел идентификацию в качестве оператора комнаты.",
|
||||||
"authenticated-as-controller-notification" : u"<{}> вошел как оператор комнаты.",
|
"authenticated-as-controller-notification" : u"{} вошел как оператор комнаты.",
|
||||||
"created-controlled-room-notification" : u"Создана управляемая комната '{}' с паролем '{}'. Сохраните эти данные!", # RoomName, operatorPassword
|
"created-controlled-room-notification" : u"Создана управляемая комната '{}' с паролем '{}'. Сохраните эти данные!", # RoomName, operatorPassword
|
||||||
|
|
||||||
"file-different-notification" : u"Вероятно, файл, который Вы смотрите, отличается от того, который смотрит <{}>.", # User
|
"file-different-notification" : u"Вероятно, файл, который Вы смотрите, отличается от того, который смотрит {}.", # User
|
||||||
"file-differences-notification" : u"Ваш файл отличается: {}", # Differences
|
"file-differences-notification" : u"Ваш файл отличается: {}", # Differences
|
||||||
"room-file-differences" : u"Несовпадения файла: {}", # File differences (filename, size, and/or duration)
|
"room-file-differences" : u"Несовпадения файла: {}", # File differences (filename, size, and/or duration)
|
||||||
"file-difference-filename" : u"имя",
|
"file-difference-filename" : u"имя",
|
||||||
@ -135,7 +135,13 @@ ru = {
|
|||||||
"vlc-failed-versioncheck" : u"Данная версия VLC не поддерживается Syncplay. Пожалуйста, используйте VLC версии 2 или выше.",
|
"vlc-failed-versioncheck" : u"Данная версия VLC не поддерживается Syncplay. Пожалуйста, используйте VLC версии 2 или выше.",
|
||||||
"vlc-failed-other" : u"Во время загрузки скрипта интерфейса syncplay.lua в VLC произошла следующая ошибка: {}", # Syncplay Error
|
"vlc-failed-other" : u"Во время загрузки скрипта интерфейса syncplay.lua в VLC произошла следующая ошибка: {}", # Syncplay Error
|
||||||
|
|
||||||
"not-supported-by-server-error" : u"Эта возможность не поддерживается сервером. Требуется сервер Syncplay {}+, вы подключены к серверу Syncplay {}.", #minVersion, serverVersion
|
"feature-sharedPlaylists": u"shared playlists", # used for not-supported-by-server-error # TODO: Translate
|
||||||
|
"feature-chat": u"chat", # used for not-supported-by-server-error # TODO: Translate
|
||||||
|
"feature-readiness": u"readiness", # used for not-supported-by-server-error # TODO: Translate
|
||||||
|
"feature-managedRooms": u"managed rooms", # used for not-supported-by-server-error # TODO: Translate
|
||||||
|
|
||||||
|
"not-supported-by-server-error": u"The {} feature is not supported by this server..", # feature # TODO: Translate
|
||||||
|
#OLD TRANSLATION: "not-supported-by-server-error" : u"Эта возможность не поддерживается сервером. Требуется сервер Syncplay {}+, вы подключены к серверу Syncplay {}.", #minVersion, serverVersion
|
||||||
"shared-playlists-not-supported-by-server-error" : u"Общие списки воспроизведения могут не поддерживаться сервером. Для корректной работы требуется сервер Syncplay {}+, вы подключены к серверу Syncplay {}.", #minVersion, serverVersion
|
"shared-playlists-not-supported-by-server-error" : u"Общие списки воспроизведения могут не поддерживаться сервером. Для корректной работы требуется сервер Syncplay {}+, вы подключены к серверу Syncplay {}.", #minVersion, serverVersion
|
||||||
"shared-playlists-disabled-by-server-error" : "The shared playlist feature has been disabled in the server configuration. To use this feature you will need to connect to a different server.", # TODO: Translate
|
"shared-playlists-disabled-by-server-error" : "The shared playlist feature has been disabled in the server configuration. To use this feature you will need to connect to a different server.", # TODO: Translate
|
||||||
|
|
||||||
@ -227,6 +233,7 @@ ru = {
|
|||||||
"messages-label" : u"Сообщения",
|
"messages-label" : u"Сообщения",
|
||||||
"messages-osd-title" : u"Настройки OSD",
|
"messages-osd-title" : u"Настройки OSD",
|
||||||
"messages-other-title" : u"Другие настройки отображения",
|
"messages-other-title" : u"Другие настройки отображения",
|
||||||
|
"chat-label" : u"Chat", # TODO: Translate
|
||||||
"privacy-label" : u"Приватность",
|
"privacy-label" : u"Приватность",
|
||||||
"privacy-title" : u"Настройки приватности",
|
"privacy-title" : u"Настройки приватности",
|
||||||
"unpause-title" : u"Если вы стартуете, то:",
|
"unpause-title" : u"Если вы стартуете, то:",
|
||||||
@ -237,6 +244,27 @@ ru = {
|
|||||||
"syncplay-trusteddomains-title": u"Доверенные сайты (стрим-сервисы, видеохостинги, файлы в сети)",
|
"syncplay-trusteddomains-title": u"Доверенные сайты (стрим-сервисы, видеохостинги, файлы в сети)",
|
||||||
"addtrusteddomain-menu-label" : u"Добавить {} как доверенный сайт", # Domain
|
"addtrusteddomain-menu-label" : u"Добавить {} как доверенный сайт", # Domain
|
||||||
|
|
||||||
|
"chat-title": u"Chat message input", # TODO: Translate
|
||||||
|
"chatinputenabled-label": u"Enable chat input via mpv (using enter key)", # TODO: Translate
|
||||||
|
"chatdirectinput-label" : u"Allow instant chat input (bypass having to press enter key to chat)", # TODO: Translate
|
||||||
|
"chatinputfont-label": u"Chat input font", # TODO: Translate
|
||||||
|
"chatfont-label": u"Set font", # TODO: Translate
|
||||||
|
"chatcolour-label": u"Set colour", # TODO: Translate
|
||||||
|
"chatinputposition-label": u"Position of message input area in mpv", # TODO: Translate
|
||||||
|
"chat-top-option": u"Top", # TODO: Translate
|
||||||
|
"chat-middle-option": u"Middle", # TODO: Translate
|
||||||
|
"chat-bottom-option": u"Bottom", # TODO: Translate
|
||||||
|
"chatoutputfont-label": u"Chat output font", # TODO: Translate
|
||||||
|
"chatoutputenabled-label": u"Enable chat output in media player (mpv only for now)", # TODO: Translate
|
||||||
|
"chatoutputposition-label": u"Output mode", # TODO: Translate
|
||||||
|
"chat-chatroom-option": u"Chatroom style", # TODO: Translate
|
||||||
|
"chat-scrolling-option": u"Scrolling style", # TODO: Translate
|
||||||
|
|
||||||
|
"mpv-key-tab-hint": u"[TAB] to toggle access to alphabet row key shortcuts.", # TODO: Translate
|
||||||
|
"mpv-key-hint": u"[ENTER] to send message. [ESC] to escape chat mode.", # TODO: Translate
|
||||||
|
"alphakey-mode-warning-first-line": u"You can temporarily use old mpv bindings with a-z keys.", # TODO: Translate
|
||||||
|
"alphakey-mode-warning-second-line": u"Press [TAB] to return to Syncplay chat mode.", # TODO: Translate
|
||||||
|
|
||||||
"help-label" : u"Помощь",
|
"help-label" : u"Помощь",
|
||||||
"reset-label" : u"Сброс настроек",
|
"reset-label" : u"Сброс настроек",
|
||||||
"run-label" : u"Запустить",
|
"run-label" : u"Запустить",
|
||||||
@ -357,6 +385,22 @@ ru = {
|
|||||||
"unpause-ifminusersready-tooltip" : u"Когда вы стартуете не готовым, воспроизведение начнется, если остальные готовы и присутствует достаточное число зрителей.",
|
"unpause-ifminusersready-tooltip" : u"Когда вы стартуете не готовым, воспроизведение начнется, если остальные готовы и присутствует достаточное число зрителей.",
|
||||||
"trusteddomains-arguments-tooltip" : u"Сайты, которые разрешены для автоматического воспроизведения из общего списка воспроизведения.",
|
"trusteddomains-arguments-tooltip" : u"Сайты, которые разрешены для автоматического воспроизведения из общего списка воспроизведения.",
|
||||||
|
|
||||||
|
"chatinputenabled-tooltip": u"Enable chat input in mpv (press enter to chat, enter to send, escape to cancel)",# TODO: Translate
|
||||||
|
"chatdirectinput-tooltip" : u"Skip having to press 'enter' to go into chat input mode in mpv. Press TAB in mpv to temporarily disable this feature.", # TODO: Translate
|
||||||
|
"font-label-tooltip": u"Font used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.",# TODO: Translate
|
||||||
|
"set-input-font-tooltip": u"Font family used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.",# TODO: Translate
|
||||||
|
"set-input-colour-tooltip": u"Font colour used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.",# TODO: Translate
|
||||||
|
"chatinputposition-tooltip": u"Location in mpv where chat input text will appear when you press enter and type.",# TODO: Translate
|
||||||
|
"chatinputposition-top-tooltip": u"Place chat input at top of mpv window.", # TODO: Translate
|
||||||
|
"chatinputposition-middle-tooltip": u"Place chat input in dead centre of mpv window.", # TODO: Translate
|
||||||
|
"chatinputposition-bottom-tooltip": u"Place chat input at bottom of mpv window.", # TODO: Translate
|
||||||
|
"chatoutputenabled-tooltip": u"Show chat messages in OSD (if supported by media player).", # TODO: Translate
|
||||||
|
"font-output-label-tooltip": u"Chat output font.", # TODO: Translate
|
||||||
|
"set-output-font-tooltip": u"Font used for when displaying chat messages.", # TODO: Translate
|
||||||
|
"chatoutputmode-tooltip": u"How chat messages are displayed.", # TODO: Translate
|
||||||
|
"chatoutputmode-chatroom-tooltip": u"Display new lines of chat directly below previous line.", # TODO: Translate
|
||||||
|
"chatoutputmode-scrolling-tooltip": u"Scroll chat text from right to left.", # TODO: Translate
|
||||||
|
|
||||||
"help-tooltip" : u"Открыть Руководство Пользователя на Syncplay.pl.",
|
"help-tooltip" : u"Открыть Руководство Пользователя на Syncplay.pl.",
|
||||||
"reset-tooltip" : u"Сбрасывает все настройки Syncplay в начальное состояние.",
|
"reset-tooltip" : u"Сбрасывает все настройки Syncplay в начальное состояние.",
|
||||||
"update-server-list-tooltip" : u"Обновить список публичных серверов от syncplay.pl.",
|
"update-server-list-tooltip" : u"Обновить список публичных серверов от syncplay.pl.",
|
||||||
@ -419,6 +463,7 @@ ru = {
|
|||||||
"addurlstoplaylist-menu-label" : u"Добавить ссылку в очередь",
|
"addurlstoplaylist-menu-label" : u"Добавить ссылку в очередь",
|
||||||
"editplaylist-menu-label": u"Редактировать список",
|
"editplaylist-menu-label": u"Редактировать список",
|
||||||
|
|
||||||
|
"open-containing-folder": u"Open folder containing this file", # TODO: Traslate
|
||||||
"addusersfiletoplaylist-menu-label" : u"Добавить файл {} в список воспроизведения", # item owner indicator
|
"addusersfiletoplaylist-menu-label" : u"Добавить файл {} в список воспроизведения", # item owner indicator
|
||||||
"addusersstreamstoplaylist-menu-label" : u"Добавить поток {} в список воспроизведения", # item owner indicator
|
"addusersstreamstoplaylist-menu-label" : u"Добавить поток {} в список воспроизведения", # item owner indicator
|
||||||
"openusersstream-menu-label" : u"Открыть поток от {}", # [username]'s
|
"openusersstream-menu-label" : u"Открыть поток от {}", # [username]'s
|
||||||
|
|||||||
@ -12,7 +12,7 @@ class BasePlayer(object):
|
|||||||
'''
|
'''
|
||||||
Display given message on player's OSD or similar means
|
Display given message on player's OSD or similar means
|
||||||
'''
|
'''
|
||||||
def displayMessage(self, message, duration = (constants.OSD_DURATION*1000)):
|
def displayMessage(self, message, duration = (constants.OSD_DURATION*1000), secondaryOSD=False, mood=constants.MESSAGE_NEUTRAL):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
'''
|
'''
|
||||||
@ -34,6 +34,12 @@ class BasePlayer(object):
|
|||||||
def setPaused(self, value):
|
def setPaused(self, value):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
'''
|
||||||
|
@type value: list
|
||||||
|
'''
|
||||||
|
def setFeatures(self, featureList):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
'''
|
'''
|
||||||
@type value: float
|
@type value: float
|
||||||
'''
|
'''
|
||||||
|
|||||||
@ -306,8 +306,9 @@ class MpcHcApi:
|
|||||||
|
|
||||||
class MPCHCAPIPlayer(BasePlayer):
|
class MPCHCAPIPlayer(BasePlayer):
|
||||||
speedSupported = False
|
speedSupported = False
|
||||||
secondaryOSDSupported = False
|
alertOSDSupported = False
|
||||||
customOpenDialog = False
|
customOpenDialog = False
|
||||||
|
chatOSDSupported = False
|
||||||
osdMessageSeparator = "; "
|
osdMessageSeparator = "; "
|
||||||
|
|
||||||
def __init__(self, client):
|
def __init__(self, client):
|
||||||
@ -399,7 +400,7 @@ class MPCHCAPIPlayer(BasePlayer):
|
|||||||
def openFile(self, filePath, resetPosition=False):
|
def openFile(self, filePath, resetPosition=False):
|
||||||
self._mpcApi.openFile(filePath)
|
self._mpcApi.openFile(filePath)
|
||||||
|
|
||||||
def displayMessage(self, message, duration = (constants.OSD_DURATION*1000), secondaryOSD=False):
|
def displayMessage(self, message, duration = (constants.OSD_DURATION*1000), OSDType=constants.OSD_NOTIFICATION, mood=constants.MESSAGE_NEUTRAL):
|
||||||
self._mpcApi.sendOsd(message, constants.MPC_OSD_POSITION, duration)
|
self._mpcApi.sendOsd(message, constants.MPC_OSD_POSITION, duration)
|
||||||
|
|
||||||
@retry(MpcHcApi.PlayerNotReadyException, constants.MPC_MAX_RETRIES, constants.MPC_RETRY_WAIT_TIME, 1)
|
@retry(MpcHcApi.PlayerNotReadyException, constants.MPC_MAX_RETRIES, constants.MPC_RETRY_WAIT_TIME, 1)
|
||||||
@ -411,6 +412,8 @@ class MPCHCAPIPlayer(BasePlayer):
|
|||||||
self._mpcApi.pause()
|
self._mpcApi.pause()
|
||||||
else:
|
else:
|
||||||
self._mpcApi.unpause()
|
self._mpcApi.unpause()
|
||||||
|
def setFeatures(self, featureList):
|
||||||
|
pass
|
||||||
|
|
||||||
@retry(MpcHcApi.PlayerNotReadyException, constants.MPC_MAX_RETRIES, constants.MPC_RETRY_WAIT_TIME, 1)
|
@retry(MpcHcApi.PlayerNotReadyException, constants.MPC_MAX_RETRIES, constants.MPC_RETRY_WAIT_TIME, 1)
|
||||||
def setPosition(self, value):
|
def setPosition(self, value):
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
# coding:utf8
|
||||||
import subprocess
|
import subprocess
|
||||||
import re
|
import re
|
||||||
import threading
|
import threading
|
||||||
@ -11,7 +12,8 @@ from syncplay.utils import isWindows
|
|||||||
class MplayerPlayer(BasePlayer):
|
class MplayerPlayer(BasePlayer):
|
||||||
speedSupported = True
|
speedSupported = True
|
||||||
customOpenDialog = False
|
customOpenDialog = False
|
||||||
secondaryOSDSupported = False
|
alertOSDSupported = False
|
||||||
|
chatOSDSupported = False
|
||||||
osdMessageSeparator = "; "
|
osdMessageSeparator = "; "
|
||||||
|
|
||||||
RE_ANSWER = re.compile(constants.MPLAYER_ANSWER_REGEX)
|
RE_ANSWER = re.compile(constants.MPLAYER_ANSWER_REGEX)
|
||||||
@ -88,8 +90,15 @@ class MplayerPlayer(BasePlayer):
|
|||||||
def _getProperty(self, property_):
|
def _getProperty(self, property_):
|
||||||
self._listener.sendLine("get_property {}".format(property_))
|
self._listener.sendLine("get_property {}".format(property_))
|
||||||
|
|
||||||
def displayMessage(self, message, duration=(constants.OSD_DURATION * 1000), secondaryOSD=False):
|
def displayMessage(self, message, duration=(constants.OSD_DURATION * 1000), OSDType=constants.OSD_NOTIFICATION, mood=constants.MESSAGE_NEUTRAL):
|
||||||
self._listener.sendLine(u'{} "{!s}" {} {}'.format(self.OSD_QUERY, self._stripNewlines(message), duration, constants.MPLAYER_OSD_LEVEL).encode('utf-8'))
|
messageString = self._sanitizeText(message.replace("\\n", "<NEWLINE>")).replace("<NEWLINE>", "\\n")
|
||||||
|
self._listener.sendLine(u'{} "{!s}" {} {}'.format(self.OSD_QUERY, messageString, duration, constants.MPLAYER_OSD_LEVEL).encode('utf-8'))
|
||||||
|
|
||||||
|
def displayChatMessage(self, username, message):
|
||||||
|
messageString = u"<{}> {}".format(username, message)
|
||||||
|
messageString = self._sanitizeText(messageString.replace("\\n", "<NEWLINE>")).replace("<NEWLINE>", "\\n")
|
||||||
|
duration = int(constants.OSD_DURATION * 1000)
|
||||||
|
self._listener.sendLine(u'{} "{!s}" {} {}'.format(self.OSD_QUERY, messageString, duration, constants.MPLAYER_OSD_LEVEL).encode('utf-8'))
|
||||||
|
|
||||||
def setSpeed(self, value):
|
def setSpeed(self, value):
|
||||||
self._setProperty('speed', "{:.2f}".format(value))
|
self._setProperty('speed', "{:.2f}".format(value))
|
||||||
@ -105,6 +114,9 @@ class MplayerPlayer(BasePlayer):
|
|||||||
self.setPaused(self._client.getGlobalPaused())
|
self.setPaused(self._client.getGlobalPaused())
|
||||||
self.setPosition(self._client.getGlobalPosition())
|
self.setPosition(self._client.getGlobalPosition())
|
||||||
|
|
||||||
|
def setFeatures(self, featureList):
|
||||||
|
pass
|
||||||
|
|
||||||
def setPosition(self, value):
|
def setPosition(self, value):
|
||||||
self._position = max(value,0)
|
self._position = max(value,0)
|
||||||
self._setProperty(self.POSITION_QUERY, "{}".format(value))
|
self._setProperty(self.POSITION_QUERY, "{}".format(value))
|
||||||
@ -130,9 +142,16 @@ class MplayerPlayer(BasePlayer):
|
|||||||
def _getPosition(self):
|
def _getPosition(self):
|
||||||
self._getProperty(self.POSITION_QUERY)
|
self._getProperty(self.POSITION_QUERY)
|
||||||
|
|
||||||
def _stripNewlines(self, text):
|
def _sanitizeText(self, text):
|
||||||
text = text.replace("\r", "")
|
text = text.replace("\r", "")
|
||||||
text = text.replace("\n", "")
|
text = text.replace("\n", "")
|
||||||
|
text = text.replace("\\\"", "<SYNCPLAY_QUOTE>")
|
||||||
|
text = text.replace("\"", "<SYNCPLAY_QUOTE>")
|
||||||
|
text = text.replace("%", "%%")
|
||||||
|
text = text.replace("\\", "\\\\")
|
||||||
|
text = text.replace("{", "\\\\{")
|
||||||
|
text = text.replace("}", "\\\\}")
|
||||||
|
text = text.replace("<SYNCPLAY_QUOTE>","\\\"")
|
||||||
return text
|
return text
|
||||||
|
|
||||||
def _quoteArg(self, arg):
|
def _quoteArg(self, arg):
|
||||||
@ -158,6 +177,8 @@ class MplayerPlayer(BasePlayer):
|
|||||||
def lineReceived(self, line):
|
def lineReceived(self, line):
|
||||||
if line:
|
if line:
|
||||||
self._client.ui.showDebugMessage("player << {}".format(line))
|
self._client.ui.showDebugMessage("player << {}".format(line))
|
||||||
|
line = line.replace("[cplayer] ", "") # -v workaround
|
||||||
|
line = line.replace("[term-msg] ", "") # -v workaround
|
||||||
line = line.replace(" cplayer: ","") # --msg-module workaround
|
line = line.replace(" cplayer: ","") # --msg-module workaround
|
||||||
line = line.replace(" term-msg: ", "")
|
line = line.replace(" term-msg: ", "")
|
||||||
if "Failed to get value of property" in line or "=(unavailable)" in line or line == "ANS_filename=" or line == "ANS_length=" or line == "ANS_path=":
|
if "Failed to get value of property" in line or "=(unavailable)" in line or line == "ANS_filename=" or line == "ANS_length=" or line == "ANS_path=":
|
||||||
@ -277,6 +298,9 @@ class MplayerPlayer(BasePlayer):
|
|||||||
self.lastSendTime = None
|
self.lastSendTime = None
|
||||||
self.lastNotReadyTime = None
|
self.lastNotReadyTime = None
|
||||||
self.__playerController = playerController
|
self.__playerController = playerController
|
||||||
|
if not self.__playerController._client._config["chatOutputEnabled"]:
|
||||||
|
self.__playerController.alertOSDSupported = False
|
||||||
|
self.__playerController.chatOSDSupported = False
|
||||||
if self.__playerController.getPlayerPathErrors(playerPath,filePath):
|
if self.__playerController.getPlayerPathErrors(playerPath,filePath):
|
||||||
raise ValueError()
|
raise ValueError()
|
||||||
if filePath and '://' not in filePath:
|
if filePath and '://' not in filePath:
|
||||||
@ -332,6 +356,18 @@ class MplayerPlayer(BasePlayer):
|
|||||||
self.__playerController.lineReceived(line)
|
self.__playerController.lineReceived(line)
|
||||||
self.__playerController.drop()
|
self.__playerController.drop()
|
||||||
|
|
||||||
|
def sendChat(self, message):
|
||||||
|
if message:
|
||||||
|
if message[:1] == "/" and message <> "/":
|
||||||
|
command = message[1:]
|
||||||
|
if command and command[:1] == "/":
|
||||||
|
message = message[1:]
|
||||||
|
else:
|
||||||
|
self.__playerController.reactor.callFromThread(self.__playerController._client.ui.executeCommand,
|
||||||
|
command)
|
||||||
|
return
|
||||||
|
self.__playerController.reactor.callFromThread(self.__playerController._client.sendChat, message)
|
||||||
|
|
||||||
def isReadyForSend(self):
|
def isReadyForSend(self):
|
||||||
self.checkForReadinessOverride()
|
self.checkForReadinessOverride()
|
||||||
return self.readyToSend
|
return self.readyToSend
|
||||||
|
|||||||
@ -1,14 +1,16 @@
|
|||||||
|
# coding:utf8
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
from syncplay.players.mplayer import MplayerPlayer
|
from syncplay.players.mplayer import MplayerPlayer
|
||||||
from syncplay.messages import getMessage
|
from syncplay.messages import getMessage
|
||||||
from syncplay import constants
|
from syncplay import constants
|
||||||
from syncplay.utils import isURL
|
from syncplay.utils import isURL, findResourcePath
|
||||||
import os, sys, time
|
import os, sys, time
|
||||||
|
|
||||||
class MpvPlayer(MplayerPlayer):
|
class MpvPlayer(MplayerPlayer):
|
||||||
RE_VERSION = re.compile('.*mpv (\d+)\.(\d+)\.\d+.*')
|
RE_VERSION = re.compile('.*mpv (\d+)\.(\d+)\.\d+.*')
|
||||||
osdMessageSeparator = "\\n"
|
osdMessageSeparator = "\\n"
|
||||||
|
osdMessageSeparator = "; " # TODO: Make conditional
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def run(client, playerPath, filePath, args):
|
def run(client, playerPath, filePath, args):
|
||||||
@ -17,6 +19,9 @@ class MpvPlayer(MplayerPlayer):
|
|||||||
except:
|
except:
|
||||||
ver = None
|
ver = None
|
||||||
constants.MPV_NEW_VERSION = ver is None or int(ver.group(1)) > 0 or int(ver.group(2)) >= 6
|
constants.MPV_NEW_VERSION = ver is None or int(ver.group(1)) > 0 or int(ver.group(2)) >= 6
|
||||||
|
constants.MPV_OSC_VISIBILITY_CHANGE_VERSION = False if ver is None else int(ver.group(1)) > 0 or int(ver.group(2)) >= 28
|
||||||
|
if not constants.MPV_OSC_VISIBILITY_CHANGE_VERSION:
|
||||||
|
client.ui.showDebugMessage(u"This version of mpv is not known to be compatible with changing the OSC visibility. Please use mpv >=0.28.0.")
|
||||||
if constants.MPV_NEW_VERSION:
|
if constants.MPV_NEW_VERSION:
|
||||||
return NewMpvPlayer(client, MpvPlayer.getExpandedPath(playerPath), filePath, args)
|
return NewMpvPlayer(client, MpvPlayer.getExpandedPath(playerPath), filePath, args)
|
||||||
else:
|
else:
|
||||||
@ -30,6 +35,7 @@ class MpvPlayer(MplayerPlayer):
|
|||||||
args.extend(constants.MPV_SLAVE_ARGS)
|
args.extend(constants.MPV_SLAVE_ARGS)
|
||||||
if constants.MPV_NEW_VERSION:
|
if constants.MPV_NEW_VERSION:
|
||||||
args.extend(constants.MPV_SLAVE_ARGS_NEW)
|
args.extend(constants.MPV_SLAVE_ARGS_NEW)
|
||||||
|
args.extend([u"--script={}".format(findResourcePath("syncplayintf.lua"))])
|
||||||
return args
|
return args
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -108,6 +114,25 @@ class OldMpvPlayer(MpvPlayer):
|
|||||||
class NewMpvPlayer(OldMpvPlayer):
|
class NewMpvPlayer(OldMpvPlayer):
|
||||||
lastResetTime = None
|
lastResetTime = None
|
||||||
lastMPVPositionUpdate = None
|
lastMPVPositionUpdate = None
|
||||||
|
alertOSDSupported = True
|
||||||
|
chatOSDSupported = True
|
||||||
|
|
||||||
|
def displayMessage(self, message, duration=(constants.OSD_DURATION * 1000), OSDType=constants.OSD_NOTIFICATION,
|
||||||
|
mood=constants.MESSAGE_NEUTRAL):
|
||||||
|
if not self._client._config["chatOutputEnabled"]:
|
||||||
|
super(self.__class__, self).displayMessage(message=message,duration=duration,OSDType=OSDType,mood=mood)
|
||||||
|
return
|
||||||
|
messageString = self._sanitizeText(message.replace("\\n", "<NEWLINE>")).replace("\\\\",constants.MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER).replace("<NEWLINE>", "\\n")
|
||||||
|
self._listener.sendLine(u'script-message-to syncplayintf {}-osd-{} "{}"'.format(OSDType, mood, messageString))
|
||||||
|
|
||||||
|
def displayChatMessage(self, username, message):
|
||||||
|
if not self._client._config["chatOutputEnabled"]:
|
||||||
|
super(self.__class__, self).displayChatMessage(username,message)
|
||||||
|
return
|
||||||
|
username = self._sanitizeText(username.replace("\\",constants.MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER))
|
||||||
|
message = self._sanitizeText(message.replace("\\",constants.MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER))
|
||||||
|
messageString = u"<{}> {}".format(username, message)
|
||||||
|
self._listener.sendLine(u'script-message-to syncplayintf chat "{}"'.format(messageString))
|
||||||
|
|
||||||
def setPaused(self, value):
|
def setPaused(self, value):
|
||||||
if self._paused == value:
|
if self._paused == value:
|
||||||
@ -199,6 +224,9 @@ class NewMpvPlayer(OldMpvPlayer):
|
|||||||
self._clearFileLoaded()
|
self._clearFileLoaded()
|
||||||
self._listener.sendLine(u'loadfile {}'.format(self._quoteArg(filePath)), notReadyAfterThis=True)
|
self._listener.sendLine(u'loadfile {}'.format(self._quoteArg(filePath)), notReadyAfterThis=True)
|
||||||
|
|
||||||
|
def setFeatures(self, featureList):
|
||||||
|
self.sendMpvOptions()
|
||||||
|
|
||||||
def setPosition(self, value):
|
def setPosition(self, value):
|
||||||
if value < constants.DO_NOT_RESET_POSITION_THRESHOLD and self._recentlyReset():
|
if value < constants.DO_NOT_RESET_POSITION_THRESHOLD and self._recentlyReset():
|
||||||
self._client.ui.showDebugMessage("Did not seek as recently reset and {} below 'do not reset position' threshold".format(value))
|
self._client.ui.showDebugMessage("Did not seek as recently reset and {} below 'do not reset position' threshold".format(value))
|
||||||
@ -222,9 +250,29 @@ class NewMpvPlayer(OldMpvPlayer):
|
|||||||
else:
|
else:
|
||||||
self._storePosition(0)
|
self._storePosition(0)
|
||||||
|
|
||||||
|
def sendMpvOptions(self):
|
||||||
|
options = []
|
||||||
|
for option in constants.MPV_SYNCPLAYINTF_OPTIONS_TO_SEND:
|
||||||
|
options.append(u"{}={}".format(option, self._client._config[option]))
|
||||||
|
for option in constants.MPV_SYNCPLAYINTF_CONSTANTS_TO_SEND:
|
||||||
|
options.append(option)
|
||||||
|
for option in constants.MPV_SYNCPLAYINTF_LANGUAGE_TO_SEND:
|
||||||
|
options.append(u"{}={}".format(option, getMessage(option)))
|
||||||
|
options.append(u"OscVisibilityChangeCompatible={}".format(constants.MPV_OSC_VISIBILITY_CHANGE_VERSION))
|
||||||
|
options_string = ", ".join(options)
|
||||||
|
self._listener.sendLine(u'script-message-to syncplayintf set_syncplayintf_options "{}"'.format(options_string))
|
||||||
|
self._setOSDPosition()
|
||||||
|
|
||||||
def _handleUnknownLine(self, line):
|
def _handleUnknownLine(self, line):
|
||||||
self.mpvErrorCheck(line)
|
self.mpvErrorCheck(line)
|
||||||
|
|
||||||
|
if "<chat>" in line:
|
||||||
|
line = line.decode("utf-8").replace(constants.MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER, "\\").encode("utf-8")
|
||||||
|
self._listener.sendChat(line[6:-7])
|
||||||
|
|
||||||
|
if "<get_syncplayintf_options>" in line:
|
||||||
|
self.sendMpvOptions()
|
||||||
|
|
||||||
if line == "<SyncplayUpdateFile>" or "Playing:" in line:
|
if line == "<SyncplayUpdateFile>" or "Playing:" in line:
|
||||||
self._listener.setReadyToSend(False)
|
self._listener.setReadyToSend(False)
|
||||||
self._clearFileLoaded()
|
self._clearFileLoaded()
|
||||||
@ -236,6 +284,11 @@ class NewMpvPlayer(OldMpvPlayer):
|
|||||||
elif "Failed" in line or "failed" in line or "No video or audio streams selected" in line or "error" in line:
|
elif "Failed" in line or "failed" in line or "No video or audio streams selected" in line or "error" in line:
|
||||||
self._listener.setReadyToSend(True)
|
self._listener.setReadyToSend(True)
|
||||||
|
|
||||||
|
def _setOSDPosition(self):
|
||||||
|
if self._client._config['chatMoveOSD'] and (self._client._config['chatOutputEnabled'] or (self._client._config['chatInputEnabled'] and self._client._config['chatInputPosition'] == constants.INPUT_POSITION_TOP)):
|
||||||
|
self._setProperty("osd-align-y", "bottom")
|
||||||
|
self._setProperty("osd-margin-y", int(self._client._config['chatOSDMargin']))
|
||||||
|
|
||||||
def _recentlyReset(self):
|
def _recentlyReset(self):
|
||||||
if not self.lastResetTime:
|
if not self.lastResetTime:
|
||||||
return False
|
return False
|
||||||
|
|||||||
@ -16,7 +16,8 @@ from syncplay.utils import isBSD, isLinux, isWindows, isMacOS
|
|||||||
class VlcPlayer(BasePlayer):
|
class VlcPlayer(BasePlayer):
|
||||||
speedSupported = True
|
speedSupported = True
|
||||||
customOpenDialog = False
|
customOpenDialog = False
|
||||||
secondaryOSDSupported = True
|
chatOSDSupported = False
|
||||||
|
alertOSDSupported = True
|
||||||
osdMessageSeparator = "; "
|
osdMessageSeparator = "; "
|
||||||
|
|
||||||
RE_ANSWER = re.compile(constants.VLC_ANSWER_REGEX)
|
RE_ANSWER = re.compile(constants.VLC_ANSWER_REGEX)
|
||||||
@ -118,9 +119,9 @@ class VlcPlayer(BasePlayer):
|
|||||||
else:
|
else:
|
||||||
return self._position
|
return self._position
|
||||||
|
|
||||||
def displayMessage(self, message, duration=constants.OSD_DURATION * 1000, secondaryOSD=False):
|
def displayMessage(self, message, duration=constants.OSD_DURATION * 1000, OSDType=constants.OSD_DURATION, mood=constants.MESSAGE_NEUTRAL):
|
||||||
duration /= 1000
|
duration /= 1000
|
||||||
if secondaryOSD == False:
|
if OSDType != constants.OSD_ALERT:
|
||||||
self._listener.sendLine('display-osd: {}, {}, {}'.format('top-right', duration, message.encode('utf8')))
|
self._listener.sendLine('display-osd: {}, {}, {}'.format('top-right', duration, message.encode('utf8')))
|
||||||
else:
|
else:
|
||||||
self._listener.sendLine('display-secondary-osd: {}, {}, {}'.format('center', duration, message.encode('utf8')))
|
self._listener.sendLine('display-secondary-osd: {}, {}, {}'.format('center', duration, message.encode('utf8')))
|
||||||
@ -128,6 +129,9 @@ class VlcPlayer(BasePlayer):
|
|||||||
def setSpeed(self, value):
|
def setSpeed(self, value):
|
||||||
self._listener.sendLine("set-rate: {:.2n}".format(value))
|
self._listener.sendLine("set-rate: {:.2n}".format(value))
|
||||||
|
|
||||||
|
def setFeatures(self, featureList):
|
||||||
|
pass
|
||||||
|
|
||||||
def setPosition(self, value):
|
def setPosition(self, value):
|
||||||
self._lastVLCPositionUpdate = time.time()
|
self._lastVLCPositionUpdate = time.time()
|
||||||
self._listener.sendLine("set-position: {}".format(value).replace(".",self.radixChar))
|
self._listener.sendLine("set-position: {}".format(value).replace(".",self.radixChar))
|
||||||
|
|||||||
@ -5,8 +5,8 @@ import syncplay
|
|||||||
from functools import wraps
|
from functools import wraps
|
||||||
import time
|
import time
|
||||||
from syncplay.messages import getMessage
|
from syncplay.messages import getMessage
|
||||||
from syncplay.constants import PING_MOVING_AVERAGE_WEIGHT
|
from syncplay.constants import PING_MOVING_AVERAGE_WEIGHT, CONTROLLED_ROOMS_MIN_VERSION, USER_READY_MIN_VERSION, SHARED_PLAYLIST_MIN_VERSION, CHAT_MIN_VERSION
|
||||||
|
from syncplay.utils import meetsMinVersion
|
||||||
|
|
||||||
class JSONCommandProtocol(LineReceiver):
|
class JSONCommandProtocol(LineReceiver):
|
||||||
def handleMessages(self, messages):
|
def handleMessages(self, messages):
|
||||||
@ -107,6 +107,7 @@ class SyncClientProtocol(JSONCommandProtocol):
|
|||||||
if room: hello["room"] = {"name" :room}
|
if room: hello["room"] = {"name" :room}
|
||||||
hello["version"] = "1.2.255" # Used so newer clients work on 1.2.X server
|
hello["version"] = "1.2.255" # Used so newer clients work on 1.2.X server
|
||||||
hello["realversion"] = syncplay.version
|
hello["realversion"] = syncplay.version
|
||||||
|
hello["features"] = self._client.getFeatures()
|
||||||
self.sendMessage({"Hello": hello})
|
self.sendMessage({"Hello": hello})
|
||||||
|
|
||||||
def _SetUser(self, users):
|
def _SetUser(self, users):
|
||||||
@ -147,6 +148,11 @@ class SyncClientProtocol(JSONCommandProtocol):
|
|||||||
self._client.playlist.changeToPlaylistIndex(values['index'], values['user'])
|
self._client.playlist.changeToPlaylistIndex(values['index'], values['user'])
|
||||||
elif command == "playlistChange":
|
elif command == "playlistChange":
|
||||||
self._client.playlist.changePlaylist(values['files'], values['user'])
|
self._client.playlist.changePlaylist(values['files'], values['user'])
|
||||||
|
elif command == "features":
|
||||||
|
self._client.setUserFeatures(values["username"],values['features'])
|
||||||
|
|
||||||
|
def sendFeaturesUpdate(self, features):
|
||||||
|
self.sendSet({"features": features})
|
||||||
|
|
||||||
def sendSet(self, setting):
|
def sendSet(self, setting):
|
||||||
self.sendMessage({"Set": setting})
|
self.sendMessage({"Set": setting})
|
||||||
@ -173,7 +179,8 @@ class SyncClientProtocol(JSONCommandProtocol):
|
|||||||
file_ = user[1]['file'] if user[1]['file'] <> {} else None
|
file_ = user[1]['file'] if user[1]['file'] <> {} else None
|
||||||
isController = user[1]['controller'] if 'controller' in user[1] else False
|
isController = user[1]['controller'] if 'controller' in user[1] else False
|
||||||
isReady = user[1]['isReady'] if 'isReady' in user[1] else None
|
isReady = user[1]['isReady'] if 'isReady' in user[1] else None
|
||||||
self._client.userlist.addUser(userName, roomName, file_, noMessage=True, isController=isController, isReady=isReady)
|
features = user[1]['features'] if 'features' in user[1] else None
|
||||||
|
self._client.userlist.addUser(userName, roomName, file_, noMessage=True, isController=isController, isReady=isReady, features=features)
|
||||||
self._client.userlist.showUserList()
|
self._client.userlist.showUserList()
|
||||||
|
|
||||||
def sendList(self):
|
def sendList(self):
|
||||||
@ -249,9 +256,9 @@ class SyncClientProtocol(JSONCommandProtocol):
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
def handleChat(self,message):
|
def handleChat(self,message):
|
||||||
|
username = message['username']
|
||||||
userMessage = message['message']
|
userMessage = message['message']
|
||||||
messageString = u"<{}> {}".format(message['username'], userMessage)
|
self._client.ui.showChatMessage(username, userMessage)
|
||||||
self._client.ui.showMessage(messageString)
|
|
||||||
|
|
||||||
def setReady(self, isReady, manuallyInitiated=True):
|
def setReady(self, isReady, manuallyInitiated=True):
|
||||||
self.sendSet({
|
self.sendSet({
|
||||||
@ -286,6 +293,7 @@ class SyncServerProtocol(JSONCommandProtocol):
|
|||||||
def __init__(self, factory):
|
def __init__(self, factory):
|
||||||
self._factory = factory
|
self._factory = factory
|
||||||
self._version = None
|
self._version = None
|
||||||
|
self._features = None
|
||||||
self._logged = False
|
self._logged = False
|
||||||
self.clientIgnoringOnTheFly = 0
|
self.clientIgnoringOnTheFly = 0
|
||||||
self.serverIgnoringOnTheFly = 0
|
self.serverIgnoringOnTheFly = 0
|
||||||
@ -319,6 +327,16 @@ class SyncServerProtocol(JSONCommandProtocol):
|
|||||||
def connectionLost(self, reason):
|
def connectionLost(self, reason):
|
||||||
self._factory.removeWatcher(self._watcher)
|
self._factory.removeWatcher(self._watcher)
|
||||||
|
|
||||||
|
def getFeatures(self):
|
||||||
|
if not self._features:
|
||||||
|
self._features = {}
|
||||||
|
self._features["sharedPlaylists"] = meetsMinVersion(self._version, SHARED_PLAYLIST_MIN_VERSION)
|
||||||
|
self._features["chat"] = meetsMinVersion(self._version, CHAT_MIN_VERSION)
|
||||||
|
self._features["featureList"] = False
|
||||||
|
self._features["readiness"] = meetsMinVersion(self._version, USER_READY_MIN_VERSION)
|
||||||
|
self._features["managedRooms"] = meetsMinVersion(self._version, CONTROLLED_ROOMS_MIN_VERSION)
|
||||||
|
return self._features
|
||||||
|
|
||||||
def isLogged(self):
|
def isLogged(self):
|
||||||
return self._logged
|
return self._logged
|
||||||
|
|
||||||
@ -345,7 +363,8 @@ class SyncServerProtocol(JSONCommandProtocol):
|
|||||||
roomName = None
|
roomName = None
|
||||||
version = hello["version"] if hello.has_key("version") else None
|
version = hello["version"] if hello.has_key("version") else None
|
||||||
version = hello["realversion"] if hello.has_key("realversion") else version
|
version = hello["realversion"] if hello.has_key("realversion") else version
|
||||||
return username, serverPassword, roomName, version
|
features = hello["features"] if hello.has_key("features") else None
|
||||||
|
return username, serverPassword, roomName, version, features
|
||||||
|
|
||||||
def _checkPassword(self, serverPassword):
|
def _checkPassword(self, serverPassword):
|
||||||
if self._factory.password:
|
if self._factory.password:
|
||||||
@ -358,7 +377,7 @@ class SyncServerProtocol(JSONCommandProtocol):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def handleHello(self, hello):
|
def handleHello(self, hello):
|
||||||
username, serverPassword, roomName, version = self._extractHelloArguments(hello)
|
username, serverPassword, roomName, version, features = self._extractHelloArguments(hello)
|
||||||
if not username or not roomName or not version:
|
if not username or not roomName or not version:
|
||||||
self.dropWithError(getMessage("hello-server-error"))
|
self.dropWithError(getMessage("hello-server-error"))
|
||||||
return
|
return
|
||||||
@ -366,14 +385,22 @@ class SyncServerProtocol(JSONCommandProtocol):
|
|||||||
if not self._checkPassword(serverPassword):
|
if not self._checkPassword(serverPassword):
|
||||||
return
|
return
|
||||||
self._version = version
|
self._version = version
|
||||||
|
self.setFeatures(features)
|
||||||
self._factory.addWatcher(self, username, roomName)
|
self._factory.addWatcher(self, username, roomName)
|
||||||
self._logged = True
|
self._logged = True
|
||||||
self.sendHello(version)
|
self.sendHello(version)
|
||||||
|
|
||||||
|
@requireLogged
|
||||||
def handleChat(self,chatMessage):
|
def handleChat(self,chatMessage):
|
||||||
if not self._factory.disableChat:
|
if not self._factory.disableChat:
|
||||||
self._factory.sendChat(self._watcher,chatMessage)
|
self._factory.sendChat(self._watcher,chatMessage)
|
||||||
|
|
||||||
|
def setFeatures(self, features):
|
||||||
|
self._features = features
|
||||||
|
|
||||||
|
def sendFeaturesUpdate(self):
|
||||||
|
self.sendSet({"features": self.getFeatures()})
|
||||||
|
|
||||||
def setWatcher(self, watcher):
|
def setWatcher(self, watcher):
|
||||||
self._watcher = watcher
|
self._watcher = watcher
|
||||||
|
|
||||||
@ -410,6 +437,9 @@ class SyncServerProtocol(JSONCommandProtocol):
|
|||||||
self._factory.setPlaylist(self._watcher, set_[1]['files'])
|
self._factory.setPlaylist(self._watcher, set_[1]['files'])
|
||||||
elif command == "playlistIndex":
|
elif command == "playlistIndex":
|
||||||
self._factory.setPlaylistIndex(self._watcher, set_[1]['index'])
|
self._factory.setPlaylistIndex(self._watcher, set_[1]['index'])
|
||||||
|
elif command == "features":
|
||||||
|
#TODO: Check
|
||||||
|
self._watcher.setFeatures(set_[1])
|
||||||
|
|
||||||
def sendSet(self, setting):
|
def sendSet(self, setting):
|
||||||
self.sendMessage({"Set": setting})
|
self.sendMessage({"Set": setting})
|
||||||
@ -476,7 +506,8 @@ class SyncServerProtocol(JSONCommandProtocol):
|
|||||||
"position": 0,
|
"position": 0,
|
||||||
"file": watcher.getFile() if watcher.getFile() else {},
|
"file": watcher.getFile() if watcher.getFile() else {},
|
||||||
"controller": watcher.isController(),
|
"controller": watcher.isController(),
|
||||||
"isReady": watcher.isReady()
|
"isReady": watcher.isReady(),
|
||||||
|
"features": watcher.getFeatures()
|
||||||
}
|
}
|
||||||
userlist[room.getName()][watcher.getName()] = userFile
|
userlist[room.getName()][watcher.getName()] = userFile
|
||||||
|
|
||||||
|
|||||||
@ -48,6 +48,11 @@ class SyncFactory(Factory):
|
|||||||
features["readiness"] = not self.disableReady
|
features["readiness"] = not self.disableReady
|
||||||
features["managedRooms"] = True
|
features["managedRooms"] = True
|
||||||
features["chat"] = not self.disableChat
|
features["chat"] = not self.disableChat
|
||||||
|
features["maxChatMessageLength"] = constants.MAX_CHAT_MESSAGE_LENGTH
|
||||||
|
features["maxUsernameLength"] = constants.MAX_USERNAME_LENGTH
|
||||||
|
features["maxRoomNameLength"] = constants.MAX_ROOM_NAME_LENGTH
|
||||||
|
features["maxFilenameLength"] = constants.MAX_FILENAME_LENGTH
|
||||||
|
|
||||||
return features
|
return features
|
||||||
|
|
||||||
def getMotd(self, userIp, username, room, clientVersion):
|
def getMotd(self, userIp, username, room, clientVersion):
|
||||||
@ -108,7 +113,7 @@ class SyncFactory(Factory):
|
|||||||
self._roomManager.broadcast(watcher, l)
|
self._roomManager.broadcast(watcher, l)
|
||||||
|
|
||||||
def sendJoinMessage(self, watcher):
|
def sendJoinMessage(self, watcher):
|
||||||
l = lambda w: w.sendSetting(watcher.getName(), watcher.getRoom(), None, {"joined": True, "version": watcher.getVersion()}) if w != watcher else None
|
l = lambda w: w.sendSetting(watcher.getName(), watcher.getRoom(), None, {"joined": True, "version": watcher.getVersion(), "features": watcher.getFeatures()}) if w != watcher else None
|
||||||
self._roomManager.broadcast(watcher, l)
|
self._roomManager.broadcast(watcher, l)
|
||||||
self._roomManager.broadcastRoom(watcher, lambda w: w.sendSetReady(watcher.getName(), watcher.isReady(), False))
|
self._roomManager.broadcastRoom(watcher, lambda w: w.sendSetReady(watcher.getName(), watcher.isReady(), False))
|
||||||
|
|
||||||
@ -413,6 +418,10 @@ class Watcher(object):
|
|||||||
def setReady(self, ready):
|
def setReady(self, ready):
|
||||||
self._ready = ready
|
self._ready = ready
|
||||||
|
|
||||||
|
def getFeatures(self):
|
||||||
|
features = self._connector.getFeatures()
|
||||||
|
return features
|
||||||
|
|
||||||
def isReady(self):
|
def isReady(self):
|
||||||
if self._server.disableReady:
|
if self._server.disableReady:
|
||||||
return None
|
return None
|
||||||
|
|||||||
@ -8,6 +8,7 @@ from syncplay.messages import getMessage, setLanguage, isValidLanguage
|
|||||||
from syncplay.players.playerFactory import PlayerFactory
|
from syncplay.players.playerFactory import PlayerFactory
|
||||||
from syncplay.utils import isMacOS
|
from syncplay.utils import isMacOS
|
||||||
import codecs
|
import codecs
|
||||||
|
import re
|
||||||
|
|
||||||
class InvalidConfigValue(Exception):
|
class InvalidConfigValue(Exception):
|
||||||
def __init__(self, message):
|
def __init__(self, message):
|
||||||
@ -64,6 +65,29 @@ class ConfigurationGetter(object):
|
|||||||
"showNonControllerOSD" : False,
|
"showNonControllerOSD" : False,
|
||||||
"showContactInfo" : True,
|
"showContactInfo" : True,
|
||||||
"showDurationNotification" : True,
|
"showDurationNotification" : True,
|
||||||
|
"chatInputEnabled" : True,
|
||||||
|
"chatInputFontFamily" : 'sans-serif',
|
||||||
|
"chatInputRelativeFontSize" : constants.DEFAULT_CHAT_FONT_SIZE,
|
||||||
|
"chatInputFontWeight" : constants.DEFAULT_CHAT_FONT_WEIGHT,
|
||||||
|
"chatInputFontUnderline": False,
|
||||||
|
"chatInputFontColor": constants.DEFAULT_CHAT_INPUT_FONT_COLOR,
|
||||||
|
"chatInputPosition": constants.INPUT_POSITION_TOP,
|
||||||
|
"chatDirectInput": False,
|
||||||
|
"chatOutputEnabled": True,
|
||||||
|
"chatOutputFontFamily": 'sans-serif',
|
||||||
|
"chatOutputRelativeFontSize": constants.DEFAULT_CHAT_FONT_SIZE,
|
||||||
|
"chatOutputFontWeight": constants.DEFAULT_CHAT_FONT_WEIGHT,
|
||||||
|
"chatOutputFontUnderline": False,
|
||||||
|
"chatOutputMode": constants.CHATROOM_MODE,
|
||||||
|
"chatMaxLines": 7,
|
||||||
|
"chatTopMargin": 25,
|
||||||
|
"chatLeftMargin": 20,
|
||||||
|
"chatBottomMargin": 30,
|
||||||
|
"chatMoveOSD": True,
|
||||||
|
"chatOSDMargin": 110,
|
||||||
|
"notificationTimeout": 3,
|
||||||
|
"alertTimeout": 5,
|
||||||
|
"chatTimeout": 7,
|
||||||
"publicServers" : []
|
"publicServers" : []
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +130,13 @@ class ConfigurationGetter(object):
|
|||||||
"sharedPlaylistEnabled",
|
"sharedPlaylistEnabled",
|
||||||
"loopAtEndOfPlaylist",
|
"loopAtEndOfPlaylist",
|
||||||
"loopSingleFiles",
|
"loopSingleFiles",
|
||||||
"onlySwitchToTrustedDomains"
|
"onlySwitchToTrustedDomains",
|
||||||
|
"chatInputEnabled",
|
||||||
|
"chatInputFontUnderline",
|
||||||
|
"chatDirectInput",
|
||||||
|
"chatMoveOSD",
|
||||||
|
"chatOutputEnabled",
|
||||||
|
"chatOutputFontUnderline"
|
||||||
]
|
]
|
||||||
self._tristate = [
|
self._tristate = [
|
||||||
"checkForUpdatesAutomatically",
|
"checkForUpdatesAutomatically",
|
||||||
@ -125,6 +155,22 @@ class ConfigurationGetter(object):
|
|||||||
"rewindThreshold",
|
"rewindThreshold",
|
||||||
"fastforwardThreshold",
|
"fastforwardThreshold",
|
||||||
"autoplayMinUsers",
|
"autoplayMinUsers",
|
||||||
|
"chatInputRelativeFontSize",
|
||||||
|
"chatInputFontWeight",
|
||||||
|
"chatOutputFontWeight",
|
||||||
|
"chatOutputRelativeFontSize",
|
||||||
|
"chatMaxLines",
|
||||||
|
"chatTopMargin",
|
||||||
|
"chatLeftMargin",
|
||||||
|
"chatBottomMargin",
|
||||||
|
"chatOSDMargin",
|
||||||
|
"notificationTimeout",
|
||||||
|
"alertTimeout",
|
||||||
|
"chatTimeout"
|
||||||
|
]
|
||||||
|
|
||||||
|
self._hexadecimal = [
|
||||||
|
"chatInputFontColor"
|
||||||
]
|
]
|
||||||
|
|
||||||
self._iniStructure = {
|
self._iniStructure = {
|
||||||
@ -143,7 +189,19 @@ class ConfigurationGetter(object):
|
|||||||
"onlySwitchToTrustedDomains", "trustedDomains","publicServers"],
|
"onlySwitchToTrustedDomains", "trustedDomains","publicServers"],
|
||||||
"gui": ["showOSD", "showOSDWarnings", "showSlowdownOSD",
|
"gui": ["showOSD", "showOSDWarnings", "showSlowdownOSD",
|
||||||
"showDifferentRoomOSD", "showSameRoomOSD",
|
"showDifferentRoomOSD", "showSameRoomOSD",
|
||||||
"showNonControllerOSD", "showDurationNotification"],
|
"showNonControllerOSD", "showDurationNotification",
|
||||||
|
"chatInputEnabled","chatInputFontUnderline",
|
||||||
|
"chatInputFontFamily", "chatInputRelativeFontSize",
|
||||||
|
"chatInputFontWeight", "chatInputFontColor",
|
||||||
|
"chatInputPosition","chatDirectInput",
|
||||||
|
"chatOutputFontFamily", "chatOutputRelativeFontSize",
|
||||||
|
"chatOutputFontWeight", "chatOutputFontUnderline",
|
||||||
|
"chatOutputMode", "chatMaxLines",
|
||||||
|
"chatTopMargin", "chatLeftMargin",
|
||||||
|
"chatBottomMargin", "chatDirectInput",
|
||||||
|
"chatMoveOSD", "chatOSDMargin",
|
||||||
|
"notificationTimeout", "alertTimeout",
|
||||||
|
"chatTimeout","chatOutputEnabled"],
|
||||||
"general": ["language", "checkForUpdatesAutomatically",
|
"general": ["language", "checkForUpdatesAutomatically",
|
||||||
"lastCheckedForUpdates"]
|
"lastCheckedForUpdates"]
|
||||||
}
|
}
|
||||||
@ -197,6 +255,11 @@ class ConfigurationGetter(object):
|
|||||||
for key in self._numeric:
|
for key in self._numeric:
|
||||||
self._config[key] = float(self._config[key])
|
self._config[key] = float(self._config[key])
|
||||||
|
|
||||||
|
for key in self._hexadecimal:
|
||||||
|
match = re.search(r'^#(?:[0-9a-fA-F]){6}$', self._config[key])
|
||||||
|
if not match:
|
||||||
|
self._config[key] = u"#FFFFFF"
|
||||||
|
|
||||||
for key in self._required:
|
for key in self._required:
|
||||||
if key == "playerPath":
|
if key == "playerPath":
|
||||||
player = None
|
player = None
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import sys
|
|||||||
import threading
|
import threading
|
||||||
from syncplay.messages import getMessage, getLanguages, setLanguage, getInitialLanguage
|
from syncplay.messages import getMessage, getLanguages, setLanguage, getInitialLanguage
|
||||||
from syncplay import constants
|
from syncplay import constants
|
||||||
from syncplay.utils import isBSD, isLinux, isMacOS
|
from syncplay.utils import isBSD, isLinux, isMacOS, isWindows
|
||||||
from syncplay.utils import resourcespath, posixresourcespath
|
from syncplay.utils import resourcespath, posixresourcespath
|
||||||
class GuiConfiguration:
|
class GuiConfiguration:
|
||||||
def __init__(self, config, error=None, defaultConfig=None):
|
def __init__(self, config, error=None, defaultConfig=None):
|
||||||
@ -289,13 +289,16 @@ class ConfigDialog(QtWidgets.QDialog):
|
|||||||
def loadLastUpdateCheckDate(self):
|
def loadLastUpdateCheckDate(self):
|
||||||
settings = QSettings("Syncplay", "Interface")
|
settings = QSettings("Syncplay", "Interface")
|
||||||
settings.beginGroup("Update")
|
settings.beginGroup("Update")
|
||||||
self.lastCheckedForUpdates = settings.value("lastCheckedQt", None)
|
try:
|
||||||
if self.lastCheckedForUpdates:
|
self.lastCheckedForUpdates = settings.value("lastCheckedQt", None)
|
||||||
if self.config["lastCheckedForUpdates"] is not None and self.config["lastCheckedForUpdates"] is not "":
|
if self.lastCheckedForUpdates:
|
||||||
if self.lastCheckedForUpdates.toPython() > datetime.strptime(self.config["lastCheckedForUpdates"], "%Y-%m-%d %H:%M:%S.%f"):
|
if self.config["lastCheckedForUpdates"] is not None and self.config["lastCheckedForUpdates"] is not "":
|
||||||
|
if self.lastCheckedForUpdates.toPython() > datetime.strptime(self.config["lastCheckedForUpdates"], "%Y-%m-%d %H:%M:%S.%f"):
|
||||||
|
self.config["lastCheckedForUpdates"] = self.lastCheckedForUpdates.toString("yyyy-MM-d HH:mm:ss.z")
|
||||||
|
else:
|
||||||
self.config["lastCheckedForUpdates"] = self.lastCheckedForUpdates.toString("yyyy-MM-d HH:mm:ss.z")
|
self.config["lastCheckedForUpdates"] = self.lastCheckedForUpdates.toString("yyyy-MM-d HH:mm:ss.z")
|
||||||
else:
|
except:
|
||||||
self.config["lastCheckedForUpdates"] = self.lastCheckedForUpdates.toString("yyyy-MM-d HH:mm:ss.z")
|
self.lastCheckedForUpdates = None
|
||||||
|
|
||||||
def loadSavedPublicServerList(self):
|
def loadSavedPublicServerList(self):
|
||||||
settings = QSettings("Syncplay", "Interface")
|
settings = QSettings("Syncplay", "Interface")
|
||||||
@ -515,7 +518,7 @@ class ConfigDialog(QtWidgets.QDialog):
|
|||||||
|
|
||||||
def connectChildren(self, widget):
|
def connectChildren(self, widget):
|
||||||
widgetName = str(widget.objectName())
|
widgetName = str(widget.objectName())
|
||||||
if self.subitems.has_key(widgetName) and isinstance(widget, QCheckBox):
|
if self.subitems.has_key(widgetName):
|
||||||
widget.stateChanged.connect(lambda: self.updateSubwidgets(self, widget))
|
widget.stateChanged.connect(lambda: self.updateSubwidgets(self, widget))
|
||||||
self.updateSubwidgets(self, widget)
|
self.updateSubwidgets(self, widget)
|
||||||
|
|
||||||
@ -869,6 +872,155 @@ class ConfigDialog(QtWidgets.QDialog):
|
|||||||
self.syncSettingsLayout.setAlignment(Qt.AlignTop)
|
self.syncSettingsLayout.setAlignment(Qt.AlignTop)
|
||||||
self.stackedLayout.addWidget(self.syncSettingsFrame)
|
self.stackedLayout.addWidget(self.syncSettingsFrame)
|
||||||
|
|
||||||
|
def addChatTab(self):
|
||||||
|
self.chatFrame = QtWidgets.QFrame()
|
||||||
|
self.chatLayout = QtWidgets.QVBoxLayout()
|
||||||
|
self.chatLayout.setAlignment(Qt.AlignTop)
|
||||||
|
|
||||||
|
# Input
|
||||||
|
self.chatInputGroup = QtWidgets.QGroupBox(getMessage("chat-title"))
|
||||||
|
self.chatInputLayout = QtWidgets.QGridLayout()
|
||||||
|
self.chatLayout.addWidget(self.chatInputGroup)
|
||||||
|
self.chatInputGroup.setLayout(self.chatInputLayout)
|
||||||
|
self.chatInputEnabledCheckbox = QCheckBox(getMessage("chatinputenabled-label"))
|
||||||
|
self.chatInputEnabledCheckbox.setObjectName("chatInputEnabled")
|
||||||
|
self.chatInputLayout.addWidget(self.chatInputEnabledCheckbox, 1, 0, 1,1, Qt.AlignLeft)
|
||||||
|
|
||||||
|
self.chatDirectInputCheckbox = QCheckBox(getMessage("chatdirectinput-label"))
|
||||||
|
self.chatDirectInputCheckbox.setObjectName("chatDirectInput")
|
||||||
|
self.chatDirectInputCheckbox.setStyleSheet(
|
||||||
|
constants.STYLE_SUBCHECKBOX.format(self.posixresourcespath + u"chevrons_right.png"))
|
||||||
|
self.chatInputLayout.addWidget(self.chatDirectInputCheckbox, 2, 0, 1,1, Qt.AlignLeft)
|
||||||
|
|
||||||
|
self.inputFontLayout = QtWidgets.QHBoxLayout()
|
||||||
|
self.inputFontLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.inputFontFrame = QtWidgets.QFrame()
|
||||||
|
self.inputFontFrame.setLayout(self.inputFontLayout)
|
||||||
|
self.inputFontFrame.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
|
||||||
|
self.chatFontLabel = QLabel(getMessage("chatinputfont-label"), self)
|
||||||
|
self.chatFontLabel.setStyleSheet(constants.STYLE_SUBCHECKBOX.format(self.posixresourcespath + u"chevrons_right.png"))
|
||||||
|
self.chatFontLabel.setObjectName("font-label")
|
||||||
|
self.chatInputFontButton = QtWidgets.QPushButton(getMessage("chatfont-label"))
|
||||||
|
self.chatInputFontButton.setObjectName("set-input-font")
|
||||||
|
self.chatInputFontButtonGroup = QtWidgets.QButtonGroup()
|
||||||
|
self.chatInputFontButtonGroup.addButton(self.chatInputFontButton)
|
||||||
|
self.chatInputFontButton.released.connect(lambda: self.fontDialog("chatInput"))
|
||||||
|
self.chatInputColourButton = QtWidgets.QPushButton(getMessage("chatcolour-label"))
|
||||||
|
self.chatInputColourButton.setObjectName("set-input-colour")
|
||||||
|
self.chatInputColourButtonGroup = QtWidgets.QButtonGroup()
|
||||||
|
self.chatInputColourButtonGroup.addButton(self.chatInputColourButton)
|
||||||
|
self.chatInputColourButton.released.connect(lambda: self.colourDialog("chatInput"))
|
||||||
|
self.inputFontLayout.addWidget(self.chatFontLabel, Qt.AlignLeft)
|
||||||
|
self.inputFontLayout.addWidget(self.chatInputFontButton, Qt.AlignLeft)
|
||||||
|
self.inputFontLayout.addWidget(self.chatInputColourButton, Qt.AlignLeft)
|
||||||
|
self.chatInputLayout.addWidget(self.inputFontFrame, 3, 0, 1, 3, Qt.AlignLeft)
|
||||||
|
|
||||||
|
self.chatInputPositionFrame = QtWidgets.QFrame()
|
||||||
|
self.chatInputPositionLayout = QtWidgets.QHBoxLayout()
|
||||||
|
self.chatInputPositionLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.chatInputPositionFrame.setLayout(self.chatInputPositionLayout)
|
||||||
|
self.chatInputPositionFrame.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
|
||||||
|
self.chatInputPositionLabel = QLabel(getMessage("chatinputposition-label"), self)
|
||||||
|
self.chatInputPositionLabel.setStyleSheet(constants.STYLE_SUBCHECKBOX.format(self.posixresourcespath + u"chevrons_right.png"))
|
||||||
|
self.chatInputPositionGroup = QButtonGroup()
|
||||||
|
self.chatInputTopOption = QRadioButton(getMessage("chat-top-option"))
|
||||||
|
self.chatInputMiddleOption = QRadioButton(getMessage("chat-middle-option"))
|
||||||
|
self.chatInputBottomOption = QRadioButton(getMessage("chat-bottom-option"))
|
||||||
|
self.chatInputPositionGroup.addButton(self.chatInputTopOption)
|
||||||
|
self.chatInputPositionGroup.addButton(self.chatInputMiddleOption)
|
||||||
|
self.chatInputPositionGroup.addButton(self.chatInputBottomOption)
|
||||||
|
|
||||||
|
self.chatInputPositionLabel.setObjectName("chatinputposition")
|
||||||
|
self.chatInputTopOption.setObjectName("chatinputposition-top" + constants.CONFIG_NAME_MARKER + "chatInputPosition" + constants.CONFIG_VALUE_MARKER + constants.INPUT_POSITION_TOP)
|
||||||
|
self.chatInputMiddleOption.setObjectName("chatinputposition-middle" + constants.CONFIG_NAME_MARKER + "chatInputPosition" + constants.CONFIG_VALUE_MARKER + constants.INPUT_POSITION_MIDDLE)
|
||||||
|
self.chatInputBottomOption.setObjectName("chatinputposition-bottom" + constants.CONFIG_NAME_MARKER + "chatInputPosition" + constants.CONFIG_VALUE_MARKER + constants.INPUT_POSITION_BOTTOM)
|
||||||
|
|
||||||
|
self.chatInputPositionLayout.addWidget(self.chatInputPositionLabel)
|
||||||
|
self.chatInputPositionLayout.addWidget(self.chatInputTopOption)
|
||||||
|
self.chatInputPositionLayout.addWidget(self.chatInputMiddleOption)
|
||||||
|
self.chatInputPositionLayout.addWidget(self.chatInputBottomOption)
|
||||||
|
self.chatInputLayout.addWidget(self.chatInputPositionFrame)
|
||||||
|
|
||||||
|
self.subitems['chatInputEnabled'] = [self.chatInputPositionLabel.objectName(), self.chatInputTopOption.objectName(),
|
||||||
|
self.chatInputMiddleOption.objectName(), self.chatInputBottomOption.objectName(),
|
||||||
|
self.chatInputFontButton.objectName(), self.chatFontLabel.objectName(),
|
||||||
|
self.chatInputColourButton.objectName(), self.chatDirectInputCheckbox.objectName()]
|
||||||
|
# Output
|
||||||
|
self.chatOutputGroup = QtWidgets.QGroupBox(u"Chat message output")
|
||||||
|
self.chatOutputLayout = QtWidgets.QGridLayout()
|
||||||
|
self.chatLayout.addWidget(self.chatOutputGroup)
|
||||||
|
self.chatOutputGroup.setLayout(self.chatOutputLayout)
|
||||||
|
self.chatOutputEnabledCheckbox = QCheckBox(getMessage("chatoutputenabled-label"))
|
||||||
|
self.chatOutputEnabledCheckbox.setObjectName("chatOutputEnabled")
|
||||||
|
self.chatOutputLayout.addWidget(self.chatOutputEnabledCheckbox, 1, 0, 1,1, Qt.AlignLeft)
|
||||||
|
|
||||||
|
self.outputFontLayout = QtWidgets.QHBoxLayout()
|
||||||
|
self.outputFontLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.outputFontFrame = QtWidgets.QFrame()
|
||||||
|
self.outputFontFrame.setLayout(self.outputFontLayout)
|
||||||
|
self.outputFontFrame.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
|
||||||
|
self.chatOutputFontLabel = QLabel(getMessage("chatoutputfont-label"), self)
|
||||||
|
self.chatOutputFontLabel.setStyleSheet(constants.STYLE_SUBCHECKBOX.format(self.posixresourcespath + u"chevrons_right.png"))
|
||||||
|
self.chatOutputFontLabel.setObjectName("font-output-label")
|
||||||
|
self.chatOutputFontButton = QtWidgets.QPushButton(getMessage("chatfont-label"))
|
||||||
|
self.chatOutputFontButton.setObjectName("set-output-font")
|
||||||
|
self.chatOutputFontButtonGroup = QtWidgets.QButtonGroup()
|
||||||
|
self.chatOutputFontButtonGroup.addButton(self.chatOutputFontButton)
|
||||||
|
self.chatOutputFontButton.released.connect(lambda: self.fontDialog("chatOutput"))
|
||||||
|
self.chatOutputColourButton = QtWidgets.QPushButton(getMessage("chatcolour-label"))
|
||||||
|
self.outputFontLayout.addWidget(self.chatOutputFontLabel, Qt.AlignLeft)
|
||||||
|
self.outputFontLayout.addWidget(self.chatOutputFontButton, Qt.AlignLeft)
|
||||||
|
self.chatOutputLayout.addWidget(self.outputFontFrame, 2, 0, 1, 3, Qt.AlignLeft)
|
||||||
|
|
||||||
|
self.chatOutputModeLabel = QLabel(getMessage("chatoutputposition-label"), self)
|
||||||
|
self.chatOutputModeLabel.setStyleSheet(constants.STYLE_SUBCHECKBOX.format(self.posixresourcespath + u"chevrons_right.png"))
|
||||||
|
self.chatOutputModeGroup = QButtonGroup()
|
||||||
|
self.chatOutputChatroomOption = QRadioButton(getMessage("chat-chatroom-option"))
|
||||||
|
self.chatOutputScrollingOption = QRadioButton(getMessage("chat-scrolling-option"))
|
||||||
|
self.chatOutputModeGroup.addButton(self.chatOutputChatroomOption)
|
||||||
|
self.chatOutputModeGroup.addButton(self.chatOutputScrollingOption)
|
||||||
|
|
||||||
|
self.chatOutputModeLabel.setObjectName("chatoutputmode")
|
||||||
|
self.chatOutputChatroomOption.setObjectName("chatoutputmode-chatroom" + constants.CONFIG_NAME_MARKER + "chatOutputMode" + constants.CONFIG_VALUE_MARKER + constants.CHATROOM_MODE)
|
||||||
|
self.chatOutputScrollingOption.setObjectName("chatoutputmode-scrolling" + constants.CONFIG_NAME_MARKER + "chatOutputMode" + constants.CONFIG_VALUE_MARKER + constants.SCROLLING_MODE)
|
||||||
|
|
||||||
|
self.chatOutputModeFrame = QtWidgets.QFrame()
|
||||||
|
self.chatOutputModeLayout = QtWidgets.QHBoxLayout()
|
||||||
|
self.chatOutputModeLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.chatOutputModeFrame.setLayout(self.chatOutputModeLayout)
|
||||||
|
self.chatOutputModeFrame.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
|
||||||
|
self.chatOutputModeLayout.addWidget(self.chatOutputModeLabel)
|
||||||
|
self.chatOutputModeLayout.addWidget(self.chatOutputChatroomOption)
|
||||||
|
self.chatOutputModeLayout.addWidget(self.chatOutputScrollingOption)
|
||||||
|
self.chatOutputLayout.addWidget(self.chatOutputModeFrame)
|
||||||
|
|
||||||
|
self.subitems['chatOutputEnabled'] = [self.chatOutputModeLabel.objectName(), self.chatOutputChatroomOption.objectName(),
|
||||||
|
self.chatOutputScrollingOption.objectName(),self.chatOutputFontButton.objectName(),
|
||||||
|
self.chatOutputFontLabel.objectName()]
|
||||||
|
# chatFrame
|
||||||
|
self.chatFrame.setLayout(self.chatLayout)
|
||||||
|
self.stackedLayout.addWidget(self.chatFrame)
|
||||||
|
|
||||||
|
def fontDialog(self, configName):
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setFamily(self.config[configName+ u"FontFamily"])
|
||||||
|
font.setPointSize(self.config[configName + u"RelativeFontSize"])
|
||||||
|
font.setWeight(self.config[configName + u"FontWeight"])
|
||||||
|
font.setUnderline(self.config[configName + u"FontUnderline"])
|
||||||
|
value, ok = QtWidgets.QFontDialog.getFont(font)
|
||||||
|
if ok:
|
||||||
|
self.config[configName + u"FontFamily"] = value.family()
|
||||||
|
self.config[configName + u"RelativeFontSize"] = value.pointSize()
|
||||||
|
self.config[configName + u"FontWeight"] = value.weight()
|
||||||
|
self.config[configName + u"FontUnderline"] = value.underline()
|
||||||
|
|
||||||
|
def colourDialog(self, configName):
|
||||||
|
oldColour = QtGui.QColor()
|
||||||
|
oldColour.setNamedColor(self.config[configName+ u"FontColor"])
|
||||||
|
colour = QtWidgets.QColorDialog.getColor(oldColour, self)
|
||||||
|
if colour.isValid():
|
||||||
|
self.config[configName + u"FontColor"] = colour.name()
|
||||||
|
|
||||||
def addMessageTab(self):
|
def addMessageTab(self):
|
||||||
self.messageFrame = QtWidgets.QFrame()
|
self.messageFrame = QtWidgets.QFrame()
|
||||||
self.messageLayout = QtWidgets.QVBoxLayout()
|
self.messageLayout = QtWidgets.QVBoxLayout()
|
||||||
@ -967,18 +1119,20 @@ class ConfigDialog(QtWidgets.QDialog):
|
|||||||
self.helpButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + u'help.png'), getMessage("help-label"))
|
self.helpButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + u'help.png'), getMessage("help-label"))
|
||||||
self.helpButton.setObjectName("help")
|
self.helpButton.setObjectName("help")
|
||||||
self.helpButton.setMaximumSize(self.helpButton.sizeHint())
|
self.helpButton.setMaximumSize(self.helpButton.sizeHint())
|
||||||
self.helpButton.pressed.connect(self.openHelp)
|
self.helpButton.released.connect(self.openHelp)
|
||||||
|
|
||||||
self.resetButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + u'cog_delete.png'),getMessage("reset-label"))
|
self.resetButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + u'cog_delete.png'),getMessage("reset-label"))
|
||||||
self.resetButton.setMaximumSize(self.resetButton.sizeHint())
|
self.resetButton.setMaximumSize(self.resetButton.sizeHint())
|
||||||
self.resetButton.setObjectName("reset")
|
self.resetButton.setObjectName("reset")
|
||||||
self.resetButton.pressed.connect(self.resetSettings)
|
self.resetButton.released.connect(self.resetSettings)
|
||||||
|
|
||||||
|
self.runButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + u'accept.png'), getMessage("run-label"))
|
||||||
|
self.runButton.released.connect(self._runWithoutStoringConfig)
|
||||||
self.runButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + u'accept.png'), getMessage("run-label"))
|
self.runButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + u'accept.png'), getMessage("run-label"))
|
||||||
self.runButton.pressed.connect(self._runWithoutStoringConfig)
|
self.runButton.pressed.connect(self._runWithoutStoringConfig)
|
||||||
self.runButton.setToolTip(getMessage("nostore-tooltip"))
|
self.runButton.setToolTip(getMessage("nostore-tooltip"))
|
||||||
self.storeAndRunButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + u'accept.png'), getMessage("storeandrun-label"))
|
self.storeAndRunButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + u'accept.png'), getMessage("storeandrun-label"))
|
||||||
self.storeAndRunButton.pressed.connect(self._saveDataAndLeave)
|
self.storeAndRunButton.released.connect(self._saveDataAndLeave)
|
||||||
self.bottomButtonLayout.addWidget(self.helpButton)
|
self.bottomButtonLayout.addWidget(self.helpButton)
|
||||||
self.bottomButtonLayout.addWidget(self.resetButton)
|
self.bottomButtonLayout.addWidget(self.resetButton)
|
||||||
self.bottomButtonLayout.addWidget(self.runButton)
|
self.bottomButtonLayout.addWidget(self.runButton)
|
||||||
@ -1008,7 +1162,8 @@ class ConfigDialog(QtWidgets.QDialog):
|
|||||||
self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(resourcespath + u"house.png"),getMessage("basics-label")))
|
self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(resourcespath + u"house.png"),getMessage("basics-label")))
|
||||||
self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(resourcespath + u"control_pause_blue.png"),getMessage("readiness-label")))
|
self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(resourcespath + u"control_pause_blue.png"),getMessage("readiness-label")))
|
||||||
self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(resourcespath + u"film_link.png"),getMessage("sync-label")))
|
self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(resourcespath + u"film_link.png"),getMessage("sync-label")))
|
||||||
self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(resourcespath + u"comments.png"),getMessage("messages-label")))
|
self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(resourcespath + u"user_comment.png"), getMessage("chat-label")))
|
||||||
|
self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(resourcespath + u"error.png"),getMessage("messages-label")))
|
||||||
self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(resourcespath + u"cog.png"),getMessage("misc-label")))
|
self.tabListWidget.addItem(QtWidgets.QListWidgetItem(QtGui.QIcon(resourcespath + u"cog.png"),getMessage("misc-label")))
|
||||||
self.tabListLayout.addWidget(self.tabListWidget)
|
self.tabListLayout.addWidget(self.tabListWidget)
|
||||||
self.tabListFrame.setLayout(self.tabListLayout)
|
self.tabListFrame.setLayout(self.tabListLayout)
|
||||||
@ -1108,6 +1263,12 @@ class ConfigDialog(QtWidgets.QDialog):
|
|||||||
self.QtWidgets = QtWidgets
|
self.QtWidgets = QtWidgets
|
||||||
self.QtGui = QtGui
|
self.QtGui = QtGui
|
||||||
self.error = error
|
self.error = error
|
||||||
|
if isWindows():
|
||||||
|
resourcespath = utils.findWorkingDir() + "\\resources\\"
|
||||||
|
else:
|
||||||
|
resourcespath = utils.findWorkingDir() + u"/resources/"
|
||||||
|
self.posixresourcespath = utils.findWorkingDir().replace(u"\\","/") + u"/resources/"
|
||||||
|
self.resourcespath = resourcespath
|
||||||
|
|
||||||
super(ConfigDialog, self).__init__()
|
super(ConfigDialog, self).__init__()
|
||||||
|
|
||||||
@ -1127,6 +1288,7 @@ class ConfigDialog(QtWidgets.QDialog):
|
|||||||
self.addBasicTab()
|
self.addBasicTab()
|
||||||
self.addReadinessTab()
|
self.addReadinessTab()
|
||||||
self.addSyncTab()
|
self.addSyncTab()
|
||||||
|
self.addChatTab()
|
||||||
self.addMessageTab()
|
self.addMessageTab()
|
||||||
self.addMiscTab()
|
self.addMiscTab()
|
||||||
self.tabList()
|
self.tabList()
|
||||||
|
|||||||
@ -38,7 +38,7 @@ class ConsoleUI(threading.Thread):
|
|||||||
self.PromptResult = data
|
self.PromptResult = data
|
||||||
self.promptMode.set()
|
self.promptMode.set()
|
||||||
elif self._syncplayClient:
|
elif self._syncplayClient:
|
||||||
self._executeCommand(data)
|
self.executeCommand(data)
|
||||||
except EOFError:
|
except EOFError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -136,7 +136,7 @@ class ConsoleUI(threading.Thread):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _executeCommand(self, data):
|
def executeCommand(self, data):
|
||||||
command = re.match(constants.UI_COMMAND_REGEX, data)
|
command = re.match(constants.UI_COMMAND_REGEX, data)
|
||||||
if not command:
|
if not command:
|
||||||
return
|
return
|
||||||
@ -145,7 +145,7 @@ class ConsoleUI(threading.Thread):
|
|||||||
self._syncplayClient.setPosition(self._syncplayClient.playerPositionBeforeLastSeek)
|
self._syncplayClient.setPosition(self._syncplayClient.playerPositionBeforeLastSeek)
|
||||||
self._syncplayClient.playerPositionBeforeLastSeek = tmp_pos
|
self._syncplayClient.playerPositionBeforeLastSeek = tmp_pos
|
||||||
elif command.group('command') in constants.COMMANDS_LIST:
|
elif command.group('command') in constants.COMMANDS_LIST:
|
||||||
self._syncplayClient.getUserList()
|
self.getUserlist()
|
||||||
elif command.group('command') in constants.COMMANDS_CHAT:
|
elif command.group('command') in constants.COMMANDS_CHAT:
|
||||||
message= command.group('parameter')
|
message= command.group('parameter')
|
||||||
self._syncplayClient.sendChat(message)
|
self._syncplayClient.sendChat(message)
|
||||||
@ -158,8 +158,8 @@ class ConsoleUI(threading.Thread):
|
|||||||
room = self._syncplayClient.userlist.currentUser.file["name"]
|
room = self._syncplayClient.userlist.currentUser.file["name"]
|
||||||
else:
|
else:
|
||||||
room = self._syncplayClient.defaultRoom
|
room = self._syncplayClient.defaultRoom
|
||||||
|
|
||||||
self._syncplayClient.setRoom(room, resetAutoplay=True)
|
self._syncplayClient.setRoom(room, resetAutoplay=True)
|
||||||
|
self._syncplayClient.ui.updateRoomName(room)
|
||||||
self._syncplayClient.sendRoom()
|
self._syncplayClient.sendRoom()
|
||||||
elif command.group('command') in constants.COMMANDS_CREATE:
|
elif command.group('command') in constants.COMMANDS_CREATE:
|
||||||
roombasename = command.group('parameter')
|
roombasename = command.group('parameter')
|
||||||
@ -191,3 +191,5 @@ class ConsoleUI(threading.Thread):
|
|||||||
self.showMessage(getMessage("syncplay-version-notification").format(syncplay.version), True)
|
self.showMessage(getMessage("syncplay-version-notification").format(syncplay.version), True)
|
||||||
self.showMessage(getMessage("more-info-notification").format(syncplay.projectURL), True)
|
self.showMessage(getMessage("more-info-notification").format(syncplay.projectURL), True)
|
||||||
|
|
||||||
|
def getUserlist(self):
|
||||||
|
self._syncplayClient.getUserList()
|
||||||
@ -16,10 +16,27 @@ import os
|
|||||||
from syncplay.utils import formatTime, sameFilename, sameFilesize, sameFileduration, RoomPasswordProvider, formatSize, isURL
|
from syncplay.utils import formatTime, sameFilename, sameFilesize, sameFileduration, RoomPasswordProvider, formatSize, isURL
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from twisted.internet import task
|
from twisted.internet import task
|
||||||
|
from syncplay.ui.consoleUI import ConsoleUI
|
||||||
if isMacOS() and IsPySide:
|
if isMacOS() and IsPySide:
|
||||||
from Foundation import NSURL
|
from Foundation import NSURL
|
||||||
lastCheckedForUpdates = None
|
lastCheckedForUpdates = None
|
||||||
|
|
||||||
|
class ConsoleInGUI(ConsoleUI):
|
||||||
|
def showMessage(self, message, noTimestamp=False):
|
||||||
|
self._syncplayClient.ui.showMessage(message, True)
|
||||||
|
|
||||||
|
def showDebugMessage(self, message):
|
||||||
|
self._syncplayClient.ui.showDebugMessage(message)
|
||||||
|
|
||||||
|
def showErrorMessage(self, message, criticalerror=False):
|
||||||
|
self._syncplayClient.ui.showErrorMessage(message, criticalerror)
|
||||||
|
|
||||||
|
def updateRoomName(self, room=""):
|
||||||
|
self._syncplayClient.ui.updateRoomName(room)
|
||||||
|
|
||||||
|
def getUserlist(self):
|
||||||
|
self._syncplayClient.showUserList(self)
|
||||||
|
|
||||||
class UserlistItemDelegate(QtWidgets.QStyledItemDelegate):
|
class UserlistItemDelegate(QtWidgets.QStyledItemDelegate):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
QtWidgets.QStyledItemDelegate.__init__(self)
|
QtWidgets.QStyledItemDelegate.__init__(self)
|
||||||
@ -84,8 +101,7 @@ class UserlistItemDelegate(QtWidgets.QStyledItemDelegate):
|
|||||||
QtWidgets.QStyledItemDelegate.paint(self, itemQPainter, optionQStyleOptionViewItem, indexQModelIndex)
|
QtWidgets.QStyledItemDelegate.paint(self, itemQPainter, optionQStyleOptionViewItem, indexQModelIndex)
|
||||||
|
|
||||||
class AboutDialog(QtWidgets.QDialog):
|
class AboutDialog(QtWidgets.QDialog):
|
||||||
|
def __init__(self, parent=None):
|
||||||
def __init__(self, parent=None):
|
|
||||||
super(AboutDialog, self).__init__(parent)
|
super(AboutDialog, self).__init__(parent)
|
||||||
if isMacOS():
|
if isMacOS():
|
||||||
self.setWindowTitle("")
|
self.setWindowTitle("")
|
||||||
@ -120,17 +136,17 @@ class AboutDialog(QtWidgets.QDialog):
|
|||||||
self.setSizeGripEnabled(False)
|
self.setSizeGripEnabled(False)
|
||||||
self.setLayout(aboutLayout)
|
self.setLayout(aboutLayout)
|
||||||
|
|
||||||
def openLicense(self):
|
def openLicense(self):
|
||||||
if isWindows():
|
if isWindows():
|
||||||
QtGui.QDesktopServices.openUrl(QUrl("file:///" + resourcespath + u"license.rtf"))
|
QtGui.QDesktopServices.openUrl(QUrl("file:///" + resourcespath + u"license.rtf"))
|
||||||
else:
|
else:
|
||||||
QtGui.QDesktopServices.openUrl(QUrl("file://" + resourcespath + u"license.rtf"))
|
QtGui.QDesktopServices.openUrl(QUrl("file://" + resourcespath + u"license.rtf"))
|
||||||
|
|
||||||
def openDependencies(self):
|
def openDependencies(self):
|
||||||
if isWindows():
|
if isWindows():
|
||||||
QtGui.QDesktopServices.openUrl(QUrl("file:///" + resourcespath + u"third-party-notices.rtf"))
|
QtGui.QDesktopServices.openUrl(QUrl("file:///" + resourcespath + u"third-party-notices.rtf"))
|
||||||
else:
|
else:
|
||||||
QtGui.QDesktopServices.openUrl(QUrl("file://" + resourcespath + u"third-party-notices.rtf"))
|
QtGui.QDesktopServices.openUrl(QUrl("file://" + resourcespath + u"third-party-notices.rtf"))
|
||||||
|
|
||||||
class MainWindow(QtWidgets.QMainWindow):
|
class MainWindow(QtWidgets.QMainWindow):
|
||||||
insertPosition = None
|
insertPosition = None
|
||||||
@ -347,6 +363,8 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
|
|
||||||
def addClient(self, client):
|
def addClient(self, client):
|
||||||
self._syncplayClient = client
|
self._syncplayClient = client
|
||||||
|
if self.console:
|
||||||
|
self.console.addClient(client)
|
||||||
self.roomInput.setText(self._syncplayClient.getRoom())
|
self.roomInput.setText(self._syncplayClient.getRoom())
|
||||||
self.config = self._syncplayClient.getConfig()
|
self.config = self._syncplayClient.getConfig()
|
||||||
try:
|
try:
|
||||||
@ -385,6 +403,8 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
self.chatInput.setReadOnly(True)
|
self.chatInput.setReadOnly(True)
|
||||||
if not featureList["sharedPlaylists"]:
|
if not featureList["sharedPlaylists"]:
|
||||||
self.playlistGroup.setEnabled(False)
|
self.playlistGroup.setEnabled(False)
|
||||||
|
self.chatInput.setMaxLength(constants.MAX_CHAT_MESSAGE_LENGTH)
|
||||||
|
self.roomInput.setMaxLength(constants.MAX_ROOM_NAME_LENGTH)
|
||||||
|
|
||||||
def showMessage(self, message, noTimestamp=False):
|
def showMessage(self, message, noTimestamp=False):
|
||||||
message = unicode(message)
|
message = unicode(message)
|
||||||
@ -1126,10 +1146,22 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
self._syncplayClient.playlist.changePlaylist(newPlaylist)
|
self._syncplayClient.playlist.changePlaylist(newPlaylist)
|
||||||
self._syncplayClient.fileSwitch.updateInfo()
|
self._syncplayClient.fileSwitch.updateInfo()
|
||||||
|
|
||||||
|
def executeCommand(self, command):
|
||||||
|
self.showMessage(u"/{}".format(command))
|
||||||
|
self.console.executeCommand(command)
|
||||||
|
|
||||||
def sendChatMessage(self):
|
def sendChatMessage(self):
|
||||||
if self.chatInput.text() <> "":
|
chatText = self.chatInput.text()
|
||||||
self._syncplayClient.sendChat(self.chatInput.text())
|
self.chatInput.setText("")
|
||||||
self.chatInput.setText("")
|
if chatText <> "":
|
||||||
|
if chatText[:1] == "/" and chatText <> "/":
|
||||||
|
command = chatText[1:]
|
||||||
|
if command and command[:1] == "/":
|
||||||
|
chatText = chatText[1:]
|
||||||
|
else:
|
||||||
|
self.executeCommand(command)
|
||||||
|
return
|
||||||
|
self._syncplayClient.sendChat(chatText)
|
||||||
|
|
||||||
def addTopLayout(self, window):
|
def addTopLayout(self, window):
|
||||||
window.topSplit = self.topSplitter(Qt.Horizontal, self)
|
window.topSplit = self.topSplitter(Qt.Horizontal, self)
|
||||||
@ -1705,6 +1737,8 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(MainWindow, self).__init__()
|
super(MainWindow, self).__init__()
|
||||||
|
self.console = ConsoleInGUI()
|
||||||
|
self.console.setDaemon(True)
|
||||||
self.newWatchlist = []
|
self.newWatchlist = []
|
||||||
self.publicServerList = []
|
self.publicServerList = []
|
||||||
self.lastCheckedForUpdates = None
|
self.lastCheckedForUpdates = None
|
||||||
|
|||||||
@ -130,6 +130,13 @@ def formatSize (bytes, precise=False):
|
|||||||
def isASCII(s):
|
def isASCII(s):
|
||||||
return all(ord(c) < 128 for c in s)
|
return all(ord(c) < 128 for c in s)
|
||||||
|
|
||||||
|
def findResourcePath(resourceName):
|
||||||
|
if resourceName == "syncplay.lua":
|
||||||
|
resourcePath = os.path.join(findWorkingDir(), "lua", "intf" , "resources", resourceName)
|
||||||
|
else:
|
||||||
|
resourcePath = os.path.join(findWorkingDir(),"resources", resourceName)
|
||||||
|
return resourcePath
|
||||||
|
|
||||||
def findWorkingDir():
|
def findWorkingDir():
|
||||||
frozen = getattr(sys, 'frozen', '')
|
frozen = getattr(sys, 'frozen', '')
|
||||||
if not frozen:
|
if not frozen:
|
||||||
@ -152,6 +159,14 @@ def getResourcesPath():
|
|||||||
resourcespath = getResourcesPath()
|
resourcespath = getResourcesPath()
|
||||||
posixresourcespath = findWorkingDir().replace(u"\\","/") + u"/resources/"
|
posixresourcespath = findWorkingDir().replace(u"\\","/") + u"/resources/"
|
||||||
|
|
||||||
|
def getDefaultMonospaceFont():
|
||||||
|
if platform.system() == "Windows":
|
||||||
|
return constants.DEFAULT_WINDOWS_MONOSPACE_FONT
|
||||||
|
elif platform.system() == "Darwin":
|
||||||
|
return constants.DEFAULT_OSX_MONOSPACE_FONT
|
||||||
|
else:
|
||||||
|
return constants.FALLBACK_MONOSPACE_FONT
|
||||||
|
|
||||||
def limitedPowerset(s, minLength):
|
def limitedPowerset(s, minLength):
|
||||||
return itertools.chain.from_iterable(itertools.combinations(s, r) for r in xrange(len(s), minLength, -1))
|
return itertools.chain.from_iterable(itertools.combinations(s, r) for r in xrange(len(s), minLength, -1))
|
||||||
|
|
||||||
@ -189,20 +204,28 @@ def blackholeStdoutForFrozenWindow():
|
|||||||
|
|
||||||
def truncateText(unicodeText, maxLength):
|
def truncateText(unicodeText, maxLength):
|
||||||
try:
|
try:
|
||||||
unicodeText = unicodedata.normalize('NFC', unicodeText)
|
unicodeText = unicodeText.decode('utf-8')
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
maxSaneLength= maxLength*5
|
return(unicode(unicodeText.encode("utf-8"), "utf-8", errors="ignore")[:maxLength])
|
||||||
if len(unicodeText) > maxSaneLength:
|
|
||||||
unicodeText = unicode(unicodeText.encode("utf-8")[:maxSaneLength], "utf-8", errors="ignore")
|
|
||||||
while len(unicodeText) > maxLength:
|
|
||||||
unicodeText = unicode(unicodeText.encode("utf-8")[:-1], "utf-8", errors="ignore")
|
|
||||||
return unicodeText
|
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
def splitText(unicodeText, maxLength):
|
||||||
|
try:
|
||||||
|
unicodeText = unicodeText.decode('utf-8')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
unicodeText = unicode(unicodeText.encode("utf-8"), "utf-8", errors="ignore")
|
||||||
|
unicodeArray = [unicodeText[i:i + maxLength] for i in range(0, len(unicodeText), maxLength)]
|
||||||
|
return(unicodeArray)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return [""]
|
||||||
|
|
||||||
# Relate to file hashing / difference checking:
|
# Relate to file hashing / difference checking:
|
||||||
|
|
||||||
def stripfilename(filename, stripURL):
|
def stripfilename(filename, stripURL):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user