French touch

This commit is contained in:
SoSIE 2021-08-13 00:50:48 +02:00
parent f7faa59246
commit e9c8456b44
15 changed files with 5737 additions and 3 deletions

Binary file not shown.

86
syncplay/ass2messages.py Normal file
View File

@ -0,0 +1,86 @@
# coding:utf8
# ass2messages.py, dictionary to subtitle exporter to automate translation
# author sosie-js
# require my pythonfx mofied version adding fixes and del_line facility
#==========================================
# For relative imports to work
import os, sys; sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from pyonfx import *
# will include message_en.py a dictiatary of English messages for the syncplay gui
# https://raw.githubusercontent.com/Syncplay/syncplay/master/syncplay/messages_en.py
import messages_en
lang="fr"
dict_message_file="messages_"+lang
ass_message_file=dict_message_file+".ass"
dict_message_file=dict_message_file+".py"
print("Welcome on the ass file %s to %s dictionary to exporter" % (dict_message_file,ass_message_file) )
print("-------------------------------------------------------------------------------------")
io = Ass(ass_message_file)
meta, styles, lines = io.get_data()
#messages will hold the message dict
messages=messages_en.en
i=len(lines)
pos_time=0
dict={}
#the fist line of Untitled.ass, empty, will serve as template
for line in lines:
dict[str(line.effect)]=str(line.raw_text)
script_dir=os.path.dirname(os.path.realpath(__file__))
path_input=os.path.join(script_dir,"messages_en.py")
input = open(path_input, "r", encoding="utf-8-sig")
template=input.read()
input.close()
note='# This file was mainly auto generated from ass2messages.py applied on '+ass_message_file+' to get these messages\n'
note+='# its format has been harmonized, values are always stored in doublequotes strings, \n'
note+='# if double quoted string in the value then they should be esacaped like this \\". There is\n'
note+='# thus no reason to have single quoted strings. Tabs \\t and newlines \\n need also to be escaped.\n'
note+='# whith ass2messages.py which handles these issues, this is no more a nightmare to handle. \n'
note+='# I fixed partially messages_en.py serving as template. an entry should be added in messages.py:\n'
note+='# "'+lang+'": messages_'+lang+'.'+lang+', . Produced by sosie - sos-productions.com\n\n'
template=template.replace('en = {',note+lang+' = {')
#Normalize space (and quotes!), launch the cleaner machine
template=template.replace('": "','": "')
template=template.replace('": \'','": \'')
def escapeBackslashAndTabs(s):
s = s.replace("\t", "\\t")
s = s.replace("\n", "\\n")
return s
def escapeDoublequotes(s):
s = s.replace('"', '\\"')
return s
for key, value in messages.items():
value=value.replace("&","&")
if(key == "LANGUAGE"):
language=value
source=('"%s": "%s"' % (key, escapeBackslashAndTabs(value)))
target=('"%s": "%s"' % (key,dict[key]))
print(key+ ': "'+value+'" => "'+dict[key]+'"')
template=template.replace(source,target)
source=('"%s": \'%s\'' % (key, escapeBackslashAndTabs(value)))
target=('"%s": "%s"' % (key,escapeDoublequotes(dict[key])))
template=template.replace(source,target)
template=template.replace('English dictionary',language+' dictionary')
path_output=os.path.join(script_dir,dict_message_file)
with open(path_output,"w") as f:
f.write(template)
#print(template)

View File

@ -4,6 +4,7 @@ from syncplay import constants
from . import messages_en from . import messages_en
from . import messages_ru from . import messages_ru
from . import messages_de from . import messages_de
from . import messages_fr
from . import messages_it from . import messages_it
from . import messages_es from . import messages_es
from . import messages_pt_BR from . import messages_pt_BR
@ -15,6 +16,7 @@ messages = {
"de": messages_de.de, "de": messages_de.de,
"en": messages_en.en, "en": messages_en.en,
"es": messages_es.es, "es": messages_es.es,
"fr": messages_fr.fr,
"it": messages_it.it, "it": messages_it.it,
"pt_PT": messages_pt_PT.pt_PT, "pt_PT": messages_pt_PT.pt_PT,
"pt_BR": messages_pt_BR.pt_BR, "pt_BR": messages_pt_BR.pt_BR,

51
syncplay/messages2ass.py Normal file
View File

@ -0,0 +1,51 @@
# coding:utf8
# messages2ass.py, dictionary to subtitle exporter to automate translation
# author sosie-js
# require my pythonfx mofied version adding fixes and del_line facility
#==========================================
# For relative imports to work
import os, sys; sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from pyonfx import *
# will include message_en.py a dictiatary of English messages for the syncplay gui
# https://raw.githubusercontent.com/Syncplay/syncplay/master/syncplay/messages_en.py
import messages_en
dict_message_file="message_en" #.py
ass_message_file=dict_message_file+".ass"
print("Welcome on the %s dictionary to ass file %s exporter" % (dict_message_file,ass_message_file) )
print("-------------------------------------------------------------------------------------")
io = Ass() #Will use aegisub template Untitled.ass as basis instead of "in.ass"
io.set_output(ass_message_file)
meta, styles, lines = io.get_data()
#messages will hold the message dict
messages=messages_en.en
i=len(lines)
pos_time=0
#the fist line of Untitled.ass, empty, will serve as template
line= lines[0].copy()
duration=2000
for key, value in messages.items():
print("Exporting value of key %s as subtiyle line" % key)
l= line.copy()
i=i+1
l.i=i
l.start_time = pos_time
l.end_time = pos_time+duration
l.effect= key
l.text =value
io.write_line(l)
pos_time=pos_time+duration
#Don't forget to remove the pollution lines of the template
# in our case remove the empty single line of Untitled.ass.
io.del_line(1)
io.save()
io.open_aegisub()

450
syncplay/messages_en.ass Normal file
View File

@ -0,0 +1,450 @@
[Script Info]
; Script generated by Aegisub 3.2.2
; http://www.aegisub.org/
Title: Default Aegisub file
ScriptType: v4.00+
WrapStyle: 0
ScaledBorderAndShadow: yes
YCbCr Matrix: None
[Aegisub Project Garbage]
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Arial,20,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,2,2,2,10,10,10,1
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:00.00,0:00:02.00,Default,,0000,0000,0000,LANGUAGE,English
Dialogue: 0,0:00:02.00,0:00:04.00,Default,,0000,0000,0000,config-cleared-notification,Settings cleared. Changes will be saved when you store a valid configuration.
Dialogue: 0,0:00:04.00,0:00:06.00,Default,,0000,0000,0000,relative-config-notification,Loaded relative configuration file(s): {}
Dialogue: 0,0:00:06.00,0:00:08.00,Default,,0000,0000,0000,connection-attempt-notification,Attempting to connect to {}:{}
Dialogue: 0,0:00:08.00,0:00:10.00,Default,,0000,0000,0000,reconnection-attempt-notification,Connection with server lost, attempting to reconnect
Dialogue: 0,0:00:10.00,0:00:12.00,Default,,0000,0000,0000,disconnection-notification,Disconnected from server
Dialogue: 0,0:00:12.00,0:00:14.00,Default,,0000,0000,0000,connection-failed-notification,Connection with server failed
Dialogue: 0,0:00:14.00,0:00:16.00,Default,,0000,0000,0000,connected-successful-notification,Successfully connected to server
Dialogue: 0,0:00:16.00,0:00:18.00,Default,,0000,0000,0000,retrying-notification,%s, Retrying in %d seconds...
Dialogue: 0,0:00:18.00,0:00:20.00,Default,,0000,0000,0000,reachout-successful-notification,Successfully reached {} ({})
Dialogue: 0,0:00:20.00,0:00:22.00,Default,,0000,0000,0000,rewind-notification,Rewinded due to time difference with {}
Dialogue: 0,0:00:22.00,0:00:24.00,Default,,0000,0000,0000,fastforward-notification,Fast-forwarded due to time difference with {}
Dialogue: 0,0:00:24.00,0:00:26.00,Default,,0000,0000,0000,slowdown-notification,Slowing down due to time difference with {}
Dialogue: 0,0:00:26.00,0:00:28.00,Default,,0000,0000,0000,revert-notification,Reverting speed back to normal
Dialogue: 0,0:00:28.00,0:00:30.00,Default,,0000,0000,0000,pause-notification,{} paused
Dialogue: 0,0:00:30.00,0:00:32.00,Default,,0000,0000,0000,unpause-notification,{} unpaused
Dialogue: 0,0:00:32.00,0:00:34.00,Default,,0000,0000,0000,seek-notification,{} jumped from {} to {}
Dialogue: 0,0:00:34.00,0:00:36.00,Default,,0000,0000,0000,current-offset-notification,Current offset: {} seconds
Dialogue: 0,0:00:36.00,0:00:38.00,Default,,0000,0000,0000,media-directory-list-updated-notification,Syncplay media directories have been updated.
Dialogue: 0,0:00:38.00,0:00:40.00,Default,,0000,0000,0000,room-join-notification,{} has joined the room: '{}'
Dialogue: 0,0:00:40.00,0:00:42.00,Default,,0000,0000,0000,left-notification,{} has left
Dialogue: 0,0:00:42.00,0:00:44.00,Default,,0000,0000,0000,left-paused-notification,{} left, {} paused
Dialogue: 0,0:00:44.00,0:00:46.00,Default,,0000,0000,0000,playing-notification,{} is playing '{}' ({})
Dialogue: 0,0:00:46.00,0:00:48.00,Default,,0000,0000,0000,playing-notification/room-addendum, in room: '{}'
Dialogue: 0,0:00:48.00,0:00:50.00,Default,,0000,0000,0000,not-all-ready,Not ready: {}
Dialogue: 0,0:00:50.00,0:00:52.00,Default,,0000,0000,0000,all-users-ready,Everyone is ready ({} users)
Dialogue: 0,0:00:52.00,0:00:54.00,Default,,0000,0000,0000,ready-to-unpause-notification,You are now set as ready - unpause again to unpause
Dialogue: 0,0:00:54.00,0:00:56.00,Default,,0000,0000,0000,set-as-ready-notification,You are now set as ready
Dialogue: 0,0:00:56.00,0:00:58.00,Default,,0000,0000,0000,set-as-not-ready-notification,You are now set as not ready
Dialogue: 0,0:00:58.00,0:01:00.00,Default,,0000,0000,0000,autoplaying-notification,Auto-playing in {}...
Dialogue: 0,0:01:00.00,0:01:02.00,Default,,0000,0000,0000,identifying-as-controller-notification,Identifying as room operator with password '{}'...
Dialogue: 0,0:01:02.00,0:01:04.00,Default,,0000,0000,0000,failed-to-identify-as-controller-notification,{} failed to identify as a room operator.
Dialogue: 0,0:01:04.00,0:01:06.00,Default,,0000,0000,0000,authenticated-as-controller-notification,{} authenticated as a room operator
Dialogue: 0,0:01:06.00,0:01:08.00,Default,,0000,0000,0000,created-controlled-room-notification,Created managed room '{}' with password '{}'. Please save this information for future reference!
In managed rooms everyone is kept in sync with the room operator(s) who are the only ones who can pause, unpause, seek, and change the playlist.
You should ask regular viewers to join the room '{}' but the room operators can join the room '{}' to automatically authenticate themselves.
Dialogue: 0,0:01:08.00,0:01:10.00,Default,,0000,0000,0000,file-different-notification,File you are playing appears to be different from {}'s
Dialogue: 0,0:01:10.00,0:01:12.00,Default,,0000,0000,0000,file-differences-notification,Your file differs in the following way(s): {}
Dialogue: 0,0:01:12.00,0:01:14.00,Default,,0000,0000,0000,room-file-differences,File differences: {}
Dialogue: 0,0:01:14.00,0:01:16.00,Default,,0000,0000,0000,file-difference-filename,name
Dialogue: 0,0:01:16.00,0:01:18.00,Default,,0000,0000,0000,file-difference-filesize,size
Dialogue: 0,0:01:18.00,0:01:20.00,Default,,0000,0000,0000,file-difference-duration,duration
Dialogue: 0,0:01:20.00,0:01:22.00,Default,,0000,0000,0000,alone-in-the-room,You're alone in the room
Dialogue: 0,0:01:22.00,0:01:24.00,Default,,0000,0000,0000,different-filesize-notification, (their file size is different from yours!)
Dialogue: 0,0:01:24.00,0:01:26.00,Default,,0000,0000,0000,userlist-playing-notification,{} is playing:
Dialogue: 0,0:01:26.00,0:01:28.00,Default,,0000,0000,0000,file-played-by-notification,File: {} is being played by:
Dialogue: 0,0:01:28.00,0:01:30.00,Default,,0000,0000,0000,no-file-played-notification,{} is not playing a file
Dialogue: 0,0:01:30.00,0:01:32.00,Default,,0000,0000,0000,notplaying-notification,People who are not playing any file:
Dialogue: 0,0:01:32.00,0:01:34.00,Default,,0000,0000,0000,userlist-room-notification,In room '{}':
Dialogue: 0,0:01:34.00,0:01:36.00,Default,,0000,0000,0000,userlist-file-notification,File
Dialogue: 0,0:01:36.00,0:01:38.00,Default,,0000,0000,0000,controller-userlist-userflag,Operator
Dialogue: 0,0:01:38.00,0:01:40.00,Default,,0000,0000,0000,ready-userlist-userflag,Ready
Dialogue: 0,0:01:40.00,0:01:42.00,Default,,0000,0000,0000,update-check-failed-notification,Could not automatically check whether Syncplay {} is up to date. Want to visit https://syncplay.pl/ to manually check for updates?
Dialogue: 0,0:01:42.00,0:01:44.00,Default,,0000,0000,0000,syncplay-uptodate-notification,Syncplay is up to date
Dialogue: 0,0:01:44.00,0:01:46.00,Default,,0000,0000,0000,syncplay-updateavailable-notification,A new version of Syncplay is available. Do you want to visit the release page?
Dialogue: 0,0:01:46.00,0:01:48.00,Default,,0000,0000,0000,mplayer-file-required-notification,Syncplay using mplayer requires you to provide file when starting
Dialogue: 0,0:01:48.00,0:01:50.00,Default,,0000,0000,0000,mplayer-file-required-notification/example,Usage example: syncplay [options] [url|path/]filename
Dialogue: 0,0:01:50.00,0:01:52.00,Default,,0000,0000,0000,mplayer2-required,Syncplay is incompatible with MPlayer 1.x, please use mplayer2 or mpv
Dialogue: 0,0:01:52.00,0:01:54.00,Default,,0000,0000,0000,unrecognized-command-notification,Unrecognized command
Dialogue: 0,0:01:54.00,0:01:56.00,Default,,0000,0000,0000,commandlist-notification,Available commands:
Dialogue: 0,0:01:56.00,0:01:58.00,Default,,0000,0000,0000,commandlist-notification/room, r [name] - change room
Dialogue: 0,0:01:58.00,0:02:00.00,Default,,0000,0000,0000,commandlist-notification/list, l - show user list
Dialogue: 0,0:02:00.00,0:02:02.00,Default,,0000,0000,0000,commandlist-notification/undo, u - undo last seek
Dialogue: 0,0:02:02.00,0:02:04.00,Default,,0000,0000,0000,commandlist-notification/pause, p - toggle pause
Dialogue: 0,0:02:04.00,0:02:06.00,Default,,0000,0000,0000,commandlist-notification/seek, [s][+-]time - seek to the given value of time, if + or - is not specified it's absolute time in seconds or min:sec
Dialogue: 0,0:02:06.00,0:02:08.00,Default,,0000,0000,0000,commandlist-notification/help, h - this help
Dialogue: 0,0:02:08.00,0:02:10.00,Default,,0000,0000,0000,commandlist-notification/toggle, t - toggles whether you are ready to watch or not
Dialogue: 0,0:02:10.00,0:02:12.00,Default,,0000,0000,0000,commandlist-notification/create, c [name] - create managed room using name of current room
Dialogue: 0,0:02:12.00,0:02:14.00,Default,,0000,0000,0000,commandlist-notification/auth, a [password] - authenticate as room operator with operator password
Dialogue: 0,0:02:14.00,0:02:16.00,Default,,0000,0000,0000,commandlist-notification/chat, ch [message] - send a chat message in a room
Dialogue: 0,0:02:16.00,0:02:18.00,Default,,0000,0000,0000,commandList-notification/queue, qa [file/url] - add file or url to bottom of playlist
Dialogue: 0,0:02:18.00,0:02:20.00,Default,,0000,0000,0000,commandList-notification/playlist, ql - show the current playlist
Dialogue: 0,0:02:20.00,0:02:22.00,Default,,0000,0000,0000,commandList-notification/select, qs [index] - select given entry in the playlist
Dialogue: 0,0:02:22.00,0:02:24.00,Default,,0000,0000,0000,commandList-notification/delete, qd [index] - delete the given entry from the playlist
Dialogue: 0,0:02:24.00,0:02:26.00,Default,,0000,0000,0000,syncplay-version-notification,Syncplay version: {}
Dialogue: 0,0:02:26.00,0:02:28.00,Default,,0000,0000,0000,more-info-notification,More info available at: {}
Dialogue: 0,0:02:28.00,0:02:30.00,Default,,0000,0000,0000,gui-data-cleared-notification,Syncplay has cleared the path and window state data used by the GUI.
Dialogue: 0,0:02:30.00,0:02:32.00,Default,,0000,0000,0000,language-changed-msgbox-label,Language will be changed when you run Syncplay.
Dialogue: 0,0:02:32.00,0:02:34.00,Default,,0000,0000,0000,promptforupdate-label,Is it okay for Syncplay to automatically check for updates from time to time?
Dialogue: 0,0:02:34.00,0:02:36.00,Default,,0000,0000,0000,media-player-latency-warning,Warning: The media player took {} seconds to respond. If you experience syncing issues then close applications to free up system resources, and if that doesn't work then try a different media player.
Dialogue: 0,0:02:36.00,0:02:38.00,Default,,0000,0000,0000,mpv-unresponsive-error,mpv has not responded for {} seconds so appears to have malfunctioned. Please restart Syncplay.
Dialogue: 0,0:02:38.00,0:02:40.00,Default,,0000,0000,0000,enter-to-exit-prompt,Press enter to exit
Dialogue: 0,0:02:40.00,0:02:42.00,Default,,0000,0000,0000,missing-arguments-error,Some necessary arguments are missing, refer to --help
Dialogue: 0,0:02:42.00,0:02:44.00,Default,,0000,0000,0000,server-timeout-error,Connection with server timed out
Dialogue: 0,0:02:44.00,0:02:46.00,Default,,0000,0000,0000,mpc-slave-error,Unable to start MPC in slave mode!
Dialogue: 0,0:02:46.00,0:02:48.00,Default,,0000,0000,0000,mpc-version-insufficient-error,MPC version not sufficient, please use `mpc-hc` >= `{}`
Dialogue: 0,0:02:48.00,0:02:50.00,Default,,0000,0000,0000,mpc-be-version-insufficient-error,MPC version not sufficient, please use `mpc-be` >= `{}`
Dialogue: 0,0:02:50.00,0:02:52.00,Default,,0000,0000,0000,mpv-version-error,Syncplay is not compatible with this version of mpv. Please use a different version of mpv (e.g. Git HEAD).
Dialogue: 0,0:02:52.00,0:02:54.00,Default,,0000,0000,0000,mpv-failed-advice,The reason mpv cannot start may be due to the use of unsupported command line arguments or an unsupported version of mpv.
Dialogue: 0,0:02:54.00,0:02:56.00,Default,,0000,0000,0000,player-file-open-error,Player failed opening file
Dialogue: 0,0:02:56.00,0:02:58.00,Default,,0000,0000,0000,player-path-error,Player path is not set properly. Supported players are: mpv, mpv.net, VLC, MPC-HC, MPC-BE, mplayer2, and IINA
Dialogue: 0,0:02:58.00,0:03:00.00,Default,,0000,0000,0000,hostname-empty-error,Hostname can't be empty
Dialogue: 0,0:03:00.00,0:03:02.00,Default,,0000,0000,0000,empty-error,{} can't be empty
Dialogue: 0,0:03:02.00,0:03:04.00,Default,,0000,0000,0000,media-player-error,Media player error: "{}"
Dialogue: 0,0:03:04.00,0:03:06.00,Default,,0000,0000,0000,unable-import-gui-error,Could not import GUI libraries. If you do not have PySide installed then you will need to install it for the GUI to work.
Dialogue: 0,0:03:06.00,0:03:08.00,Default,,0000,0000,0000,unable-import-twisted-error,Could not import Twisted. Please install Twisted v16.4.0 or later.
Dialogue: 0,0:03:08.00,0:03:10.00,Default,,0000,0000,0000,arguments-missing-error,Some necessary arguments are missing, refer to --help
Dialogue: 0,0:03:10.00,0:03:12.00,Default,,0000,0000,0000,unable-to-start-client-error,Unable to start client
Dialogue: 0,0:03:12.00,0:03:14.00,Default,,0000,0000,0000,player-path-config-error,Player path is not set properly. Supported players are: mpv, mpv.net, VLC, MPC-HC, MPC-BE, mplayer2, and IINA.
Dialogue: 0,0:03:14.00,0:03:16.00,Default,,0000,0000,0000,no-file-path-config-error,File must be selected before starting your player
Dialogue: 0,0:03:16.00,0:03:18.00,Default,,0000,0000,0000,no-hostname-config-error,Hostname can't be empty
Dialogue: 0,0:03:18.00,0:03:20.00,Default,,0000,0000,0000,invalid-port-config-error,Port must be valid
Dialogue: 0,0:03:20.00,0:03:22.00,Default,,0000,0000,0000,empty-value-config-error,{} can't be empty
Dialogue: 0,0:03:22.00,0:03:24.00,Default,,0000,0000,0000,not-json-error,Not a json encoded string
Dialogue: 0,0:03:24.00,0:03:26.00,Default,,0000,0000,0000,hello-arguments-error,Not enough Hello arguments
Dialogue: 0,0:03:26.00,0:03:28.00,Default,,0000,0000,0000,version-mismatch-error,Mismatch between versions of client and server
Dialogue: 0,0:03:28.00,0:03:30.00,Default,,0000,0000,0000,vlc-failed-connection,Failed to connect to VLC. If you have not installed syncplay.lua and are using the latest verion of VLC then please refer to https://syncplay.pl/LUA/ for instructions. Syncplay and VLC 4 are not currently compatible, so either use VLC 3 or an alternative such as mpv.
Dialogue: 0,0:03:30.00,0:03:32.00,Default,,0000,0000,0000,vlc-failed-noscript,VLC has reported that the syncplay.lua interface script has not been installed. Please refer to https://syncplay.pl/LUA/ for instructions.
Dialogue: 0,0:03:32.00,0:03:34.00,Default,,0000,0000,0000,vlc-failed-versioncheck,This version of VLC is not supported by Syncplay.
Dialogue: 0,0:03:34.00,0:03:36.00,Default,,0000,0000,0000,vlc-initial-warning,VLC does not always provide accurate position information to Syncplay, especially for .mp4 and .avi files. If you experience problems with erroneous seeking then please try an alternative media player such as <a href="https://mpv.io/">mpv</a> (or <a href="https://github.com/stax76/mpv.net/">mpv.net</a> for Windows users).
Dialogue: 0,0:03:36.00,0:03:38.00,Default,,0000,0000,0000,feature-sharedPlaylists,shared playlists
Dialogue: 0,0:03:38.00,0:03:40.00,Default,,0000,0000,0000,feature-chat,chat
Dialogue: 0,0:03:40.00,0:03:42.00,Default,,0000,0000,0000,feature-readiness,readiness
Dialogue: 0,0:03:42.00,0:03:44.00,Default,,0000,0000,0000,feature-managedRooms,managed rooms
Dialogue: 0,0:03:44.00,0:03:46.00,Default,,0000,0000,0000,not-supported-by-server-error,The {} feature is not supported by this server..
Dialogue: 0,0:03:46.00,0:03:48.00,Default,,0000,0000,0000,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 {}.
Dialogue: 0,0:03:48.00,0:03:50.00,Default,,0000,0000,0000,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.
Dialogue: 0,0:03:50.00,0:03:52.00,Default,,0000,0000,0000,invalid-seek-value,Invalid seek value
Dialogue: 0,0:03:52.00,0:03:54.00,Default,,0000,0000,0000,invalid-offset-value,Invalid offset value
Dialogue: 0,0:03:54.00,0:03:56.00,Default,,0000,0000,0000,switch-file-not-found-error,Could not switch to file '{0}'. Syncplay looks in specified media directories.
Dialogue: 0,0:03:56.00,0:03:58.00,Default,,0000,0000,0000,folder-search-timeout-error,The search for media in media directories was aborted as it took too long to search through '{}'. This will occur if you select a folder with too many sub-folders in your list of media folders to search through. For automatic file switching to work again please select File->Set Media Directories in the menu bar and remove this directory or replace it with an appropriate sub-folder. If the folder is actually fine then you can re-enable it by selecting File->Set Media Directories and pressing 'OK'.
Dialogue: 0,0:03:58.00,0:04:00.00,Default,,0000,0000,0000,folder-search-first-file-timeout-error,The search for media in '{}' was aborted as it took too long to access the directory. This could happen if it is a network drive or if you configure your drive to spin down after a period of inactivity. For automatic file switching to work again please go to File->Set Media Directories and either remove the directory or resolve the issue (e.g. by changing power saving settings).
Dialogue: 0,0:04:00.00,0:04:02.00,Default,,0000,0000,0000,added-file-not-in-media-directory-error,You loaded a file in '{}' which is not a known media directory. You can add this as a media directory by selecting File->Set Media Directories in the menu bar.
Dialogue: 0,0:04:02.00,0:04:04.00,Default,,0000,0000,0000,no-media-directories-error,No media directories have been set. For shared playlist and file switching features to work properly please select File->Set Media Directories and specify where Syncplay should look to find media files.
Dialogue: 0,0:04:04.00,0:04:06.00,Default,,0000,0000,0000,cannot-find-directory-error,Could not find media directory '{}'. To update your list of media directories please select File->Set Media Directories from the menu bar and specify where Syncplay should look to find media files.
Dialogue: 0,0:04:06.00,0:04:08.00,Default,,0000,0000,0000,failed-to-load-server-list-error,Failed to load public server list. Please visit https://www.syncplay.pl/ in your browser.
Dialogue: 0,0:04:08.00,0:04:10.00,Default,,0000,0000,0000,argument-description,Solution to synchronize playback of multiple media player instances over the network.
Dialogue: 0,0:04:10.00,0:04:12.00,Default,,0000,0000,0000,argument-epilog,If no options supplied _config values will be used
Dialogue: 0,0:04:12.00,0:04:14.00,Default,,0000,0000,0000,nogui-argument,show no GUI
Dialogue: 0,0:04:14.00,0:04:16.00,Default,,0000,0000,0000,host-argument,server's address
Dialogue: 0,0:04:16.00,0:04:18.00,Default,,0000,0000,0000,name-argument,desired username
Dialogue: 0,0:04:18.00,0:04:20.00,Default,,0000,0000,0000,debug-argument,debug mode
Dialogue: 0,0:04:20.00,0:04:22.00,Default,,0000,0000,0000,force-gui-prompt-argument,make configuration prompt appear
Dialogue: 0,0:04:22.00,0:04:24.00,Default,,0000,0000,0000,no-store-argument,don't store values in .syncplay
Dialogue: 0,0:04:24.00,0:04:26.00,Default,,0000,0000,0000,room-argument,default room
Dialogue: 0,0:04:26.00,0:04:28.00,Default,,0000,0000,0000,password-argument,server password
Dialogue: 0,0:04:28.00,0:04:30.00,Default,,0000,0000,0000,player-path-argument,path to your player executable
Dialogue: 0,0:04:30.00,0:04:32.00,Default,,0000,0000,0000,file-argument,file to play
Dialogue: 0,0:04:32.00,0:04:34.00,Default,,0000,0000,0000,args-argument,player options, if you need to pass options starting with - prepend them with single '--' argument
Dialogue: 0,0:04:34.00,0:04:36.00,Default,,0000,0000,0000,clear-gui-data-argument,resets path and window state GUI data stored as QSettings
Dialogue: 0,0:04:36.00,0:04:38.00,Default,,0000,0000,0000,language-argument,language for Syncplay messages (de/en/ru/it/es/pt_BR/pt_PT/tr)
Dialogue: 0,0:04:38.00,0:04:40.00,Default,,0000,0000,0000,version-argument,prints your version
Dialogue: 0,0:04:40.00,0:04:42.00,Default,,0000,0000,0000,version-message,You're using Syncplay version {} ({})
Dialogue: 0,0:04:42.00,0:04:44.00,Default,,0000,0000,0000,load-playlist-from-file-argument,loads playlist from text file (one entry per line)
Dialogue: 0,0:04:44.00,0:04:46.00,Default,,0000,0000,0000,config-window-title,Syncplay configuration
Dialogue: 0,0:04:46.00,0:04:48.00,Default,,0000,0000,0000,connection-group-title,Connection settings
Dialogue: 0,0:04:48.00,0:04:50.00,Default,,0000,0000,0000,host-label,Server address:
Dialogue: 0,0:04:50.00,0:04:52.00,Default,,0000,0000,0000,name-label,Username (optional):
Dialogue: 0,0:04:52.00,0:04:54.00,Default,,0000,0000,0000,password-label,Server password (if any):
Dialogue: 0,0:04:54.00,0:04:56.00,Default,,0000,0000,0000,room-label,Default room:
Dialogue: 0,0:04:56.00,0:04:58.00,Default,,0000,0000,0000,roomlist-msgbox-label,Edit room list (one per line)
Dialogue: 0,0:04:58.00,0:05:00.00,Default,,0000,0000,0000,media-setting-title,Media player settings
Dialogue: 0,0:05:00.00,0:05:02.00,Default,,0000,0000,0000,executable-path-label,Path to media player:
Dialogue: 0,0:05:02.00,0:05:04.00,Default,,0000,0000,0000,media-path-label,Path to video (optional):
Dialogue: 0,0:05:04.00,0:05:06.00,Default,,0000,0000,0000,player-arguments-label,Player arguments (if any):
Dialogue: 0,0:05:06.00,0:05:08.00,Default,,0000,0000,0000,browse-label,Browse
Dialogue: 0,0:05:08.00,0:05:10.00,Default,,0000,0000,0000,update-server-list-label,Update list
Dialogue: 0,0:05:10.00,0:05:12.00,Default,,0000,0000,0000,more-title,Show more settings
Dialogue: 0,0:05:12.00,0:05:14.00,Default,,0000,0000,0000,never-rewind-value,Never
Dialogue: 0,0:05:14.00,0:05:16.00,Default,,0000,0000,0000,seconds-suffix, secs
Dialogue: 0,0:05:16.00,0:05:18.00,Default,,0000,0000,0000,privacy-sendraw-option,Send raw
Dialogue: 0,0:05:18.00,0:05:20.00,Default,,0000,0000,0000,privacy-sendhashed-option,Send hashed
Dialogue: 0,0:05:20.00,0:05:22.00,Default,,0000,0000,0000,privacy-dontsend-option,Don't send
Dialogue: 0,0:05:22.00,0:05:24.00,Default,,0000,0000,0000,filename-privacy-label,Filename information:
Dialogue: 0,0:05:24.00,0:05:26.00,Default,,0000,0000,0000,filesize-privacy-label,File size information:
Dialogue: 0,0:05:26.00,0:05:28.00,Default,,0000,0000,0000,checkforupdatesautomatically-label,Check for Syncplay updates automatically
Dialogue: 0,0:05:28.00,0:05:30.00,Default,,0000,0000,0000,autosavejoinstolist-label,Add rooms you join to the room list
Dialogue: 0,0:05:30.00,0:05:32.00,Default,,0000,0000,0000,slowondesync-label,Slow down on minor desync (not supported on MPC-HC/BE)
Dialogue: 0,0:05:32.00,0:05:34.00,Default,,0000,0000,0000,rewindondesync-label,Rewind on major desync (recommended)
Dialogue: 0,0:05:34.00,0:05:36.00,Default,,0000,0000,0000,fastforwardondesync-label,Fast-forward if lagging behind (recommended)
Dialogue: 0,0:05:36.00,0:05:38.00,Default,,0000,0000,0000,dontslowdownwithme-label,Never slow down or rewind others (experimental)
Dialogue: 0,0:05:38.00,0:05:40.00,Default,,0000,0000,0000,pausing-title,Pausing
Dialogue: 0,0:05:40.00,0:05:42.00,Default,,0000,0000,0000,pauseonleave-label,Pause when user leaves (e.g. if they are disconnected)
Dialogue: 0,0:05:42.00,0:05:44.00,Default,,0000,0000,0000,readiness-title,Initial readiness state
Dialogue: 0,0:05:44.00,0:05:46.00,Default,,0000,0000,0000,readyatstart-label,Set me as 'ready to watch' by default
Dialogue: 0,0:05:46.00,0:05:48.00,Default,,0000,0000,0000,forceguiprompt-label,Don't always show the Syncplay configuration window
Dialogue: 0,0:05:48.00,0:05:50.00,Default,,0000,0000,0000,showosd-label,Enable OSD Messages
Dialogue: 0,0:05:50.00,0:05:52.00,Default,,0000,0000,0000,showosdwarnings-label,Include warnings (e.g. when files are different, users not ready)
Dialogue: 0,0:05:52.00,0:05:54.00,Default,,0000,0000,0000,showsameroomosd-label,Include events in your room
Dialogue: 0,0:05:54.00,0:05:56.00,Default,,0000,0000,0000,shownoncontrollerosd-label,Include events from non-operators in managed rooms
Dialogue: 0,0:05:56.00,0:05:58.00,Default,,0000,0000,0000,showdifferentroomosd-label,Include events in other rooms
Dialogue: 0,0:05:58.00,0:06:00.00,Default,,0000,0000,0000,showslowdownosd-label,Include slowing down / reverting notifications
Dialogue: 0,0:06:00.00,0:06:02.00,Default,,0000,0000,0000,language-label,Language:
Dialogue: 0,0:06:02.00,0:06:04.00,Default,,0000,0000,0000,automatic-language,Default ({})
Dialogue: 0,0:06:04.00,0:06:06.00,Default,,0000,0000,0000,showdurationnotification-label,Warn about media duration mismatches
Dialogue: 0,0:06:06.00,0:06:08.00,Default,,0000,0000,0000,basics-label,Basics
Dialogue: 0,0:06:08.00,0:06:10.00,Default,,0000,0000,0000,readiness-label,Play/Pause
Dialogue: 0,0:06:10.00,0:06:12.00,Default,,0000,0000,0000,misc-label,Misc
Dialogue: 0,0:06:12.00,0:06:14.00,Default,,0000,0000,0000,core-behaviour-title,Core room behaviour
Dialogue: 0,0:06:14.00,0:06:16.00,Default,,0000,0000,0000,syncplay-internals-title,Syncplay internals
Dialogue: 0,0:06:16.00,0:06:18.00,Default,,0000,0000,0000,syncplay-mediasearchdirectories-title,Directories to search for media
Dialogue: 0,0:06:18.00,0:06:20.00,Default,,0000,0000,0000,syncplay-mediasearchdirectories-label,Directories to search for media (one path per line)
Dialogue: 0,0:06:20.00,0:06:22.00,Default,,0000,0000,0000,sync-label,Sync
Dialogue: 0,0:06:22.00,0:06:24.00,Default,,0000,0000,0000,sync-otherslagging-title,If others are lagging behind...
Dialogue: 0,0:06:24.00,0:06:26.00,Default,,0000,0000,0000,sync-youlaggging-title,If you are lagging behind...
Dialogue: 0,0:06:26.00,0:06:28.00,Default,,0000,0000,0000,messages-label,Messages
Dialogue: 0,0:06:28.00,0:06:30.00,Default,,0000,0000,0000,messages-osd-title,On-screen Display settings
Dialogue: 0,0:06:30.00,0:06:32.00,Default,,0000,0000,0000,messages-other-title,Other display settings
Dialogue: 0,0:06:32.00,0:06:34.00,Default,,0000,0000,0000,chat-label,Chat
Dialogue: 0,0:06:34.00,0:06:36.00,Default,,0000,0000,0000,privacy-label,Privacy
Dialogue: 0,0:06:36.00,0:06:38.00,Default,,0000,0000,0000,privacy-title,Privacy settings
Dialogue: 0,0:06:38.00,0:06:40.00,Default,,0000,0000,0000,unpause-title,If you press play, set as ready and:
Dialogue: 0,0:06:40.00,0:06:42.00,Default,,0000,0000,0000,unpause-ifalreadyready-option,Unpause if already set as ready
Dialogue: 0,0:06:42.00,0:06:44.00,Default,,0000,0000,0000,unpause-ifothersready-option,Unpause if already ready or others in room are ready (default)
Dialogue: 0,0:06:44.00,0:06:46.00,Default,,0000,0000,0000,unpause-ifminusersready-option,Unpause if already ready or if all others ready and min users ready
Dialogue: 0,0:06:46.00,0:06:48.00,Default,,0000,0000,0000,unpause-always,Always unpause
Dialogue: 0,0:06:48.00,0:06:50.00,Default,,0000,0000,0000,syncplay-trusteddomains-title,Trusted domains (for streaming services and hosted content)
Dialogue: 0,0:06:50.00,0:06:52.00,Default,,0000,0000,0000,chat-title,Chat message input
Dialogue: 0,0:06:52.00,0:06:54.00,Default,,0000,0000,0000,chatinputenabled-label,Enable chat input via mpv
Dialogue: 0,0:06:54.00,0:06:56.00,Default,,0000,0000,0000,chatdirectinput-label,Allow instant chat input (bypass having to press enter key to chat)
Dialogue: 0,0:06:56.00,0:06:58.00,Default,,0000,0000,0000,chatinputfont-label,Chat input font
Dialogue: 0,0:06:58.00,0:07:00.00,Default,,0000,0000,0000,chatfont-label,Set font
Dialogue: 0,0:07:00.00,0:07:02.00,Default,,0000,0000,0000,chatcolour-label,Set colour
Dialogue: 0,0:07:02.00,0:07:04.00,Default,,0000,0000,0000,chatinputposition-label,Position of message input area in mpv
Dialogue: 0,0:07:04.00,0:07:06.00,Default,,0000,0000,0000,chat-top-option,Top
Dialogue: 0,0:07:06.00,0:07:08.00,Default,,0000,0000,0000,chat-middle-option,Middle
Dialogue: 0,0:07:08.00,0:07:10.00,Default,,0000,0000,0000,chat-bottom-option,Bottom
Dialogue: 0,0:07:10.00,0:07:12.00,Default,,0000,0000,0000,chatoutputheader-label,Chat message output
Dialogue: 0,0:07:12.00,0:07:14.00,Default,,0000,0000,0000,chatoutputfont-label,Chat output font
Dialogue: 0,0:07:14.00,0:07:16.00,Default,,0000,0000,0000,chatoutputenabled-label,Enable chat output in media player (mpv only for now)
Dialogue: 0,0:07:16.00,0:07:18.00,Default,,0000,0000,0000,chatoutputposition-label,Output mode
Dialogue: 0,0:07:18.00,0:07:20.00,Default,,0000,0000,0000,chat-chatroom-option,Chatroom style
Dialogue: 0,0:07:20.00,0:07:22.00,Default,,0000,0000,0000,chat-scrolling-option,Scrolling style
Dialogue: 0,0:07:22.00,0:07:24.00,Default,,0000,0000,0000,mpv-key-tab-hint,[TAB] to toggle access to alphabet row key shortcuts.
Dialogue: 0,0:07:24.00,0:07:26.00,Default,,0000,0000,0000,mpv-key-hint,[ENTER] to send message. [ESC] to escape chat mode.
Dialogue: 0,0:07:26.00,0:07:28.00,Default,,0000,0000,0000,alphakey-mode-warning-first-line,You can temporarily use old mpv bindings with a-z keys.
Dialogue: 0,0:07:28.00,0:07:30.00,Default,,0000,0000,0000,alphakey-mode-warning-second-line,Press [TAB] to return to Syncplay chat mode.
Dialogue: 0,0:07:30.00,0:07:32.00,Default,,0000,0000,0000,help-label,Help
Dialogue: 0,0:07:32.00,0:07:34.00,Default,,0000,0000,0000,reset-label,Restore defaults
Dialogue: 0,0:07:34.00,0:07:36.00,Default,,0000,0000,0000,run-label,Run Syncplay
Dialogue: 0,0:07:36.00,0:07:38.00,Default,,0000,0000,0000,storeandrun-label,Store configuration and run Syncplay
Dialogue: 0,0:07:38.00,0:07:40.00,Default,,0000,0000,0000,contact-label,Feel free to e-mail <a href="mailto:dev@syncplay.pl"><nobr>dev@syncplay.pl</nobr></a>, <a href="https://github.com/Syncplay/syncplay/issues"><nobr>create an issue</nobr></a> to report a bug/problem via GitHub, <a href="https://github.com/Syncplay/syncplay/discussions"><nobr>start a discussion</nobr></a> to make a suggestion or ask a question via GitHub, <a href="https://www.facebook.com/SyncplaySoftware"><nobr>like us on Facebook</nobr></a>, <a href="https://twitter.com/Syncplay/"><nobr>follow us on Twitter</nobr></a>, or visit <a href="https://syncplay.pl/"><nobr>https://syncplay.pl/</nobr></a>. Do not use Syncplay to send sensitive information.
Dialogue: 0,0:07:40.00,0:07:42.00,Default,,0000,0000,0000,joinroom-label,Join room
Dialogue: 0,0:07:42.00,0:07:44.00,Default,,0000,0000,0000,joinroom-menu-label,Join room {}
Dialogue: 0,0:07:44.00,0:07:46.00,Default,,0000,0000,0000,seektime-menu-label,Seek to time
Dialogue: 0,0:07:46.00,0:07:48.00,Default,,0000,0000,0000,undoseek-menu-label,Undo seek
Dialogue: 0,0:07:48.00,0:07:50.00,Default,,0000,0000,0000,play-menu-label,Play
Dialogue: 0,0:07:50.00,0:07:52.00,Default,,0000,0000,0000,pause-menu-label,Pause
Dialogue: 0,0:07:52.00,0:07:54.00,Default,,0000,0000,0000,playbackbuttons-menu-label,Show playback buttons
Dialogue: 0,0:07:54.00,0:07:56.00,Default,,0000,0000,0000,autoplay-menu-label,Show auto-play button
Dialogue: 0,0:07:56.00,0:07:58.00,Default,,0000,0000,0000,autoplay-guipushbuttonlabel,Play when all ready
Dialogue: 0,0:07:58.00,0:08:00.00,Default,,0000,0000,0000,autoplay-minimum-label,Min users:
Dialogue: 0,0:08:00.00,0:08:02.00,Default,,0000,0000,0000,sendmessage-label,Send
Dialogue: 0,0:08:02.00,0:08:04.00,Default,,0000,0000,0000,ready-guipushbuttonlabel,I'm ready to watch!
Dialogue: 0,0:08:04.00,0:08:06.00,Default,,0000,0000,0000,roomuser-heading-label,Room / User
Dialogue: 0,0:08:06.00,0:08:08.00,Default,,0000,0000,0000,size-heading-label,Size
Dialogue: 0,0:08:08.00,0:08:10.00,Default,,0000,0000,0000,duration-heading-label,Length
Dialogue: 0,0:08:10.00,0:08:12.00,Default,,0000,0000,0000,filename-heading-label,Filename
Dialogue: 0,0:08:12.00,0:08:14.00,Default,,0000,0000,0000,notifications-heading-label,Notifications
Dialogue: 0,0:08:14.00,0:08:16.00,Default,,0000,0000,0000,userlist-heading-label,List of who is playing what
Dialogue: 0,0:08:16.00,0:08:18.00,Default,,0000,0000,0000,browseformedia-label,Browse for media files
Dialogue: 0,0:08:18.00,0:08:20.00,Default,,0000,0000,0000,file-menu-label,&File
Dialogue: 0,0:08:20.00,0:08:22.00,Default,,0000,0000,0000,openmedia-menu-label,&Open media file
Dialogue: 0,0:08:22.00,0:08:24.00,Default,,0000,0000,0000,openstreamurl-menu-label,Open &media stream URL
Dialogue: 0,0:08:24.00,0:08:26.00,Default,,0000,0000,0000,setmediadirectories-menu-label,Set media &directories
Dialogue: 0,0:08:26.00,0:08:28.00,Default,,0000,0000,0000,loadplaylistfromfile-menu-label,&Load playlist from file
Dialogue: 0,0:08:28.00,0:08:30.00,Default,,0000,0000,0000,saveplaylisttofile-menu-label,&Save playlist to file
Dialogue: 0,0:08:30.00,0:08:32.00,Default,,0000,0000,0000,exit-menu-label,E&xit
Dialogue: 0,0:08:32.00,0:08:34.00,Default,,0000,0000,0000,advanced-menu-label,&Advanced
Dialogue: 0,0:08:34.00,0:08:36.00,Default,,0000,0000,0000,window-menu-label,&Window
Dialogue: 0,0:08:36.00,0:08:38.00,Default,,0000,0000,0000,setoffset-menu-label,Set &offset
Dialogue: 0,0:08:38.00,0:08:40.00,Default,,0000,0000,0000,createcontrolledroom-menu-label,&Create managed room
Dialogue: 0,0:08:40.00,0:08:42.00,Default,,0000,0000,0000,identifyascontroller-menu-label,&Identify as room operator
Dialogue: 0,0:08:42.00,0:08:44.00,Default,,0000,0000,0000,settrusteddomains-menu-label,Set &trusted domains
Dialogue: 0,0:08:44.00,0:08:46.00,Default,,0000,0000,0000,addtrusteddomain-menu-label,Add {} as trusted domain
Dialogue: 0,0:08:46.00,0:08:48.00,Default,,0000,0000,0000,edit-menu-label,&Edit
Dialogue: 0,0:08:48.00,0:08:50.00,Default,,0000,0000,0000,cut-menu-label,Cu&t
Dialogue: 0,0:08:50.00,0:08:52.00,Default,,0000,0000,0000,copy-menu-label,&Copy
Dialogue: 0,0:08:52.00,0:08:54.00,Default,,0000,0000,0000,paste-menu-label,&Paste
Dialogue: 0,0:08:54.00,0:08:56.00,Default,,0000,0000,0000,selectall-menu-label,&Select All
Dialogue: 0,0:08:56.00,0:08:58.00,Default,,0000,0000,0000,playback-menu-label,&Playback
Dialogue: 0,0:08:58.00,0:09:00.00,Default,,0000,0000,0000,help-menu-label,&Help
Dialogue: 0,0:09:00.00,0:09:02.00,Default,,0000,0000,0000,userguide-menu-label,Open user &guide
Dialogue: 0,0:09:02.00,0:09:04.00,Default,,0000,0000,0000,update-menu-label,Check for &update
Dialogue: 0,0:09:04.00,0:09:06.00,Default,,0000,0000,0000,startTLS-initiated,Attempting secure connection
Dialogue: 0,0:09:06.00,0:09:08.00,Default,,0000,0000,0000,startTLS-secure-connection-ok,Secure connection established ({})
Dialogue: 0,0:09:08.00,0:09:10.00,Default,,0000,0000,0000,startTLS-server-certificate-invalid,Secure connection failed. The server uses an invalid security certificate. This communication could be intercepted by a third party. For further details and troubleshooting see <a href="https://syncplay.pl/trouble">here</a>.
Dialogue: 0,0:09:10.00,0:09:12.00,Default,,0000,0000,0000,startTLS-server-certificate-invalid-DNS-ID,Syncplay does not trust this server because it uses a certificate that is not valid for its hostname.
Dialogue: 0,0:09:12.00,0:09:14.00,Default,,0000,0000,0000,startTLS-not-supported-client,This client does not support TLS
Dialogue: 0,0:09:14.00,0:09:16.00,Default,,0000,0000,0000,startTLS-not-supported-server,This server does not support TLS
Dialogue: 0,0:09:16.00,0:09:18.00,Default,,0000,0000,0000,tls-information-title,Certificate Details
Dialogue: 0,0:09:18.00,0:09:20.00,Default,,0000,0000,0000,tls-dialog-status-label,<strong>Syncplay is using an encrypted connection to {}.</strong>
Dialogue: 0,0:09:20.00,0:09:22.00,Default,,0000,0000,0000,tls-dialog-desc-label,Encryption with a digital certificate keeps information private as it is sent to or from the<br/>server {}.
Dialogue: 0,0:09:22.00,0:09:24.00,Default,,0000,0000,0000,tls-dialog-connection-label,Information encrypted using Transport Layer Security (TLS), version {} with the cipher<br/>suite: {}.
Dialogue: 0,0:09:24.00,0:09:26.00,Default,,0000,0000,0000,tls-dialog-certificate-label,Certificate issued by {} valid until {}.
Dialogue: 0,0:09:26.00,0:09:28.00,Default,,0000,0000,0000,about-menu-label,&About Syncplay
Dialogue: 0,0:09:28.00,0:09:30.00,Default,,0000,0000,0000,about-dialog-title,About Syncplay
Dialogue: 0,0:09:30.00,0:09:32.00,Default,,0000,0000,0000,about-dialog-release,Version {} release {}
Dialogue: 0,0:09:32.00,0:09:34.00,Default,,0000,0000,0000,about-dialog-license-text,Licensed under the Apache&nbsp;License,&nbsp;Version 2.0
Dialogue: 0,0:09:34.00,0:09:36.00,Default,,0000,0000,0000,about-dialog-license-button,License
Dialogue: 0,0:09:36.00,0:09:38.00,Default,,0000,0000,0000,about-dialog-dependencies,Dependencies
Dialogue: 0,0:09:38.00,0:09:40.00,Default,,0000,0000,0000,setoffset-msgbox-label,Set offset
Dialogue: 0,0:09:40.00,0:09:42.00,Default,,0000,0000,0000,offsetinfo-msgbox-label,Offset (see https://syncplay.pl/guide/ for usage instructions):
Dialogue: 0,0:09:42.00,0:09:44.00,Default,,0000,0000,0000,promptforstreamurl-msgbox-label,Open media stream URL
Dialogue: 0,0:09:44.00,0:09:46.00,Default,,0000,0000,0000,promptforstreamurlinfo-msgbox-label,Stream URL
Dialogue: 0,0:09:46.00,0:09:48.00,Default,,0000,0000,0000,addfolder-label,Add folder
Dialogue: 0,0:09:48.00,0:09:50.00,Default,,0000,0000,0000,adduris-msgbox-label,Add URLs to playlist (one per line)
Dialogue: 0,0:09:50.00,0:09:52.00,Default,,0000,0000,0000,editplaylist-msgbox-label,Set playlist (one per line)
Dialogue: 0,0:09:52.00,0:09:54.00,Default,,0000,0000,0000,trusteddomains-msgbox-label,Domains it is okay to automatically switch to (one per line)
Dialogue: 0,0:09:54.00,0:09:56.00,Default,,0000,0000,0000,createcontrolledroom-msgbox-label,Create managed room
Dialogue: 0,0:09:56.00,0:09:58.00,Default,,0000,0000,0000,controlledroominfo-msgbox-label,Enter name of managed room
(see https://syncplay.pl/guide/ for usage instructions):
Dialogue: 0,0:09:58.00,0:10:00.00,Default,,0000,0000,0000,identifyascontroller-msgbox-label,Identify as room operator
Dialogue: 0,0:10:00.00,0:10:02.00,Default,,0000,0000,0000,identifyinfo-msgbox-label,Enter operator password for this room
(see https://syncplay.pl/guide/ for usage instructions):
Dialogue: 0,0:10:02.00,0:10:04.00,Default,,0000,0000,0000,public-server-msgbox-label,Select the public server for this viewing session
Dialogue: 0,0:10:04.00,0:10:06.00,Default,,0000,0000,0000,megabyte-suffix, MB
Dialogue: 0,0:10:06.00,0:10:08.00,Default,,0000,0000,0000,host-tooltip,Hostname or IP to connect to, optionally including port (e.g. syncplay.pl:8999). Only synchronised with people on same server/port.
Dialogue: 0,0:10:08.00,0:10:10.00,Default,,0000,0000,0000,name-tooltip,Nickname you will be known by. No registration, so can easily change later. Random name generated if none specified.
Dialogue: 0,0:10:10.00,0:10:12.00,Default,,0000,0000,0000,password-tooltip,Passwords are only needed for connecting to private servers.
Dialogue: 0,0:10:12.00,0:10:14.00,Default,,0000,0000,0000,room-tooltip,Room to join upon connection can be almost anything, but you will only be synchronised with people in the same room.
Dialogue: 0,0:10:14.00,0:10:16.00,Default,,0000,0000,0000,edit-rooms-tooltip,Edit room list.
Dialogue: 0,0:10:16.00,0:10:18.00,Default,,0000,0000,0000,executable-path-tooltip,Location of your chosen supported media player (mpv, mpv.net, VLC, MPC-HC/BE, mplayer2 or IINA).
Dialogue: 0,0:10:18.00,0:10:20.00,Default,,0000,0000,0000,media-path-tooltip,Location of video or stream to be opened. Necessary for mplayer2.
Dialogue: 0,0:10:20.00,0:10:22.00,Default,,0000,0000,0000,player-arguments-tooltip,Additional command line arguments / switches to pass on to this media player.
Dialogue: 0,0:10:22.00,0:10:24.00,Default,,0000,0000,0000,mediasearcdirectories-arguments-tooltip,Directories where Syncplay will search for media files, e.g. when you are using the click to switch feature. Syncplay will look recursively through sub-folders.
Dialogue: 0,0:10:24.00,0:10:26.00,Default,,0000,0000,0000,more-tooltip,Display less frequently used settings.
Dialogue: 0,0:10:26.00,0:10:28.00,Default,,0000,0000,0000,filename-privacy-tooltip,Privacy mode for sending currently playing filename to server.
Dialogue: 0,0:10:28.00,0:10:30.00,Default,,0000,0000,0000,filesize-privacy-tooltip,Privacy mode for sending size of currently playing file to server.
Dialogue: 0,0:10:30.00,0:10:32.00,Default,,0000,0000,0000,privacy-sendraw-tooltip,Send this information without obfuscation. This is the default option with most functionality.
Dialogue: 0,0:10:32.00,0:10:34.00,Default,,0000,0000,0000,privacy-sendhashed-tooltip,Send a hashed version of the information, making it less visible to other clients.
Dialogue: 0,0:10:34.00,0:10:36.00,Default,,0000,0000,0000,privacy-dontsend-tooltip,Do not send this information to the server. This provides for maximum privacy.
Dialogue: 0,0:10:36.00,0:10:38.00,Default,,0000,0000,0000,checkforupdatesautomatically-tooltip,Regularly check with the Syncplay website to see whether a new version of Syncplay is available.
Dialogue: 0,0:10:38.00,0:10:40.00,Default,,0000,0000,0000,autosavejoinstolist-tooltip,When you join a room in a server, automatically remember the room name in the list of rooms to join.
Dialogue: 0,0:10:40.00,0:10:42.00,Default,,0000,0000,0000,slowondesync-tooltip,Reduce playback rate temporarily when needed to bring you back in sync with other viewers. Not supported on MPC-HC/BE.
Dialogue: 0,0:10:42.00,0:10:44.00,Default,,0000,0000,0000,dontslowdownwithme-tooltip,Means others do not get slowed down or rewinded if your playback is lagging. Useful for room operators.
Dialogue: 0,0:10:44.00,0:10:46.00,Default,,0000,0000,0000,pauseonleave-tooltip,Pause playback if you get disconnected or someone leaves from your room.
Dialogue: 0,0:10:46.00,0:10:48.00,Default,,0000,0000,0000,readyatstart-tooltip,Set yourself as 'ready' at start (otherwise you are set as 'not ready' until you change your readiness state)
Dialogue: 0,0:10:48.00,0:10:50.00,Default,,0000,0000,0000,forceguiprompt-tooltip,Configuration dialogue is not shown when opening a file with Syncplay.
Dialogue: 0,0:10:50.00,0:10:52.00,Default,,0000,0000,0000,nostore-tooltip,Run Syncplay with the given configuration, but do not permanently store the changes.
Dialogue: 0,0:10:52.00,0:10:54.00,Default,,0000,0000,0000,rewindondesync-tooltip,Jump back when needed to get back in sync. Disabling this option can result in major desyncs!
Dialogue: 0,0:10:54.00,0:10:56.00,Default,,0000,0000,0000,fastforwardondesync-tooltip,Jump forward when out of sync with room operator (or your pretend position if 'Never slow down or rewind others' enabled).
Dialogue: 0,0:10:56.00,0:10:58.00,Default,,0000,0000,0000,showosd-tooltip,Sends Syncplay messages to media player OSD.
Dialogue: 0,0:10:58.00,0:11:00.00,Default,,0000,0000,0000,showosdwarnings-tooltip,Show warnings if playing different file, alone in room, users not ready, etc.
Dialogue: 0,0:11:00.00,0:11:02.00,Default,,0000,0000,0000,showsameroomosd-tooltip,Show OSD notifications for events relating to room user is in.
Dialogue: 0,0:11:02.00,0:11:04.00,Default,,0000,0000,0000,shownoncontrollerosd-tooltip,Show OSD notifications for events relating to non-operators who are in managed rooms.
Dialogue: 0,0:11:04.00,0:11:06.00,Default,,0000,0000,0000,showdifferentroomosd-tooltip,Show OSD notifications for events relating to room user is not in.
Dialogue: 0,0:11:06.00,0:11:08.00,Default,,0000,0000,0000,showslowdownosd-tooltip,Show notifications of slowing down / reverting on time difference.
Dialogue: 0,0:11:08.00,0:11:10.00,Default,,0000,0000,0000,showdurationnotification-tooltip,Useful for when a segment in a multi-part file is missing, but can result in false positives.
Dialogue: 0,0:11:10.00,0:11:12.00,Default,,0000,0000,0000,language-tooltip,Language to be used by Syncplay.
Dialogue: 0,0:11:12.00,0:11:14.00,Default,,0000,0000,0000,unpause-always-tooltip,If you press unpause it always sets you as ready and unpause, rather than just setting you as ready.
Dialogue: 0,0:11:14.00,0:11:16.00,Default,,0000,0000,0000,unpause-ifalreadyready-tooltip,If you press unpause when not ready it will set you as ready - press unpause again to unpause.
Dialogue: 0,0:11:16.00,0:11:18.00,Default,,0000,0000,0000,unpause-ifothersready-tooltip,If you press unpause when not ready, it will only unpause if others are ready.
Dialogue: 0,0:11:18.00,0:11:20.00,Default,,0000,0000,0000,unpause-ifminusersready-tooltip,If you press unpause when not ready, it will only unpause if others are ready and minimum users threshold is met.
Dialogue: 0,0:11:20.00,0:11:22.00,Default,,0000,0000,0000,trusteddomains-arguments-tooltip,Domains that it is okay for Syncplay to automatically switch to when shared playlists is enabled.
Dialogue: 0,0:11:22.00,0:11:24.00,Default,,0000,0000,0000,chatinputenabled-tooltip,Enable chat input in mpv (press enter to chat, enter to send, escape to cancel)
Dialogue: 0,0:11:24.00,0:11:26.00,Default,,0000,0000,0000,chatdirectinput-tooltip,Skip having to press 'enter' to go into chat input mode in mpv. Press TAB in mpv to temporarily disable this feature.
Dialogue: 0,0:11:26.00,0:11:28.00,Default,,0000,0000,0000,font-label-tooltip,Font used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.
Dialogue: 0,0:11:28.00,0:11:30.00,Default,,0000,0000,0000,set-input-font-tooltip,Font family used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.
Dialogue: 0,0:11:30.00,0:11:32.00,Default,,0000,0000,0000,set-input-colour-tooltip,Font colour used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.
Dialogue: 0,0:11:32.00,0:11:34.00,Default,,0000,0000,0000,chatinputposition-tooltip,Location in mpv where chat input text will appear when you press enter and type.
Dialogue: 0,0:11:34.00,0:11:36.00,Default,,0000,0000,0000,chatinputposition-top-tooltip,Place chat input at top of mpv window.
Dialogue: 0,0:11:36.00,0:11:38.00,Default,,0000,0000,0000,chatinputposition-middle-tooltip,Place chat input in dead centre of mpv window.
Dialogue: 0,0:11:38.00,0:11:40.00,Default,,0000,0000,0000,chatinputposition-bottom-tooltip,Place chat input at bottom of mpv window.
Dialogue: 0,0:11:40.00,0:11:42.00,Default,,0000,0000,0000,chatoutputenabled-tooltip,Show chat messages in OSD (if supported by media player).
Dialogue: 0,0:11:42.00,0:11:44.00,Default,,0000,0000,0000,font-output-label-tooltip,Chat output font.
Dialogue: 0,0:11:44.00,0:11:46.00,Default,,0000,0000,0000,set-output-font-tooltip,Font used for when displaying chat messages.
Dialogue: 0,0:11:46.00,0:11:48.00,Default,,0000,0000,0000,chatoutputmode-tooltip,How chat messages are displayed.
Dialogue: 0,0:11:48.00,0:11:50.00,Default,,0000,0000,0000,chatoutputmode-chatroom-tooltip,Display new lines of chat directly below previous line.
Dialogue: 0,0:11:50.00,0:11:52.00,Default,,0000,0000,0000,chatoutputmode-scrolling-tooltip,Scroll chat text from right to left.
Dialogue: 0,0:11:52.00,0:11:54.00,Default,,0000,0000,0000,help-tooltip,Opens the Syncplay.pl user guide.
Dialogue: 0,0:11:54.00,0:11:56.00,Default,,0000,0000,0000,reset-tooltip,Reset all settings to the default configuration.
Dialogue: 0,0:11:56.00,0:11:58.00,Default,,0000,0000,0000,update-server-list-tooltip,Connect to syncplay.pl to update list of public servers.
Dialogue: 0,0:11:58.00,0:12:00.00,Default,,0000,0000,0000,sslconnection-tooltip,Securely connected to server. Click for certificate details.
Dialogue: 0,0:12:00.00,0:12:02.00,Default,,0000,0000,0000,joinroom-tooltip,Leave current room and joins specified room.
Dialogue: 0,0:12:02.00,0:12:04.00,Default,,0000,0000,0000,seektime-msgbox-label,Jump to specified time (in seconds / min:sec). Use +/- for relative seek.
Dialogue: 0,0:12:04.00,0:12:06.00,Default,,0000,0000,0000,ready-tooltip,Indicates whether you are ready to watch.
Dialogue: 0,0:12:06.00,0:12:08.00,Default,,0000,0000,0000,autoplay-tooltip,Auto-play when all users who have readiness indicator are ready and minimum user threshold met.
Dialogue: 0,0:12:08.00,0:12:10.00,Default,,0000,0000,0000,switch-to-file-tooltip,Double click to switch to {}
Dialogue: 0,0:12:10.00,0:12:12.00,Default,,0000,0000,0000,sendmessage-tooltip,Send message to room
Dialogue: 0,0:12:12.00,0:12:14.00,Default,,0000,0000,0000,differentsize-note,Different size!
Dialogue: 0,0:12:14.00,0:12:16.00,Default,,0000,0000,0000,differentsizeandduration-note,Different size and duration!
Dialogue: 0,0:12:16.00,0:12:18.00,Default,,0000,0000,0000,differentduration-note,Different duration!
Dialogue: 0,0:12:18.00,0:12:20.00,Default,,0000,0000,0000,nofile-note,(No file being played)
Dialogue: 0,0:12:20.00,0:12:22.00,Default,,0000,0000,0000,new-syncplay-available-motd-message,You are using Syncplay {} but a newer version is available from https://syncplay.pl
Dialogue: 0,0:12:22.00,0:12:24.00,Default,,0000,0000,0000,welcome-server-notification,Welcome to Syncplay server, ver. {0}
Dialogue: 0,0:12:24.00,0:12:26.00,Default,,0000,0000,0000,client-connected-room-server-notification,{0}({2}) connected to room '{1}'
Dialogue: 0,0:12:26.00,0:12:28.00,Default,,0000,0000,0000,client-left-server-notification,{0} left server
Dialogue: 0,0:12:28.00,0:12:30.00,Default,,0000,0000,0000,no-salt-notification,PLEASE NOTE: To allow room operator passwords generated by this server instance to still work when the server is restarted, please add the following command line argument when running the Syncplay server in the future: --salt {}
Dialogue: 0,0:12:30.00,0:12:32.00,Default,,0000,0000,0000,server-argument-description,Solution to synchronize playback of multiple media player instances over the network. Server instance
Dialogue: 0,0:12:32.00,0:12:34.00,Default,,0000,0000,0000,server-argument-epilog,If no options supplied _config values will be used
Dialogue: 0,0:12:34.00,0:12:36.00,Default,,0000,0000,0000,server-port-argument,server TCP port
Dialogue: 0,0:12:36.00,0:12:38.00,Default,,0000,0000,0000,server-password-argument,server password
Dialogue: 0,0:12:38.00,0:12:40.00,Default,,0000,0000,0000,server-isolate-room-argument,should rooms be isolated?
Dialogue: 0,0:12:40.00,0:12:42.00,Default,,0000,0000,0000,server-salt-argument,random string used to generate managed room passwords
Dialogue: 0,0:12:42.00,0:12:44.00,Default,,0000,0000,0000,server-disable-ready-argument,disable readiness feature
Dialogue: 0,0:12:44.00,0:12:46.00,Default,,0000,0000,0000,server-motd-argument,path to file from which motd will be fetched
Dialogue: 0,0:12:46.00,0:12:48.00,Default,,0000,0000,0000,server-chat-argument,Should chat be disabled?
Dialogue: 0,0:12:48.00,0:12:50.00,Default,,0000,0000,0000,server-chat-maxchars-argument,Maximum number of characters in a chat message (default is {})
Dialogue: 0,0:12:50.00,0:12:52.00,Default,,0000,0000,0000,server-maxusernamelength-argument,Maximum number of characters in a username (default is {})
Dialogue: 0,0:12:52.00,0:12:54.00,Default,,0000,0000,0000,server-stats-db-file-argument,Enable server stats using the SQLite db file provided
Dialogue: 0,0:12:54.00,0:12:56.00,Default,,0000,0000,0000,server-startTLS-argument,Enable TLS connections using the certificate files in the path provided
Dialogue: 0,0:12:56.00,0:12:58.00,Default,,0000,0000,0000,server-messed-up-motd-unescaped-placeholders,Message of the Day has unescaped placeholders. All $ signs should be doubled ($$).
Dialogue: 0,0:12:58.00,0:13:00.00,Default,,0000,0000,0000,server-messed-up-motd-too-long,Message of the Day is too long - maximum of {} chars, {} given.
Dialogue: 0,0:13:00.00,0:13:02.00,Default,,0000,0000,0000,unknown-command-server-error,Unknown command {}
Dialogue: 0,0:13:02.00,0:13:04.00,Default,,0000,0000,0000,not-json-server-error,Not a json encoded string {}
Dialogue: 0,0:13:04.00,0:13:06.00,Default,,0000,0000,0000,line-decode-server-error,Not a utf-8 string
Dialogue: 0,0:13:06.00,0:13:08.00,Default,,0000,0000,0000,not-known-server-error,You must be known to server before sending this command
Dialogue: 0,0:13:08.00,0:13:10.00,Default,,0000,0000,0000,client-drop-server-error,Client drop: {} -- {}
Dialogue: 0,0:13:10.00,0:13:12.00,Default,,0000,0000,0000,password-required-server-error,Password required
Dialogue: 0,0:13:12.00,0:13:14.00,Default,,0000,0000,0000,wrong-password-server-error,Wrong password supplied
Dialogue: 0,0:13:14.00,0:13:16.00,Default,,0000,0000,0000,hello-server-error,Not enough Hello arguments
Dialogue: 0,0:13:16.00,0:13:18.00,Default,,0000,0000,0000,playlist-selection-changed-notification,{} changed the playlist selection
Dialogue: 0,0:13:18.00,0:13:20.00,Default,,0000,0000,0000,playlist-contents-changed-notification,{} updated the playlist
Dialogue: 0,0:13:20.00,0:13:22.00,Default,,0000,0000,0000,cannot-find-file-for-playlist-switch-error,Could not find file {} in media directories for playlist switch!
Dialogue: 0,0:13:22.00,0:13:24.00,Default,,0000,0000,0000,cannot-add-duplicate-error,Could not add second entry for '{}' to the playlist as no duplicates are allowed.
Dialogue: 0,0:13:24.00,0:13:26.00,Default,,0000,0000,0000,cannot-add-unsafe-path-error,Could not automatically load {} because it is not on a trusted domain. You can switch to the URL manually by double clicking it in the playlist, and add trusted domains via File->Advanced->Set Trusted Domains. If you right click on a URL then you can add its domain as a trusted domain via the context menu.
Dialogue: 0,0:13:26.00,0:13:28.00,Default,,0000,0000,0000,sharedplaylistenabled-label,Enable shared playlists
Dialogue: 0,0:13:28.00,0:13:30.00,Default,,0000,0000,0000,removefromplaylist-menu-label,Remove from playlist
Dialogue: 0,0:13:30.00,0:13:32.00,Default,,0000,0000,0000,shuffleremainingplaylist-menu-label,Shuffle remaining playlist
Dialogue: 0,0:13:32.00,0:13:34.00,Default,,0000,0000,0000,shuffleentireplaylist-menu-label,Shuffle entire playlist
Dialogue: 0,0:13:34.00,0:13:36.00,Default,,0000,0000,0000,undoplaylist-menu-label,Undo last change to playlist
Dialogue: 0,0:13:36.00,0:13:38.00,Default,,0000,0000,0000,addfilestoplaylist-menu-label,Add file(s) to bottom of playlist
Dialogue: 0,0:13:38.00,0:13:40.00,Default,,0000,0000,0000,addurlstoplaylist-menu-label,Add URL(s) to bottom of playlist
Dialogue: 0,0:13:40.00,0:13:42.00,Default,,0000,0000,0000,editplaylist-menu-label,Edit playlist
Dialogue: 0,0:13:42.00,0:13:44.00,Default,,0000,0000,0000,open-containing-folder,Open folder containing this file
Dialogue: 0,0:13:44.00,0:13:46.00,Default,,0000,0000,0000,addyourfiletoplaylist-menu-label,Add your file to playlist
Dialogue: 0,0:13:46.00,0:13:48.00,Default,,0000,0000,0000,addotherusersfiletoplaylist-menu-label,Add {}'s file to playlist
Dialogue: 0,0:13:48.00,0:13:50.00,Default,,0000,0000,0000,addyourstreamstoplaylist-menu-label,Add your stream to playlist
Dialogue: 0,0:13:50.00,0:13:52.00,Default,,0000,0000,0000,addotherusersstreamstoplaylist-menu-label,Add {}' stream to playlist
Dialogue: 0,0:13:52.00,0:13:54.00,Default,,0000,0000,0000,openusersstream-menu-label,Open {}'s stream
Dialogue: 0,0:13:54.00,0:13:56.00,Default,,0000,0000,0000,openusersfile-menu-label,Open {}'s file
Dialogue: 0,0:13:56.00,0:13:58.00,Default,,0000,0000,0000,playlist-instruction-item-message,Drag file here to add it to the shared playlist.
Dialogue: 0,0:13:58.00,0:14:00.00,Default,,0000,0000,0000,sharedplaylistenabled-tooltip,Room operators can add files to a synced playlist to make it easy for everyone to watching the same thing. Configure media directories under 'Misc'.
Dialogue: 0,0:14:00.00,0:14:02.00,Default,,0000,0000,0000,playlist-empty-error,Playlist is currently empty.
Dialogue: 0,0:14:02.00,0:14:04.00,Default,,0000,0000,0000,playlist-invalid-index-error,Invalid playlist index

6
syncplay/messages_en.py Executable file → Normal file
View File

@ -35,7 +35,7 @@ en = {
"left-notification": "{} has left", # User "left-notification": "{} has left", # User
"left-paused-notification": "{} left, {} paused", # User who left, User who paused "left-paused-notification": "{} left, {} paused", # User who left, User who paused
"playing-notification": "{} is playing '{}' ({})", # User, file, duration "playing-notification": "{} is playing '{}' ({})", # User, file, duration
"playing-notification/room-addendum": " in room: '{}'", # Room "playing-notification/room-addendum": " in room: '{}'", # Room
"not-all-ready": "Not ready: {}", # Usernames "not-all-ready": "Not ready: {}", # Usernames
"all-users-ready": "Everyone is ready ({} users)", # Number of ready users "all-users-ready": "Everyone is ready ({} users)", # Number of ready users
@ -163,11 +163,11 @@ en = {
"argument-description": 'Solution to synchronize playback of multiple media player instances over the network.', "argument-description": 'Solution to synchronize playback of multiple media player instances over the network.',
"argument-epilog": 'If no options supplied _config values will be used', "argument-epilog": 'If no options supplied _config values will be used',
"nogui-argument": 'show no GUI', "nogui-argument": 'show no GUI',
"host-argument": 'server\'s address', "host-argument": "server's address",
"name-argument": 'desired username', "name-argument": 'desired username',
"debug-argument": 'debug mode', "debug-argument": 'debug mode',
"force-gui-prompt-argument": 'make configuration prompt appear', "force-gui-prompt-argument": 'make configuration prompt appear',
"no-store-argument": 'don\'t store values in .syncplay', "no-store-argument": "don't store values in .syncplay",
"room-argument": 'default room', "room-argument": 'default room',
"password-argument": 'server password', "password-argument": 'server password',
"player-path-argument": 'path to your player executable', "player-path-argument": 'path to your player executable',

441
syncplay/messages_fr.ass Normal file
View File

@ -0,0 +1,441 @@
[Script Info]
; Script generated by Aegisub 3.2.2
; http://www.aegisub.org/
Title: Default Aegisub file
ScriptType: v4.00+
WrapStyle: 0
ScaledBorderAndShadow: yes
YCbCr Matrix: None
[Aegisub Project Garbage]
Scroll Position: 104
Active Line: 122
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Arial,20,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,2,2,2,10,10,10,1
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:00.00,0:00:02.00,Default,,0,0,0,LANGUAGE,Français
Dialogue: 0,0:00:02.00,0:00:04.00,Default,,0,0,0,config-cleared-notification,Paramètres effacés. Les modifications seront enregistrées lorsque vous enregistrez une configuration valide.
Dialogue: 0,0:00:04.00,0:00:06.00,Default,,0,0,0,relative-config-notification,Fichiers de configuration relatifs chargés: {}
Dialogue: 0,0:00:06.00,0:00:08.00,Default,,0,0,0,connection-attempt-notification,Tentative de connexion à {}:{}
Dialogue: 0,0:00:08.00,0:00:10.00,Default,,0,0,0,reconnection-attempt-notification,Connexion avec le serveur perdue, tentative de reconnexion
Dialogue: 0,0:00:10.00,0:00:12.00,Default,,0,0,0,disconnection-notification,Déconnecté du serveur
Dialogue: 0,0:00:12.00,0:00:14.00,Default,,0,0,0,connection-failed-notification,Échec de la connexion avec le serveur
Dialogue: 0,0:00:14.00,0:00:16.00,Default,,0,0,0,connected-successful-notification,Connexion réussie au serveur
Dialogue: 0,0:00:16.00,0:00:18.00,Default,,0,0,0,retrying-notification,%s, nouvelle tentative dans %d secondes...
Dialogue: 0,0:00:18.00,0:00:20.00,Default,,0,0,0,reachout-successful-notification,Vous avez atteint {} ({})
Dialogue: 0,0:00:20.00,0:00:22.00,Default,,0,0,0,rewind-notification,Retour en arrière en raison du décalage de temps avec {}
Dialogue: 0,0:00:22.00,0:00:24.00,Default,,0,0,0,fastforward-notification,Avance rapide en raison du décalage de temps avec {}
Dialogue: 0,0:00:24.00,0:00:26.00,Default,,0,0,0,slowdown-notification,Ralentissement dû au décalage de temps avec {}
Dialogue: 0,0:00:26.00,0:00:28.00,Default,,0,0,0,revert-notification,Retour à la vitesse normale
Dialogue: 0,0:00:28.00,0:00:30.00,Default,,0,0,0,pause-notification,{} en pause
Dialogue: 0,0:00:30.00,0:00:32.00,Default,,0,0,0,unpause-notification,{} non suspendu
Dialogue: 0,0:00:32.00,0:00:34.00,Default,,0,0,0,seek-notification,{} est passé de {} à {}
Dialogue: 0,0:00:34.00,0:00:36.00,Default,,0,0,0,current-offset-notification,Décalage actuel: {}secondes
Dialogue: 0,0:00:36.00,0:00:38.00,Default,,0,0,0,media-directory-list-updated-notification,Les répertoires multimédias Syncplay ont été mis à jour.
Dialogue: 0,0:00:38.00,0:00:40.00,Default,,0,0,0,room-join-notification,{} a rejoint la salle: '{}'
Dialogue: 0,0:00:40.00,0:00:42.00,Default,,0,0,0,left-notification,{} est parti
Dialogue: 0,0:00:42.00,0:00:44.00,Default,,0,0,0,left-paused-notification,{} restants, {} en pause
Dialogue: 0,0:00:44.00,0:00:46.00,Default,,0,0,0,playing-notification,{} est en train de jouer '{}' ({})
Dialogue: 0,0:00:46.00,0:00:48.00,Default,,0,0,0,playing-notification/room-addendum,dans le salon: '{}'
Dialogue: 0,0:00:48.00,0:00:50.00,Default,,0,0,0,not-all-ready,Pas prêt: {}
Dialogue: 0,0:00:50.00,0:00:52.00,Default,,0,0,0,all-users-ready,Tout le monde est prêt ({} utilisateurs)
Dialogue: 0,0:00:52.00,0:00:54.00,Default,,0,0,0,ready-to-unpause-notification,Vous êtes maintenant défini comme prêt - réactivez la pause pour réactiver
Dialogue: 0,0:00:54.00,0:00:56.00,Default,,0,0,0,set-as-ready-notification,Vous êtes maintenant défini comme prêt
Dialogue: 0,0:00:56.00,0:00:58.00,Default,,0,0,0,set-as-not-ready-notification,Vous êtes maintenant défini comme non prêt
Dialogue: 0,0:00:58.00,0:01:00.00,Default,,0,0,0,autoplaying-notification,Lecture automatique dans {}...
Dialogue: 0,0:01:00.00,0:01:02.00,Default,,0,0,0,identifying-as-controller-notification,Identification en tant qu'opérateur de salle avec le mot de passe '{}'...
Dialogue: 0,0:01:02.00,0:01:04.00,Default,,0,0,0,failed-to-identify-as-controller-notification,{} n'a pas réussi à s'identifier en tant qu'opérateur de salle.
Dialogue: 0,0:01:04.00,0:01:06.00,Default,,0,0,0,authenticated-as-controller-notification,{} authentifié en tant qu'opérateur de salle
Dialogue: 0,0:01:06.00,0:01:08.00,Default,,0,0,0,created-controlled-room-notification,Salle gérée créée «{}» avec le mot de passe «{}». Veuillez conserver ces informations pour référence future !\n\nDans les salons gérés, tout le monde est synchronisé avec le ou les opérateurs de salon qui sont les seuls à pouvoir mettre en pause, reprendre, se déplacer dans la lecture et modifier la liste de lecture.\n\nVous devez demander aux spectateurs réguliers de rejoindre le salon '{}' mais les opérateurs de salon peuvent rejoindre le salon '{}' pour s'authentifier automatiquement.
Dialogue: 0,0:01:08.00,0:01:10.00,Default,,0,0,0,file-different-notification,Le fichier que vous lisez semble être différent de celui de {}
Dialogue: 0,0:01:10.00,0:01:12.00,Default,,0,0,0,file-differences-notification,Votre fichier diffère de la (des) manière(s) suivante(s): {}
Dialogue: 0,0:01:12.00,0:01:14.00,Default,,0,0,0,room-file-differences,Différences de fichiers: {}
Dialogue: 0,0:01:14.00,0:01:16.00,Default,,0,0,0,file-difference-filename,Nom
Dialogue: 0,0:01:16.00,0:01:18.00,Default,,0,0,0,file-difference-filesize,Taille
Dialogue: 0,0:01:18.00,0:01:20.00,Default,,0,0,0,file-difference-duration,durée
Dialogue: 0,0:01:20.00,0:01:22.00,Default,,0,0,0,alone-in-the-room,Vous êtes seul dans le salon
Dialogue: 0,0:01:22.00,0:01:24.00,Default,,0,0,0,different-filesize-notification,(leur taille de fichier est différente de la vôtre!)
Dialogue: 0,0:01:24.00,0:01:26.00,Default,,0,0,0,userlist-playing-notification,{} est en train de jouer:
Dialogue: 0,0:01:26.00,0:01:28.00,Default,,0,0,0,file-played-by-notification,Fichier: {} est lu par:
Dialogue: 0,0:01:28.00,0:01:30.00,Default,,0,0,0,no-file-played-notification,{} ne lit pas de fichier
Dialogue: 0,0:01:30.00,0:01:32.00,Default,,0,0,0,notplaying-notification,Les personnes qui ne lisent aucun fichier:
Dialogue: 0,0:01:32.00,0:01:34.00,Default,,0,0,0,userlist-room-notification,Dans la chambre '{}':
Dialogue: 0,0:01:34.00,0:01:36.00,Default,,0,0,0,userlist-file-notification,Fichier
Dialogue: 0,0:01:36.00,0:01:38.00,Default,,0,0,0,controller-userlist-userflag,Opérateur
Dialogue: 0,0:01:38.00,0:01:40.00,Default,,0,0,0,ready-userlist-userflag,Prêt
Dialogue: 0,0:01:40.00,0:01:42.00,Default,,0,0,0,update-check-failed-notification,Impossible de vérifier automatiquement si Syncplay {} est à jour. Vous voulez visiter https://syncplay.pl/ pour vérifier manuellement les mises à jour?
Dialogue: 0,0:01:42.00,0:01:44.00,Default,,0,0,0,syncplay-uptodate-notification,Syncplay est à jour
Dialogue: 0,0:01:44.00,0:01:46.00,Default,,0,0,0,syncplay-updateavailable-notification,Une nouvelle version de Syncplay est disponible. Voulez-vous visiter la page de publication?
Dialogue: 0,0:01:46.00,0:01:48.00,Default,,0,0,0,mplayer-file-required-notification,Syncplay à l'aide de mplayer nécessite que vous fournissiez un fichier au démarrage
Dialogue: 0,0:01:48.00,0:01:50.00,Default,,0,0,0,mplayer-file-required-notification/example,Exemple d'utilisation: syncplay [options] [url|chemin/]nom de fichier
Dialogue: 0,0:01:50.00,0:01:52.00,Default,,0,0,0,mplayer2-required,Syncplay est incompatible avec MPlayer 1.x, veuillez utiliser mplayer2 ou mpv
Dialogue: 0,0:01:52.00,0:01:54.00,Default,,0,0,0,unrecognized-command-notification,commande non reconnue
Dialogue: 0,0:01:54.00,0:01:56.00,Default,,0,0,0,commandlist-notification,Commandes disponibles:
Dialogue: 0,0:01:56.00,0:01:58.00,Default,,0,0,0,commandlist-notification/room,\tr [nom] - changer de chambre
Dialogue: 0,0:01:58.00,0:02:00.00,Default,,0,0,0,commandlist-notification/list,\tl - afficher la liste des utilisateurs
Dialogue: 0,0:02:00.00,0:02:02.00,Default,,0,0,0,commandlist-notification/undo,\tu - annuler la dernière recherche
Dialogue: 0,0:02:02.00,0:02:04.00,Default,,0,0,0,commandlist-notification/pause,\tp - basculer sur pause
Dialogue: 0,0:02:04.00,0:02:06.00,Default,,0,0,0,commandlist-notification/seek,\t[s][+-]temps - recherche la valeur de temps donnée, si + ou - n'est pas spécifié c'est le temps absolu en secondes ou min:sec
Dialogue: 0,0:02:06.00,0:02:08.00,Default,,0,0,0,commandlist-notification/help,\th - cette aide
Dialogue: 0,0:02:08.00,0:02:10.00,Default,,0,0,0,commandlist-notification/toggle,\tt - bascule si vous êtes prêt à regarder ou non
Dialogue: 0,0:02:10.00,0:02:12.00,Default,,0,0,0,commandlist-notification/create,\tc [nom] - crée une salle gérée en utilisant le nom de la salle actuelle
Dialogue: 0,0:02:12.00,0:02:14.00,Default,,0,0,0,commandlist-notification/auth,\tun [mot de passe] - s'authentifier en tant qu'opérateur de salle avec le mot de passe opérateur
Dialogue: 0,0:02:14.00,0:02:16.00,Default,,0,0,0,commandlist-notification/chat,\tch [message] - envoyer un message de chat dans une pièce
Dialogue: 0,0:02:16.00,0:02:18.00,Default,,0,0,0,commandList-notification/queue,\tqa [fichier/url] - ajoute un fichier ou une URL au bas de la liste de lecture
Dialogue: 0,0:02:18.00,0:02:20.00,Default,,0,0,0,commandList-notification/playlist,\tql - afficher la liste de lecture actuelle
Dialogue: 0,0:02:20.00,0:02:22.00,Default,,0,0,0,commandList-notification/select,\tqs [index] - sélectionnez l'entrée donnée dans la liste de lecture
Dialogue: 0,0:02:22.00,0:02:24.00,Default,,0,0,0,commandList-notification/delete,\tqd [index] - supprime l'entrée donnée de la liste de lecture
Dialogue: 0,0:02:24.00,0:02:26.00,Default,,0,0,0,syncplay-version-notification,Version de Syncplay: {}
Dialogue: 0,0:02:26.00,0:02:28.00,Default,,0,0,0,more-info-notification,Plus d'informations disponibles sur: {}
Dialogue: 0,0:02:28.00,0:02:30.00,Default,,0,0,0,gui-data-cleared-notification,Syncplay a effacé les données d'état de chemin et de fenêtre utilisées par l'interface graphique.
Dialogue: 0,0:02:30.00,0:02:32.00,Default,,0,0,0,language-changed-msgbox-label,La langue sera modifiée lorsque vous exécuterez Syncplay.
Dialogue: 0,0:02:32.00,0:02:34.00,Default,,0,0,0,promptforupdate-label,Est-ce que Syncplay peut vérifier automatiquement les mises à jour de temps en temps?
Dialogue: 0,0:02:34.00,0:02:36.00,Default,,0,0,0,media-player-latency-warning,Avertissement: Le lecteur multimédia a mis {}secondes à répondre. Si vous rencontrez des problèmes de synchronisation, fermez les applications pour libérer des ressources système, et si cela ne fonctionne pas, essayez un autre lecteur multimédia.
Dialogue: 0,0:02:36.00,0:02:38.00,Default,,0,0,0,mpv-unresponsive-error,mpv n'a pas répondu pendant {} secondes et semble donc avoir mal fonctionné. Veuillez redémarrer Syncplay.
Dialogue: 0,0:02:38.00,0:02:40.00,Default,,0,0,0,enter-to-exit-prompt,Appuyez sur entrée pour quitter
Dialogue: 0,0:02:40.00,0:02:42.00,Default,,0,0,0,missing-arguments-error,Certains arguments nécessaires sont manquants, reportez-vous à --help
Dialogue: 0,0:02:42.00,0:02:44.00,Default,,0,0,0,server-timeout-error,La connexion avec le serveur a expiré
Dialogue: 0,0:02:44.00,0:02:46.00,Default,,0,0,0,mpc-slave-error,Impossible de démarrer MPC en mode esclave!
Dialogue: 0,0:02:46.00,0:02:48.00,Default,,0,0,0,mpc-version-insufficient-error,La version MPC n'est pas suffisante, veuillez utiliser `mpc-hc` >= `{}`
Dialogue: 0,0:02:48.00,0:02:50.00,Default,,0,0,0,mpc-be-version-insufficient-error,La version MPC n'est pas suffisante, veuillez utiliser `mpc-be` >= `{}`
Dialogue: 0,0:02:50.00,0:02:52.00,Default,,0,0,0,mpv-version-error,Syncplay n'est pas compatible avec cette version de mpv. Veuillez utiliser une version différente de mpv (par exemple Git HEAD).
Dialogue: 0,0:02:52.00,0:02:54.00,Default,,0,0,0,mpv-failed-advice,La raison pour laquelle mpv ne peut pas démarrer peut être due à l'utilisation d'arguments de ligne de commande non pris en charge ou à une version non prise en charge de mpv.
Dialogue: 0,0:02:54.00,0:02:56.00,Default,,0,0,0,player-file-open-error,Le lecteur n'a pas réussi à ouvrir le fichier
Dialogue: 0,0:02:56.00,0:02:58.00,Default,,0,0,0,player-path-error,Le chemin du lecteur n'est pas défini correctement. Les lecteurs pris en charge sont : mpv, mpv.net, VLC, MPC-HC, MPC-BE, mplayer2 et IINA
Dialogue: 0,0:02:58.00,0:03:00.00,Default,,0,0,0,hostname-empty-error,Le nom d'hôte ne peut pas être vide
Dialogue: 0,0:03:00.00,0:03:02.00,Default,,0,0,0,empty-error,{} ne peut pas être vide
Dialogue: 0,0:03:02.00,0:03:04.00,Default,,0,0,0,media-player-error,Erreur du lecteur multimédia: "{}"
Dialogue: 0,0:03:04.00,0:03:06.00,Default,,0,0,0,unable-import-gui-error,Impossible d'importer les bibliothèques GUI. Si vous n'avez pas installé PySide, vous devrez l'installer pour que l'interface graphique fonctionne.
Dialogue: 0,0:03:06.00,0:03:08.00,Default,,0,0,0,unable-import-twisted-error,Impossible d'importer Twisted. Veuillez installer Twisted v16.4.0 ou une version ultérieure.
Dialogue: 0,0:03:08.00,0:03:10.00,Default,,0,0,0,arguments-missing-error,Certains arguments nécessaires sont manquants, reportez-vous à --help
Dialogue: 0,0:03:10.00,0:03:12.00,Default,,0,0,0,unable-to-start-client-error,Impossible de démarrer le client
Dialogue: 0,0:03:12.00,0:03:14.00,Default,,0,0,0,player-path-config-error,Le chemin du lecteur n'est pas défini correctement. Les lecteurs pris en charge sont : mpv, mpv.net, VLC, MPC-HC, MPC-BE, mplayer2 et IINA.
Dialogue: 0,0:03:14.00,0:03:16.00,Default,,0,0,0,no-file-path-config-error,Le fichier doit être sélectionné avant de démarrer votre lecteur
Dialogue: 0,0:03:16.00,0:03:18.00,Default,,0,0,0,no-hostname-config-error,Le nom d'hôte ne peut pas être vide
Dialogue: 0,0:03:18.00,0:03:20.00,Default,,0,0,0,invalid-port-config-error,Le port doit être valide
Dialogue: 0,0:03:20.00,0:03:22.00,Default,,0,0,0,empty-value-config-error,{} ne peut pas être vide
Dialogue: 0,0:03:22.00,0:03:24.00,Default,,0,0,0,not-json-error,Pas une chaîne encodée en json
Dialogue: 0,0:03:24.00,0:03:26.00,Default,,0,0,0,hello-arguments-error,Pas assez d'arguments pour Hello
Dialogue: 0,0:03:26.00,0:03:28.00,Default,,0,0,0,version-mismatch-error,Non-concordance entre les versions du client et du serveur
Dialogue: 0,0:03:28.00,0:03:30.00,Default,,0,0,0,vlc-failed-connection,Échec de la connexion à VLC. Si vous n'avez pas installé syncplay.lua et utilisez la dernière version de VLC, veuillez vous référer à https://syncplay.pl/LUA/ pour obtenir des instructions. Syncplay et VLC 4 ne sont actuellement pas compatibles, utilisez donc VLC 3 ou une alternative telle que mpv.
Dialogue: 0,0:03:30.00,0:03:32.00,Default,,0,0,0,vlc-failed-noscript,VLC a signalé que le script d'interface syncplay.lua n'a pas été installé. Veuillez vous référer à https://syncplay.pl/LUA/ pour obtenir des instructions.
Dialogue: 0,0:03:32.00,0:03:34.00,Default,,0,0,0,vlc-failed-versioncheck,Cette version de VLC n'est pas prise en charge par Syncplay.
Dialogue: 0,0:03:34.00,0:03:36.00,Default,,0,0,0,vlc-initial-warning,VLC ne fournit pas toujours des informations de position précises à Syncplay, en particulier pour les fichiers .mp4 et .avi. Si vous rencontrez des problèmes de recherche erronée, essayez un autre lecteur multimédia tel que <a href="https://mpv.io/">mpv</a> (ou <a href="https://github.com/stax76/mpv.net/">mpv.net</a> pour les utilisateurs de Windows).
Dialogue: 0,0:03:36.00,0:03:38.00,Default,,0,0,0,feature-sharedPlaylists,listes de lecture partagées
Dialogue: 0,0:03:38.00,0:03:40.00,Default,,0,0,0,feature-chat,chat
Dialogue: 0,0:03:40.00,0:03:42.00,Default,,0,0,0,feature-readiness,préparation
Dialogue: 0,0:03:42.00,0:03:44.00,Default,,0,0,0,feature-managedRooms,salons gérés
Dialogue: 0,0:03:44.00,0:03:46.00,Default,,0,0,0,not-supported-by-server-error,La fonctionnalité {} n'est pas prise en charge par ce serveur.
Dialogue: 0,0:03:46.00,0:03:48.00,Default,,0,0,0,shared-playlists-not-supported-by-server-error,La fonctionnalité de listes de lecture partagées peut ne pas être prise en charge par le serveur. Pour s'assurer qu'il fonctionne correctement, il faut un serveur exécutant Syncplay {}+, mais le serveur exécute Syncplay {}.
Dialogue: 0,0:03:48.00,0:03:50.00,Default,,0,0,0,shared-playlists-disabled-by-server-error,La fonctionnalité de liste de lecture partagée a été désactivée dans la configuration du serveur. Pour utiliser cette fonctionnalité, vous devrez vous connecter à un autre serveur.
Dialogue: 0,0:03:50.00,0:03:52.00,Default,,0,0,0,invalid-seek-value,Valeur de recherche non valide
Dialogue: 0,0:03:52.00,0:03:54.00,Default,,0,0,0,invalid-offset-value,Valeur de décalage non valide
Dialogue: 0,0:03:54.00,0:03:56.00,Default,,0,0,0,switch-file-not-found-error,Impossible de passer au fichier ''. Syncplay recherche dans les répertoires multimédias spécifiés.
Dialogue: 0,0:03:56.00,0:03:58.00,Default,,0,0,0,folder-search-timeout-error,La recherche de médias dans les répertoires de médias a été abandonnée car la recherche dans '{}' a pris trop de temps. Cela se produira si vous sélectionnez un dossier avec trop de sous-dossiers dans votre liste de dossiers multimédias à parcourir. Pour que le basculement automatique des fichiers fonctionne à nouveau, veuillez sélectionner Fichier->Définir les répertoires multimédias dans la barre de menu et supprimez ce répertoire ou remplacez-le par un sous-dossier approprié. Si le dossier est correct, vous pouvez le réactiver en sélectionnant Fichier->Définir les répertoires multimédias et en appuyant sur «OK».
Dialogue: 0,0:03:58.00,0:04:00.00,Default,,0,0,0,folder-search-first-file-timeout-error,La recherche de média dans '{}' a été abandonnée car elle a pris trop de temps pour accéder au répertoire. Cela peut arriver s'il s'agit d'un lecteur réseau ou si vous configurez votre lecteur pour qu'il ralentisse après une période d'inactivité. Pour que le basculement automatique des fichiers fonctionne à nouveau, accédez à Fichier-> Définir les répertoires multimédias et supprimez le répertoire ou résolvez le problème (par exemple en modifiant les paramètres d'économie d'énergie).
Dialogue: 0,0:04:00.00,0:04:02.00,Default,,0,0,0,added-file-not-in-media-directory-error,Vous avez chargé un fichier dans '{}' qui n'est pas un répertoire média connu. Vous pouvez l'ajouter en tant que répertoire multimédia en sélectionnant Fichier->Définir les répertoires multimédias dans la barre de menus.
Dialogue: 0,0:04:02.00,0:04:04.00,Default,,0,0,0,no-media-directories-error,Aucun répertoire multimédia n'a été défini. Pour que les fonctionnalités de liste de lecture partagée et de changement de fichier fonctionnent correctement, sélectionnez Fichier-> Définir les répertoires multimédias et spécifiez où Syncplay doit rechercher les fichiers multimédias.
Dialogue: 0,0:04:04.00,0:04:06.00,Default,,0,0,0,cannot-find-directory-error,Impossible de trouver le répertoire multimédia '{}'. Pour mettre à jour votre liste de répertoires multimédias, veuillez sélectionner Fichier->Définir les répertoires multimédias dans la barre de menu et spécifiez où Syncplay doit chercher pour trouver les fichiers multimédias.
Dialogue: 0,0:04:06.00,0:04:08.00,Default,,0,0,0,failed-to-load-server-list-error,Échec du chargement de la liste des serveurs publics. Veuillez visiter https://www.syncplay.pl/ dans votre navigateur.
Dialogue: 0,0:04:08.00,0:04:10.00,Default,,0,0,0,argument-description,Solution pour synchroniser la lecture de plusieurs instances de lecteur multimédia sur le réseau.
Dialogue: 0,0:04:10.00,0:04:12.00,Default,,0,0,0,argument-epilog,Si aucune option n'est fournie, les valeurs _config seront utilisées
Dialogue: 0,0:04:12.00,0:04:14.00,Default,,0,0,0,nogui-argument,masquer l'interface graphique
Dialogue: 0,0:04:14.00,0:04:16.00,Default,,0,0,0,host-argument,adresse du serveur
Dialogue: 0,0:04:16.00,0:04:18.00,Default,,0,0,0,name-argument,nom d'utilisateur souhaité
Dialogue: 0,0:04:18.00,0:04:20.00,Default,,0,0,0,debug-argument,Mode débogage
Dialogue: 0,0:04:20.00,0:04:22.00,Default,,0,0,0,force-gui-prompt-argument,faire apparaître l'invite de configuration
Dialogue: 0,0:04:22.00,0:04:24.00,Default,,0,0,0,no-store-argument,ne pas stocker de valeurs dans .syncplay
Dialogue: 0,0:04:24.00,0:04:26.00,Default,,0,0,0,room-argument,salon par défaut
Dialogue: 0,0:04:26.00,0:04:28.00,Default,,0,0,0,password-argument,Mot de passe du serveur
Dialogue: 0,0:04:28.00,0:04:30.00,Default,,0,0,0,player-path-argument,chemin d'accès à l'exécutable de votre lecteur
Dialogue: 0,0:04:30.00,0:04:32.00,Default,,0,0,0,file-argument,fichier à lire
Dialogue: 0,0:04:32.00,0:04:34.00,Default,,0,0,0,args-argument,options du lecteur, si vous devez passer des options commençant par - ajoutez-les au début avec un seul argument '--'
Dialogue: 0,0:04:34.00,0:04:36.00,Default,,0,0,0,clear-gui-data-argument,réinitialise les données GUI du chemin et de l'état de la fenêtre stockées en tant que QSettings
Dialogue: 0,0:04:36.00,0:04:38.00,Default,,0,0,0,language-argument,langue pour les messages Syncplay (de/en/ru/it/es/pt_BR/pt_PT/tr/fr)
Dialogue: 0,0:04:38.00,0:04:40.00,Default,,0,0,0,version-argument,imprime votre version
Dialogue: 0,0:04:40.00,0:04:42.00,Default,,0,0,0,version-message,Vous utilisez Syncplay version {} ({})
Dialogue: 0,0:04:42.00,0:04:44.00,Default,,0,0,0,load-playlist-from-file-argument,charge la liste de lecture à partir d'un fichier texte (une entrée par ligne)
Dialogue: 0,0:04:44.00,0:04:46.00,Default,,0,0,0,config-window-title,configuration Syncplay
Dialogue: 0,0:04:46.00,0:04:48.00,Default,,0,0,0,connection-group-title,Paramètres de connexion
Dialogue: 0,0:04:48.00,0:04:50.00,Default,,0,0,0,host-label,Adresse du serveur:
Dialogue: 0,0:04:50.00,0:04:52.00,Default,,0,0,0,name-label,Nom d'utilisateur (facultatif):
Dialogue: 0,0:04:52.00,0:04:54.00,Default,,0,0,0,password-label,Mot de passe du serveur (le cas échéant):
Dialogue: 0,0:04:54.00,0:04:56.00,Default,,0,0,0,room-label,Salon par défaut:
Dialogue: 0,0:04:56.00,0:04:58.00,Default,,0,0,0,roomlist-msgbox-label,Modifier la liste des salons (une par ligne)
Dialogue: 0,0:04:58.00,0:05:00.00,Default,,0,0,0,media-setting-title,Paramètres du lecteur multimédia
Dialogue: 0,0:05:00.00,0:05:02.00,Default,,0,0,0,executable-path-label,Chemin d'accès au lecteur multimédia:
Dialogue: 0,0:05:02.00,0:05:04.00,Default,,0,0,0,media-path-label,Chemin d'accès à la vidéo (facultatif):
Dialogue: 0,0:05:04.00,0:05:06.00,Default,,0,0,0,player-arguments-label,Arguments du joueur (le cas échéant):
Dialogue: 0,0:05:06.00,0:05:08.00,Default,,0,0,0,browse-label,Parcourir
Dialogue: 0,0:05:08.00,0:05:10.00,Default,,0,0,0,update-server-list-label,Mettre à jour la liste
Dialogue: 0,0:05:10.00,0:05:12.00,Default,,0,0,0,more-title,Afficher plus de paramètres
Dialogue: 0,0:05:12.00,0:05:14.00,Default,,0,0,0,never-rewind-value,Jamais
Dialogue: 0,0:05:14.00,0:05:16.00,Default,,0,0,0,seconds-suffix,secs
Dialogue: 0,0:05:16.00,0:05:18.00,Default,,0,0,0,privacy-sendraw-option,Envoyer brut
Dialogue: 0,0:05:18.00,0:05:20.00,Default,,0,0,0,privacy-sendhashed-option,Envoyer haché
Dialogue: 0,0:05:20.00,0:05:22.00,Default,,0,0,0,privacy-dontsend-option,Ne pas envoyer
Dialogue: 0,0:05:22.00,0:05:24.00,Default,,0,0,0,filename-privacy-label,Informations sur le nom de fichier:
Dialogue: 0,0:05:24.00,0:05:26.00,Default,,0,0,0,filesize-privacy-label,Informations sur la taille du fichier:
Dialogue: 0,0:05:26.00,0:05:28.00,Default,,0,0,0,checkforupdatesautomatically-label,Rechercher automatiquement les mises à jour de Syncplay
Dialogue: 0,0:05:28.00,0:05:30.00,Default,,0,0,0,autosavejoinstolist-label,Ajouter les salons que vous rejoignez à la liste des salons
Dialogue: 0,0:05:30.00,0:05:32.00,Default,,0,0,0,slowondesync-label,Ralentissement en cas de désynchronisation mineure (non pris en charge sur MPC-HC/BE)
Dialogue: 0,0:05:32.00,0:05:34.00,Default,,0,0,0,rewindondesync-label,Retour en arrière en cas de désynchronisation majeure (recommandé)
Dialogue: 0,0:05:34.00,0:05:36.00,Default,,0,0,0,fastforwardondesync-label,Avance rapide en cas de retard (recommandé)
Dialogue: 0,0:05:36.00,0:05:38.00,Default,,0,0,0,dontslowdownwithme-label,Ne jamais ralentir ou rembobiner les autres (expérimental)
Dialogue: 0,0:05:38.00,0:05:40.00,Default,,0,0,0,pausing-title,Pause
Dialogue: 0,0:05:40.00,0:05:42.00,Default,,0,0,0,pauseonleave-label,Pause lorsque l'utilisateur quitte (par exemple s'il est déconnecté)
Dialogue: 0,0:05:42.00,0:05:44.00,Default,,0,0,0,readiness-title,État de préparation initial
Dialogue: 0,0:05:44.00,0:05:46.00,Default,,0,0,0,readyatstart-label,Définissez-moi comme «prêt à regarder» par défaut
Dialogue: 0,0:05:46.00,0:05:48.00,Default,,0,0,0,forceguiprompt-label,Ne pas toujours afficher la fenêtre de configuration Syncplay
Dialogue: 0,0:05:48.00,0:05:50.00,Default,,0,0,0,showosd-label,Activer les Messages OSD
Dialogue: 0,0:05:50.00,0:05:52.00,Default,,0,0,0,showosdwarnings-label,Inclure des avertissements (par exemple, lorsque les fichiers sont différents, les utilisateurs ne sont pas prêts)
Dialogue: 0,0:05:52.00,0:05:54.00,Default,,0,0,0,showsameroomosd-label,Inclure des événements dans votre salon
Dialogue: 0,0:05:54.00,0:05:56.00,Default,,0,0,0,shownoncontrollerosd-label,Inclure les événements des non-opérateurs dans les salons gérés
Dialogue: 0,0:05:56.00,0:05:58.00,Default,,0,0,0,showdifferentroomosd-label,Inclure des événements dans d'autres salons
Dialogue: 0,0:05:58.00,0:06:00.00,Default,,0,0,0,showslowdownosd-label,Inclure les notifications de ralentissement/annulation
Dialogue: 0,0:06:00.00,0:06:02.00,Default,,0,0,0,language-label,Langue:
Dialogue: 0,0:06:02.00,0:06:04.00,Default,,0,0,0,automatic-language,Défaut ({})
Dialogue: 0,0:06:04.00,0:06:06.00,Default,,0,0,0,showdurationnotification-label,Avertir des incohérences de durée de média
Dialogue: 0,0:06:06.00,0:06:08.00,Default,,0,0,0,basics-label,Réglages de base
Dialogue: 0,0:06:08.00,0:06:10.00,Default,,0,0,0,readiness-label,Jouer pause
Dialogue: 0,0:06:10.00,0:06:12.00,Default,,0,0,0,misc-label,Divers
Dialogue: 0,0:06:12.00,0:06:14.00,Default,,0,0,0,core-behaviour-title,Comportement du salon principal
Dialogue: 0,0:06:14.00,0:06:16.00,Default,,0,0,0,syncplay-internals-title,procédures internes
Dialogue: 0,0:06:16.00,0:06:18.00,Default,,0,0,0,syncplay-mediasearchdirectories-title,Répertoires pour rechercher des médias
Dialogue: 0,0:06:18.00,0:06:20.00,Default,,0,0,0,syncplay-mediasearchdirectories-label,Répertoires pour rechercher des médias (un chemin par ligne)
Dialogue: 0,0:06:20.00,0:06:22.00,Default,,0,0,0,sync-label,Synchroniser
Dialogue: 0,0:06:22.00,0:06:24.00,Default,,0,0,0,sync-otherslagging-title,Si d'autres sont à la traîne...
Dialogue: 0,0:06:24.00,0:06:26.00,Default,,0,0,0,sync-youlaggging-title,Si vous êtes à la traîne...
Dialogue: 0,0:06:26.00,0:06:28.00,Default,,0,0,0,messages-label,Messages
Dialogue: 0,0:06:28.00,0:06:30.00,Default,,0,0,0,messages-osd-title,Paramètres d'affichage à l'écran
Dialogue: 0,0:06:30.00,0:06:32.00,Default,,0,0,0,messages-other-title,Autres paramètres d'affichage
Dialogue: 0,0:06:32.00,0:06:34.00,Default,,0,0,0,chat-label,Chat
Dialogue: 0,0:06:34.00,0:06:36.00,Default,,0,0,0,privacy-label,Sécurité données
Dialogue: 0,0:06:36.00,0:06:38.00,Default,,0,0,0,privacy-title,Paramètres de confidentialité
Dialogue: 0,0:06:38.00,0:06:40.00,Default,,0,0,0,unpause-title,Si vous appuyez sur play, définissez comme prêt et:
Dialogue: 0,0:06:40.00,0:06:42.00,Default,,0,0,0,unpause-ifalreadyready-option,Annuler la pause si déjà défini comme prêt
Dialogue: 0,0:06:42.00,0:06:44.00,Default,,0,0,0,unpause-ifothersready-option,Reprendre la pause si déjà prêt ou si d'autres personnes dans la pièce sont prêtes (par défaut)
Dialogue: 0,0:06:44.00,0:06:46.00,Default,,0,0,0,unpause-ifminusersready-option,Annuler la pause si déjà prêt ou si tous les autres sont prêts et utilisateurs minimum prêts
Dialogue: 0,0:06:46.00,0:06:48.00,Default,,0,0,0,unpause-always,Toujours reprendre
Dialogue: 0,0:06:48.00,0:06:50.00,Default,,0,0,0,syncplay-trusteddomains-title,Domaines de confiance (pour les services de streaming et le contenu hébergé)
Dialogue: 0,0:06:50.00,0:06:52.00,Default,,0,0,0,chat-title,Saisie du message de discussion
Dialogue: 0,0:06:52.00,0:06:54.00,Default,,0,0,0,chatinputenabled-label,Activer la saisie de discussion via mpv
Dialogue: 0,0:06:54.00,0:06:56.00,Default,,0,0,0,chatdirectinput-label,Autoriser la saisie de discussion instantanée (éviter d'avoir à appuyer sur la touche Entrée pour discuter)
Dialogue: 0,0:06:56.00,0:06:58.00,Default,,0,0,0,chatinputfont-label,Police de caractères pour la saisie sur le Chat
Dialogue: 0,0:06:58.00,0:07:00.00,Default,,0,0,0,chatfont-label,Définir la fonte
Dialogue: 0,0:07:00.00,0:07:02.00,Default,,0,0,0,chatcolour-label,Définir la couleur
Dialogue: 0,0:07:02.00,0:07:04.00,Default,,0,0,0,chatinputposition-label,Position de la zone de saisie des messages dans mpv
Dialogue: 0,0:07:04.00,0:07:06.00,Default,,0,0,0,chat-top-option,Haut
Dialogue: 0,0:07:06.00,0:07:08.00,Default,,0,0,0,chat-middle-option,Milieu
Dialogue: 0,0:07:08.00,0:07:10.00,Default,,0,0,0,chat-bottom-option,Bas
Dialogue: 0,0:07:10.00,0:07:12.00,Default,,0,0,0,chatoutputheader-label,Sortie du message de discussion
Dialogue: 0,0:07:12.00,0:07:14.00,Default,,0,0,0,chatoutputfont-label,Police de sortie du chat
Dialogue: 0,0:07:14.00,0:07:16.00,Default,,0,0,0,chatoutputenabled-label,Activer la sortie du chat dans le lecteur multimédia (mpv uniquement pour l'instant)
Dialogue: 0,0:07:16.00,0:07:18.00,Default,,0,0,0,chatoutputposition-label,Mode de sortie
Dialogue: 0,0:07:18.00,0:07:20.00,Default,,0,0,0,chat-chatroom-option,Style de salon de discussion
Dialogue: 0,0:07:20.00,0:07:22.00,Default,,0,0,0,chat-scrolling-option,Style de défilement
Dialogue: 0,0:07:22.00,0:07:24.00,Default,,0,0,0,mpv-key-tab-hint,[TAB] pour basculer l'accès aux raccourcis des touches de la ligne alphabétique.
Dialogue: 0,0:07:24.00,0:07:26.00,Default,,0,0,0,mpv-key-hint,[ENTER] pour envoyer un message. [ESC] pour quitter le mode chat.
Dialogue: 0,0:07:26.00,0:07:28.00,Default,,0,0,0,alphakey-mode-warning-first-line,Vous pouvez temporairement utiliser les anciennes liaisons mpv avec les touches az.
Dialogue: 0,0:07:28.00,0:07:30.00,Default,,0,0,0,alphakey-mode-warning-second-line,Appuyez sur [TAB] pour revenir au mode de discussion Syncplay.
Dialogue: 0,0:07:30.00,0:07:32.00,Default,,0,0,0,help-label,Aider
Dialogue: 0,0:07:32.00,0:07:34.00,Default,,0,0,0,reset-label,Réinitialiser
Dialogue: 0,0:07:34.00,0:07:36.00,Default,,0,0,0,run-label,Exécuter Syncplay
Dialogue: 0,0:07:36.00,0:07:38.00,Default,,0,0,0,storeandrun-label,Stocker la configuration et exécuter Syncplay
Dialogue: 0,0:07:38.00,0:07:40.00,Default,,0,0,0,contact-label,N'hésitez pas à envoyer un e-mail à <a href="mailto:dev@syncplay.pl"><nobr>dev@syncplay.pl</nobr></a> , à <a href="https://github.com/Syncplay/syncplay/issues"><nobr>créer un problème</nobr></a> pour signaler un bug/problème via GitHub, à <a href="https://github.com/Syncplay/syncplay/discussions"><nobr>démarrer une discussion</nobr></a> pour faire une suggestion ou à poser une question via GitHub, <a href="https://www.facebook.com/SyncplaySoftware"><nobr>à nous aimer sur Facebook</nobr></a> , à <a href="https://twitter.com/Syncplay/"><nobr>nous suivre sur Twitter</nobr></a> ou à visiter <a href="https://syncplay.pl/"><nobr>https://syncplay.pl/</nobr></a> . N'utilisez pas Syncplay pour envoyer des informations sensibles.
Dialogue: 0,0:07:40.00,0:07:42.00,Default,,0,0,0,joinroom-label,Rejoindre la salle
Dialogue: 0,0:07:42.00,0:07:44.00,Default,,0,0,0,joinroom-menu-label,Rejoindre la salle {}
Dialogue: 0,0:07:44.00,0:07:46.00,Default,,0,0,0,seektime-menu-label,Chercher le temps
Dialogue: 0,0:07:46.00,0:07:48.00,Default,,0,0,0,undoseek-menu-label,Annuler la recherche
Dialogue: 0,0:07:48.00,0:07:50.00,Default,,0,0,0,play-menu-label,Jouer
Dialogue: 0,0:07:50.00,0:07:52.00,Default,,0,0,0,pause-menu-label,Pause
Dialogue: 0,0:07:52.00,0:07:54.00,Default,,0,0,0,playbackbuttons-menu-label,Afficher les boutons de lecture
Dialogue: 0,0:07:54.00,0:07:56.00,Default,,0,0,0,autoplay-menu-label,Afficher le bouton de lecture automatique
Dialogue: 0,0:07:56.00,0:07:58.00,Default,,0,0,0,autoplay-guipushbuttonlabel,Jouer quand tout est prêt
Dialogue: 0,0:07:58.00,0:08:00.00,Default,,0,0,0,autoplay-minimum-label,Utilisateurs minimum:
Dialogue: 0,0:08:00.00,0:08:02.00,Default,,0,0,0,sendmessage-label,Envoyer
Dialogue: 0,0:08:02.00,0:08:04.00,Default,,0,0,0,ready-guipushbuttonlabel,Je suis prêt à regarder !
Dialogue: 0,0:08:04.00,0:08:06.00,Default,,0,0,0,roomuser-heading-label,Salon / Utilisateur
Dialogue: 0,0:08:06.00,0:08:08.00,Default,,0,0,0,size-heading-label,Taille
Dialogue: 0,0:08:08.00,0:08:10.00,Default,,0,0,0,duration-heading-label,Durée
Dialogue: 0,0:08:10.00,0:08:12.00,Default,,0,0,0,filename-heading-label,Nom de fichier
Dialogue: 0,0:08:12.00,0:08:14.00,Default,,0,0,0,notifications-heading-label,Notifications
Dialogue: 0,0:08:14.00,0:08:16.00,Default,,0,0,0,userlist-heading-label,Liste de qui joue quoi
Dialogue: 0,0:08:16.00,0:08:18.00,Default,,0,0,0,browseformedia-label,Parcourir les fichiers multimédias
Dialogue: 0,0:08:18.00,0:08:20.00,Default,,0,0,0,file-menu-label,&Fichier
Dialogue: 0,0:08:20.00,0:08:22.00,Default,,0,0,0,openmedia-menu-label,&Ouvrir le fichier multimédia
Dialogue: 0,0:08:22.00,0:08:24.00,Default,,0,0,0,openstreamurl-menu-label,Ouvrir l'URL du &flux multimédia
Dialogue: 0,0:08:24.00,0:08:26.00,Default,,0,0,0,setmediadirectories-menu-label,Définir les &répertoires multimédias
Dialogue: 0,0:08:26.00,0:08:28.00,Default,,0,0,0,loadplaylistfromfile-menu-label,&Charger la liste de lecture à partir du fichier
Dialogue: 0,0:08:28.00,0:08:30.00,Default,,0,0,0,saveplaylisttofile-menu-label,&Enregistrer la liste de lecture dans un fichier
Dialogue: 0,0:08:30.00,0:08:32.00,Default,,0,0,0,exit-menu-label,Sortir
Dialogue: 0,0:08:32.00,0:08:34.00,Default,,0,0,0,advanced-menu-label,&Avancée
Dialogue: 0,0:08:34.00,0:08:36.00,Default,,0,0,0,window-menu-label,&Fenêtre
Dialogue: 0,0:08:36.00,0:08:38.00,Default,,0,0,0,setoffset-menu-label,Définir &décalage
Dialogue: 0,0:08:38.00,0:08:40.00,Default,,0,0,0,createcontrolledroom-menu-label,&Créer une salon à gérer
Dialogue: 0,0:08:40.00,0:08:42.00,Default,,0,0,0,identifyascontroller-menu-label,&Identifier en tant qu'opérateur de salon
Dialogue: 0,0:08:42.00,0:08:44.00,Default,,0,0,0,settrusteddomains-menu-label,Définir des &domaines de confiance
Dialogue: 0,0:08:44.00,0:08:46.00,Default,,0,0,0,addtrusteddomain-menu-label,Ajouter {} comme domaine de confiance
Dialogue: 0,0:08:46.00,0:08:48.00,Default,,0,0,0,edit-menu-label,&Éditer
Dialogue: 0,0:08:48.00,0:08:50.00,Default,,0,0,0,cut-menu-label,Couper
Dialogue: 0,0:08:50.00,0:08:52.00,Default,,0,0,0,copy-menu-label,&Copier
Dialogue: 0,0:08:52.00,0:08:54.00,Default,,0,0,0,paste-menu-label,&Coller
Dialogue: 0,0:08:54.00,0:08:56.00,Default,,0,0,0,selectall-menu-label,&Tout sélectionner
Dialogue: 0,0:08:56.00,0:08:58.00,Default,,0,0,0,playback-menu-label,&Relecture
Dialogue: 0,0:08:58.00,0:09:00.00,Default,,0,0,0,help-menu-label,&Aide
Dialogue: 0,0:09:00.00,0:09:02.00,Default,,0,0,0,userguide-menu-label,Ouvrir le &guide de l'utilisateur
Dialogue: 0,0:09:02.00,0:09:04.00,Default,,0,0,0,update-menu-label,Rechercher et mettre à jour
Dialogue: 0,0:09:04.00,0:09:06.00,Default,,0,0,0,startTLS-initiated,Tentative de connexion sécurisée
Dialogue: 0,0:09:06.00,0:09:08.00,Default,,0,0,0,startTLS-secure-connection-ok,Connexion sécurisée établie ({})
Dialogue: 0,0:09:08.00,0:09:10.00,Default,,0,0,0,startTLS-server-certificate-invalid,Échec de la Connexion Sécurisée. Le serveur utilise un certificat de sécurité non valide. Cette communication pourrait être interceptée par un tiers. Pour plus de détails et de dépannage, voir <a href="https://syncplay.pl/trouble">ici</a> .
Dialogue: 0,0:09:10.00,0:09:12.00,Default,,0,0,0,startTLS-server-certificate-invalid-DNS-ID,Syncplay ne fait pas confiance à ce serveur car il utilise un certificat qui n'est pas valide pour son nom d'hôte.
Dialogue: 0,0:09:12.00,0:09:14.00,Default,,0,0,0,startTLS-not-supported-client,Ce client ne prend pas en charge TLS
Dialogue: 0,0:09:14.00,0:09:16.00,Default,,0,0,0,startTLS-not-supported-server,Ce serveur ne prend pas en charge TLS
Dialogue: 0,0:09:16.00,0:09:18.00,Default,,0,0,0,tls-information-title,Détails du certificat
Dialogue: 0,0:09:18.00,0:09:20.00,Default,,0,0,0,tls-dialog-status-label,<strong>Syncplay utilise une connexion cryptée à {}.</strong>
Dialogue: 0,0:09:20.00,0:09:22.00,Default,,0,0,0,tls-dialog-desc-label,Le cryptage avec un certificat numérique préserve la confidentialité des informations lorsqu'elles sont envoyées vers ou depuis le serveur {}.
Dialogue: 0,0:09:22.00,0:09:24.00,Default,,0,0,0,tls-dialog-connection-label,Informations chiffrées à l'aide de Transport Layer Security (TLS), version {} avec la suite de chiffrement: {}.
Dialogue: 0,0:09:24.00,0:09:26.00,Default,,0,0,0,tls-dialog-certificate-label,Certificat délivré par {} valable jusqu'au {}.
Dialogue: 0,0:09:26.00,0:09:28.00,Default,,0,0,0,about-menu-label,&À propos de la lecture synchronisée
Dialogue: 0,0:09:28.00,0:09:30.00,Default,,0,0,0,about-dialog-title,À propos de Syncplay
Dialogue: 0,0:09:30.00,0:09:32.00,Default,,0,0,0,about-dialog-release,Version {} release {}
Dialogue: 0,0:09:32.00,0:09:34.00,Default,,0,0,0,about-dialog-license-text,Sous licence Apache, version 2.0
Dialogue: 0,0:09:34.00,0:09:36.00,Default,,0,0,0,about-dialog-license-button,Licence
Dialogue: 0,0:09:36.00,0:09:38.00,Default,,0,0,0,about-dialog-dependencies,Dépendances
Dialogue: 0,0:09:38.00,0:09:40.00,Default,,0,0,0,setoffset-msgbox-label,Définir le décalage
Dialogue: 0,0:09:40.00,0:09:42.00,Default,,0,0,0,offsetinfo-msgbox-label,Offset (voir https://syncplay.pl/guide/ pour les instructions d'utilisation):
Dialogue: 0,0:09:42.00,0:09:44.00,Default,,0,0,0,promptforstreamurl-msgbox-label,Ouvrir l'URL du flux multimédia
Dialogue: 0,0:09:44.00,0:09:46.00,Default,,0,0,0,promptforstreamurlinfo-msgbox-label,URL de diffusion
Dialogue: 0,0:09:46.00,0:09:48.00,Default,,0,0,0,addfolder-label,Ajouter le dossier
Dialogue: 0,0:09:48.00,0:09:50.00,Default,,0,0,0,adduris-msgbox-label,Ajouter des URL à la liste de lecture (une par ligne)
Dialogue: 0,0:09:50.00,0:09:52.00,Default,,0,0,0,editplaylist-msgbox-label,Définir la liste de lecture (une par ligne)
Dialogue: 0,0:09:52.00,0:09:54.00,Default,,0,0,0,trusteddomains-msgbox-label,Domaines vers lesquels vous pouvez basculer automatiquement (un par ligne)
Dialogue: 0,0:09:54.00,0:09:56.00,Default,,0,0,0,createcontrolledroom-msgbox-label,Créer un salon à gérer
Dialogue: 0,0:09:56.00,0:09:58.00,Default,,0,0,0,controlledroominfo-msgbox-label,Saisissez le nom du salon à gérer
Dialogue: 0,0:09:58.00,0:10:00.00,Default,,0,0,0,identifyascontroller-msgbox-label,S'identifier en tant qu'opérateur de salon
Dialogue: 0,0:10:00.00,0:10:02.00,Default,,0,0,0,identifyinfo-msgbox-label,Entrez le mot de passe de l'opérateur pour ce salon
Dialogue: 0,0:10:02.00,0:10:04.00,Default,,0,0,0,public-server-msgbox-label,Sélectionnez le serveur public pour cette session de visualisation
Dialogue: 0,0:10:04.00,0:10:06.00,Default,,0,0,0,megabyte-suffix,Mo
Dialogue: 0,0:10:06.00,0:10:08.00,Default,,0,0,0,host-tooltip,Nom d'hôte ou IP auquel se connecter, incluant éventuellement le port (par exemple syncplay.pl:8999). Uniquement synchronisé avec des personnes sur le même serveur/port.
Dialogue: 0,0:10:08.00,0:10:10.00,Default,,0,0,0,name-tooltip,Surnom sous lequel vous serez connu. Pas d'inscription, donc peut facilement changer plus tard. Nom aléatoire généré si aucun n'est spécifié.
Dialogue: 0,0:10:10.00,0:10:12.00,Default,,0,0,0,password-tooltip,Les mots de passe ne sont nécessaires que pour se connecter à des serveurs privés.
Dialogue: 0,0:10:12.00,0:10:14.00,Default,,0,0,0,room-tooltip,Le salon à rejoindre lors de la connexion peut être presque n'importe quoi, mais vous ne serez synchronisé qu'avec des personnes dans le même salon.
Dialogue: 0,0:10:14.00,0:10:16.00,Default,,0,0,0,edit-rooms-tooltip,Modifier la liste des salons.
Dialogue: 0,0:10:16.00,0:10:18.00,Default,,0,0,0,executable-path-tooltip,Emplacement du lecteur multimédia pris en charge que vous avez choisi (mpv, mpv.net, VLC, MPC-HC/BE, mplayer2 ou IINA).
Dialogue: 0,0:10:18.00,0:10:20.00,Default,,0,0,0,media-path-tooltip,Emplacement de la vidéo ou du flux à ouvrir. Nécessaire pour mplayer2.
Dialogue: 0,0:10:20.00,0:10:22.00,Default,,0,0,0,player-arguments-tooltip,Arguments/commutateurs de ligne de commande supplémentaires à transmettre à ce lecteur multimédia.
Dialogue: 0,0:10:22.00,0:10:24.00,Default,,0,0,0,mediasearcdirectories-arguments-tooltip,Répertoires dans lesquels Syncplay recherchera les fichiers multimédias, par exemple lorsque vous utilisez la fonctionalité cliquer pour basculer. Syncplay recherchera récursivement dans les sous-dossiers.
Dialogue: 0,0:10:24.00,0:10:26.00,Default,,0,0,0,more-tooltip,Afficher les paramètres moins fréquemment utilisés.
Dialogue: 0,0:10:26.00,0:10:28.00,Default,,0,0,0,filename-privacy-tooltip,Mode de confidentialité pour l'envoi du nom de fichier en cours de lecture au serveur.
Dialogue: 0,0:10:28.00,0:10:30.00,Default,,0,0,0,filesize-privacy-tooltip,Mode de confidentialité pour l'envoi de la taille du fichier en cours de lecture au serveur.
Dialogue: 0,0:10:30.00,0:10:32.00,Default,,0,0,0,privacy-sendraw-tooltip,Envoyez ces informations sans brouillage. Il s'agit de l'option par défaut avec la plupart des fonctionnalités.
Dialogue: 0,0:10:32.00,0:10:34.00,Default,,0,0,0,privacy-sendhashed-tooltip,Envoyez une version hachée des informations, les rendant moins visibles pour les autres clients.
Dialogue: 0,0:10:34.00,0:10:36.00,Default,,0,0,0,privacy-dontsend-tooltip,N'envoyez pas ces informations au serveur. Cela garantit une confidentialité maximale.
Dialogue: 0,0:10:36.00,0:10:38.00,Default,,0,0,0,checkforupdatesautomatically-tooltip,Vérifiez régulièrement sur le site Web de Syncplay si une nouvelle version de Syncplay est disponible.
Dialogue: 0,0:10:38.00,0:10:40.00,Default,,0,0,0,autosavejoinstolist-tooltip,Lorsque vous rejoignez un salon sur un serveur, mémorisez automatiquement le nom de la salle dans la liste des salons à rejoindre.
Dialogue: 0,0:10:40.00,0:10:42.00,Default,,0,0,0,slowondesync-tooltip,Réduisez temporairement le taux de lecture si nécessaire pour vous synchroniser avec les autres téléspectateurs. Non pris en charge sur MPC-HC/BE.
Dialogue: 0,0:10:42.00,0:10:44.00,Default,,0,0,0,dontslowdownwithme-tooltip,Cela signifie que les autres ne sont pas ralentis ou rembobinés si votre lecture est en retard. Utile pour les opérateurs de salon.
Dialogue: 0,0:10:44.00,0:10:46.00,Default,,0,0,0,pauseonleave-tooltip,Mettez la lecture en pause si vous êtes déconnecté ou si quelqu'un quitte votre salon.
Dialogue: 0,0:10:46.00,0:10:48.00,Default,,0,0,0,readyatstart-tooltip,Définissez-vous comme «prêt» au début (sinon, vous êtes défini comme «pas prêt» jusqu'à ce que vous changiez votre état de préparation)
Dialogue: 0,0:10:48.00,0:10:50.00,Default,,0,0,0,forceguiprompt-tooltip,La boîte de dialogue de configuration ne s'affiche pas lors de l'ouverture d'un fichier avec Syncplay.
Dialogue: 0,0:10:50.00,0:10:52.00,Default,,0,0,0,nostore-tooltip,Exécutez Syncplay avec la configuration donnée, mais ne stockez pas les modifications de manière permanente.
Dialogue: 0,0:10:52.00,0:10:54.00,Default,,0,0,0,rewindondesync-tooltip,Revenez en arrière au besoin pour vous synchroniser. La désactivation de cette option peut entraîner des désynchronisations majeures!
Dialogue: 0,0:10:54.00,0:10:56.00,Default,,0,0,0,fastforwardondesync-tooltip,Avancez en cas de désynchronisation avec l'opérateur de la salle (ou votre position factice si l'option «Ne jamais ralentir ou rembobiner les autres» est activée).
Dialogue: 0,0:10:56.00,0:10:58.00,Default,,0,0,0,showosd-tooltip,Envoie des messages Syncplay à l'OSD du lecteur multimédia.
Dialogue: 0,0:10:58.00,0:11:00.00,Default,,0,0,0,showosdwarnings-tooltip,Afficher des avertissements en cas de lecture d'un fichier différent, seul dans la pièce, utilisateurs non prêts, etc.
Dialogue: 0,0:11:00.00,0:11:02.00,Default,,0,0,0,showsameroomosd-tooltip,Afficher les notifications OSD pour les événements liés à l'utilisateur du salon.
Dialogue: 0,0:11:02.00,0:11:04.00,Default,,0,0,0,shownoncontrollerosd-tooltip,Afficher les notifications OSD pour les événements relatifs aux non-opérateurs qui se trouvent dans les salles gérées.
Dialogue: 0,0:11:04.00,0:11:06.00,Default,,0,0,0,showdifferentroomosd-tooltip,Afficher les notifications OSD pour les événements liés à l'absence de l'utilisateur du salon.
Dialogue: 0,0:11:06.00,0:11:08.00,Default,,0,0,0,showslowdownosd-tooltip,Afficher les notifications de ralentissement / de retour au décalage temps.
Dialogue: 0,0:11:08.00,0:11:10.00,Default,,0,0,0,showdurationnotification-tooltip,Utile lorsqu'un segment dans un fichier en plusieurs parties est manquant, mais peut entraîner des faux positifs.
Dialogue: 0,0:11:10.00,0:11:12.00,Default,,0,0,0,language-tooltip,Langue à utiliser par Syncplay.
Dialogue: 0,0:11:12.00,0:11:14.00,Default,,0,0,0,unpause-always-tooltip,Si vous appuyez sur unpause, cela vous définit toujours comme prêt et non-pause, plutôt que de simplement vous définir comme prêt.
Dialogue: 0,0:11:14.00,0:11:16.00,Default,,0,0,0,unpause-ifalreadyready-tooltip,Si vous appuyez sur unpause lorsque vous n'êtes pas prêt, cela vous mettra comme prêt - appuyez à nouveau sur unpause pour reprendre la pause.
Dialogue: 0,0:11:16.00,0:11:18.00,Default,,0,0,0,unpause-ifothersready-tooltip,Si vous appuyez sur unpause lorsque vous n'êtes pas prêt, il ne reprendra la pause que si d'autres sont prêts.
Dialogue: 0,0:11:18.00,0:11:20.00,Default,,0,0,0,unpause-ifminusersready-tooltip,Si vous appuyez sur annuler la pause lorsqu'il n'est pas prêt, il ne s'arrêtera que si d'autres personnes sont prêtes et que le seuil minimal d'utilisateurs est atteint.
Dialogue: 0,0:11:20.00,0:11:22.00,Default,,0,0,0,trusteddomains-arguments-tooltip,Domaines vers lesquels Syncplay peut basculer automatiquement lorsque les listes de lecture partagées sont activées.
Dialogue: 0,0:11:22.00,0:11:24.00,Default,,0,0,0,chatinputenabled-tooltip,Activer la saisie du chat dans mpv (appuyez sur Entrée pour discuter, Entrée pour envoyer, Échap pour annuler)
Dialogue: 0,0:11:24.00,0:11:26.00,Default,,0,0,0,chatdirectinput-tooltip,Évitez d'avoir à appuyer sur «enter» pour passer en mode de saisie de discussion dans mpv. Appuyez sur TAB dans mpv pour désactiver temporairement cette fonctionnalité.
Dialogue: 0,0:11:26.00,0:11:28.00,Default,,0,0,0,font-label-tooltip,Police utilisée lors de la saisie de messages de discussion dans mpv. Côté client uniquement, n'affecte donc pas ce que les autres voient.
Dialogue: 0,0:11:28.00,0:11:30.00,Default,,0,0,0,set-input-font-tooltip,Famille de polices utilisée lors de la saisie de messages de discussion dans mpv. Côté client uniquement, n'affecte donc pas ce que les autres voient.
Dialogue: 0,0:11:30.00,0:11:32.00,Default,,0,0,0,set-input-colour-tooltip,Couleur de police utilisée lors de la saisie de messages de discussion dans mpv. Côté client uniquement, n'affecte donc pas ce que les autres voient.
Dialogue: 0,0:11:32.00,0:11:34.00,Default,,0,0,0,chatinputposition-tooltip,Emplacement dans mpv où le texte d'entrée de discussion apparaîtra lorsque vous appuyez sur Entrée et tapez.
Dialogue: 0,0:11:34.00,0:11:36.00,Default,,0,0,0,chatinputposition-top-tooltip,Placez l'entrée de discussion en haut de la fenêtre mpv.
Dialogue: 0,0:11:36.00,0:11:38.00,Default,,0,0,0,chatinputposition-middle-tooltip,Placez l'entrée de discussion au point mort de la fenêtre mpv.
Dialogue: 0,0:11:38.00,0:11:40.00,Default,,0,0,0,chatinputposition-bottom-tooltip,Placez l'entrée de discussion en bas de la fenêtre mpv.
Dialogue: 0,0:11:40.00,0:11:42.00,Default,,0,0,0,chatoutputenabled-tooltip,Afficher les messages de discussion dans l'OSD (si pris en charge par le lecteur multimédia).
Dialogue: 0,0:11:42.00,0:11:44.00,Default,,0,0,0,font-output-label-tooltip,Police de sortie du chat.
Dialogue: 0,0:11:44.00,0:11:46.00,Default,,0,0,0,set-output-font-tooltip,Police utilisée pour l'affichage des messages de discussion.
Dialogue: 0,0:11:46.00,0:11:48.00,Default,,0,0,0,chatoutputmode-tooltip,Comment les messages de chat sont affichés.
Dialogue: 0,0:11:48.00,0:11:50.00,Default,,0,0,0,chatoutputmode-chatroom-tooltip,Affichez les nouvelles lignes de discussion directement sous la ligne précédente.
Dialogue: 0,0:11:50.00,0:11:52.00,Default,,0,0,0,chatoutputmode-scrolling-tooltip,Faites défiler le texte du chat de droite à gauche.
Dialogue: 0,0:11:52.00,0:11:54.00,Default,,0,0,0,help-tooltip,Ouvre le guide de l'utilisateur de Syncplay.pl.
Dialogue: 0,0:11:54.00,0:11:56.00,Default,,0,0,0,reset-tooltip,Réinitialisez tous les paramètres à la configuration par défaut.
Dialogue: 0,0:11:56.00,0:11:58.00,Default,,0,0,0,update-server-list-tooltip,Connectez-vous à syncplay.pl pour mettre à jour la liste des serveurs publics.
Dialogue: 0,0:11:58.00,0:12:00.00,Default,,0,0,0,sslconnection-tooltip,Connecté en toute sécurité au serveur. Cliquez pour obtenir les détails du certificat.
Dialogue: 0,0:12:00.00,0:12:02.00,Default,,0,0,0,joinroom-tooltip,Quitter la salle actuelle et rejoindre le salon spécifié.
Dialogue: 0,0:12:02.00,0:12:04.00,Default,,0,0,0,seektime-msgbox-label,Aller au temps spécifié (en secondes / min:sec). Utilisez +/- pour la recherche relative.
Dialogue: 0,0:12:04.00,0:12:06.00,Default,,0,0,0,ready-tooltip,Indique si vous êtes prêt à regarder.
Dialogue: 0,0:12:06.00,0:12:08.00,Default,,0,0,0,autoplay-tooltip,Lecture automatique lorsque tous les utilisateurs qui ont un indicateur de disponibilité sont prêts et que le seuil d'utilisateur minimum est atteint.
Dialogue: 0,0:12:08.00,0:12:10.00,Default,,0,0,0,switch-to-file-tooltip,Double-cliquez pour passer à {}
Dialogue: 0,0:12:10.00,0:12:12.00,Default,,0,0,0,sendmessage-tooltip,Envoyer un message au salon
Dialogue: 0,0:12:12.00,0:12:14.00,Default,,0,0,0,differentsize-note,Différentes tailles!
Dialogue: 0,0:12:14.00,0:12:16.00,Default,,0,0,0,differentsizeandduration-note,Taille et durée différentes !
Dialogue: 0,0:12:16.00,0:12:18.00,Default,,0,0,0,differentduration-note,Durée différente !
Dialogue: 0,0:12:18.00,0:12:20.00,Default,,0,0,0,nofile-note,(Aucun fichier en cours de lecture)
Dialogue: 0,0:12:20.00,0:12:22.00,Default,,0,0,0,new-syncplay-available-motd-message,Vous utilisez Syncplay {} mais une version plus récente est disponible sur https://syncplay.pl
Dialogue: 0,0:12:22.00,0:12:24.00,Default,,0,0,0,welcome-server-notification,Bienvenue sur le serveur Syncplay, ver.
Dialogue: 0,0:12:24.00,0:12:26.00,Default,,0,0,0,client-connected-room-server-notification,({2}) connecté à la salle '{1}'
Dialogue: 0,0:12:26.00,0:12:28.00,Default,,0,0,0,client-left-server-notification,a quitté le serveur
Dialogue: 0,0:12:28.00,0:12:30.00,Default,,0,0,0,no-salt-notification,VEUILLEZ NOTER: Pour permettre aux mots de passe d'opérateur de salle générés par cette instance de serveur de fonctionner lorsque le serveur est redémarré, veuillez ajouter l'argument de ligne de commande suivant lors de l'exécution du serveur Syncplay à l'avenir: --salt {}
Dialogue: 0,0:12:30.00,0:12:32.00,Default,,0,0,0,server-argument-description,Solution pour synchroniser la lecture de plusieurs instances de lecteur multimédia sur le réseau. Instance de serveur
Dialogue: 0,0:12:32.00,0:12:34.00,Default,,0,0,0,server-argument-epilog,Si aucune option n'est fournie, les valeurs _config seront utilisées
Dialogue: 0,0:12:34.00,0:12:36.00,Default,,0,0,0,server-port-argument,port TCP du serveur
Dialogue: 0,0:12:36.00,0:12:38.00,Default,,0,0,0,server-password-argument,Mot de passe du serveur
Dialogue: 0,0:12:38.00,0:12:40.00,Default,,0,0,0,server-isolate-room-argument,faut-il isoler les salons ?
Dialogue: 0,0:12:40.00,0:12:42.00,Default,,0,0,0,server-salt-argument,chaîne aléatoire utilisée pour générer les mots de passe des salons gérés
Dialogue: 0,0:12:42.00,0:12:44.00,Default,,0,0,0,server-disable-ready-argument,désactiver la fonction de préparation
Dialogue: 0,0:12:44.00,0:12:46.00,Default,,0,0,0,server-motd-argument,chemin vers le fichier à partir duquel motd sera récupéré
Dialogue: 0,0:12:46.00,0:12:48.00,Default,,0,0,0,server-chat-argument,Le chat doit-il être désactivé?
Dialogue: 0,0:12:48.00,0:12:50.00,Default,,0,0,0,server-chat-maxchars-argument,Nombre maximum de caractères dans un message de discussion (la valeur par défaut est {})
Dialogue: 0,0:12:50.00,0:12:52.00,Default,,0,0,0,server-maxusernamelength-argument,Nombre maximum de caractères dans un nom d'utilisateur (la valeur par défaut est {})
Dialogue: 0,0:12:52.00,0:12:54.00,Default,,0,0,0,server-stats-db-file-argument,Activer les statistiques du serveur à l'aide du fichier db SQLite fourni
Dialogue: 0,0:12:54.00,0:12:56.00,Default,,0,0,0,server-startTLS-argument,Activer les connexions TLS à l'aide des fichiers de certificat dans le chemin fourni
Dialogue: 0,0:12:56.00,0:12:58.00,Default,,0,0,0,server-messed-up-motd-unescaped-placeholders,Le message du jour a des espaces réservés non échappés. Tous les signes $ doivent être doublés ($$).
Dialogue: 0,0:12:58.00,0:13:00.00,Default,,0,0,0,server-messed-up-motd-too-long,Le message du jour est trop long: {}caractères maximum, {} donnés.
Dialogue: 0,0:13:00.00,0:13:02.00,Default,,0,0,0,unknown-command-server-error,Commande inconnue {}
Dialogue: 0,0:13:02.00,0:13:04.00,Default,,0,0,0,not-json-server-error,Pas une chaîne encodée json {}
Dialogue: 0,0:13:04.00,0:13:06.00,Default,,0,0,0,line-decode-server-error,Pas une chaîne utf-8
Dialogue: 0,0:13:06.00,0:13:08.00,Default,,0,0,0,not-known-server-error,Vous devez être connu du serveur avant d'envoyer cette commande
Dialogue: 0,0:13:08.00,0:13:10.00,Default,,0,0,0,client-drop-server-error,Client drop: {} -- {}
Dialogue: 0,0:13:10.00,0:13:12.00,Default,,0,0,0,password-required-server-error,Mot de passe requis
Dialogue: 0,0:13:12.00,0:13:14.00,Default,,0,0,0,wrong-password-server-error,Mauvais mot de passe fourni
Dialogue: 0,0:13:14.00,0:13:16.00,Default,,0,0,0,hello-server-error,Pas assez d'arguments pour Hello
Dialogue: 0,0:13:16.00,0:13:18.00,Default,,0,0,0,playlist-selection-changed-notification,{} a modifié la sélection de la liste de lecture
Dialogue: 0,0:13:18.00,0:13:20.00,Default,,0,0,0,playlist-contents-changed-notification,{} a mis à jour la liste de lecture
Dialogue: 0,0:13:20.00,0:13:22.00,Default,,0,0,0,cannot-find-file-for-playlist-switch-error,Impossible de trouver le fichier {} dans les répertoires multimédias pour le changement de liste de lecture!
Dialogue: 0,0:13:22.00,0:13:24.00,Default,,0,0,0,cannot-add-duplicate-error,Impossible d'ajouter la deuxième entrée pour '{}' à la liste de lecture car aucun doublon n'est autorisé.
Dialogue: 0,0:13:24.00,0:13:26.00,Default,,0,0,0,cannot-add-unsafe-path-error,Impossible de charger automatiquement {}, car il ne se trouve pas sur un domaine approuvé. Vous pouvez basculer manuellement vers l'URL en double-cliquant dessus dans la liste de lecture et ajouter des domaines de confiance via Fichier->Avancé->Définir les domaines de confiance. Si vous faites un clic droit sur une URL, vous pouvez ajouter son domaine en tant que domaine de confiance via le menu contextuel.
Dialogue: 0,0:13:26.00,0:13:28.00,Default,,0,0,0,sharedplaylistenabled-label,Activer les listes de lecture partagées
Dialogue: 0,0:13:28.00,0:13:30.00,Default,,0,0,0,removefromplaylist-menu-label,Supprimer de la liste de lecture
Dialogue: 0,0:13:30.00,0:13:32.00,Default,,0,0,0,shuffleremainingplaylist-menu-label,Mélanger la liste de lecture restante
Dialogue: 0,0:13:32.00,0:13:34.00,Default,,0,0,0,shuffleentireplaylist-menu-label,Mélanger toute la liste de lecture
Dialogue: 0,0:13:34.00,0:13:36.00,Default,,0,0,0,undoplaylist-menu-label,Annuler la dernière modification de la liste de lecture
Dialogue: 0,0:13:36.00,0:13:38.00,Default,,0,0,0,addfilestoplaylist-menu-label,Ajouter des fichiers au bas de la liste de lecture
Dialogue: 0,0:13:38.00,0:13:40.00,Default,,0,0,0,addurlstoplaylist-menu-label,Ajouter des URL au bas de la liste de lecture
Dialogue: 0,0:13:40.00,0:13:42.00,Default,,0,0,0,editplaylist-menu-label,Modifier la liste de lecture
Dialogue: 0,0:13:42.00,0:13:44.00,Default,,0,0,0,open-containing-folder,Ouvrir le dossier contenant ce fichier
Dialogue: 0,0:13:44.00,0:13:46.00,Default,,0,0,0,addyourfiletoplaylist-menu-label,Ajoutez votre fichier à la liste de lecture
Dialogue: 0,0:13:46.00,0:13:48.00,Default,,0,0,0,addotherusersfiletoplaylist-menu-label,Ajouter le fichier de {} à la liste de lecture
Dialogue: 0,0:13:48.00,0:13:50.00,Default,,0,0,0,addyourstreamstoplaylist-menu-label,Ajoutez votre flux à la liste de lecture
Dialogue: 0,0:13:50.00,0:13:52.00,Default,,0,0,0,addotherusersstreamstoplaylist-menu-label,Ajouter {}' stream à la playlist
Dialogue: 0,0:13:52.00,0:13:54.00,Default,,0,0,0,openusersstream-menu-label,Ouvrir le flux de {}
Dialogue: 0,0:13:54.00,0:13:56.00,Default,,0,0,0,openusersfile-menu-label,Ouvrir le fichier de {}
Dialogue: 0,0:13:56.00,0:13:58.00,Default,,0,0,0,playlist-instruction-item-message,Faites glisser le fichier ici pour l'ajouter à la liste de lecture partagée.
Dialogue: 0,0:13:58.00,0:14:00.00,Default,,0,0,0,sharedplaylistenabled-tooltip,Les opérateurs de salle peuvent ajouter des fichiers à une liste de lecture synchronisée pour permettre à tout le monde de regarder facilement la même chose. Configurez les répertoires multimédias sous «Divers».
Dialogue: 0,0:14:00.00,0:14:02.00,Default,,0,0,0,playlist-empty-error,La liste de lecture est actuellement vide.
Dialogue: 0,0:14:02.00,0:14:04.00,Default,,0,0,0,playlist-invalid-index-error,Index de liste de lecture non valide

530
syncplay/messages_fr.py Normal file
View File

@ -0,0 +1,530 @@
# coding:utf8
"""French dictionary"""
# This file was mainly auto generated from ass2messages.py applied on messages_fr.ass to get these messages
# its format has been harmonized, values are always stored in doublequotes strings,
# if double quoted string in the value then they should be esacaped like this \". There is
# thus no reason to have single quoted strings. Tabs \t and newlines \n need also to be escaped.
# whith ass2messages.py which handles these issues, this is no more a nightmare to handle.
# I fixed partially messages_en.py serving as template. an entry should be added in messages.py:
# "fr": messages_fr.fr, . Produced by sosie - sos-productions.com
fr = {
"LANGUAGE": "Français",
# Client notifications
"config-cleared-notification": "Paramètres effacés. Les modifications seront enregistrées lorsque vous enregistrez une configuration valide.",
"relative-config-notification": "Fichiers de configuration relatifs chargés: {}",
"connection-attempt-notification": "Tentative de connexion à {}:{}", # Port, IP
"reconnection-attempt-notification": "Connexion avec le serveur perdue, tentative de reconnexion",
"disconnection-notification": "Déconnecté du serveur",
"connection-failed-notification": "Échec de la connexion avec le serveur",
"connected-successful-notification": "Connexion réussie au serveur",
"retrying-notification": "%s, nouvelle tentative dans %d secondes...", # Seconds
"reachout-successful-notification": "Vous avez atteint {} ({})",
"rewind-notification": "Retour en arrière en raison du décalage de temps avec {}", # User
"fastforward-notification": "Avance rapide en raison du décalage de temps avec {}", # User
"slowdown-notification": "Ralentissement dû au décalage de temps avec {}", # User
"revert-notification": "Retour à la vitesse normale",
"pause-notification": "{} en pause", # User
"unpause-notification": "{} non suspendu", # User
"seek-notification": "{} est passé de {} à {}", # User, from time, to time
"current-offset-notification": "Décalage actuel: {}secondes", # Offset
"media-directory-list-updated-notification": "Les répertoires multimédias Syncplay ont été mis à jour.",
"room-join-notification": "{} a rejoint la salle: '{}'", # User
"left-notification": "{} est parti", # User
"left-paused-notification": "{} restants, {} en pause", # User who left, User who paused
"playing-notification": "{} est en train de jouer '{}' ({})", # User, file, duration
"playing-notification/room-addendum": "dans le salon: '{}'", # Room
"not-all-ready": "Pas prêt: {}", # Usernames
"all-users-ready": "Tout le monde est prêt ({} utilisateurs)", # Number of ready users
"ready-to-unpause-notification": "Vous êtes maintenant défini comme prêt - réactivez la pause pour réactiver",
"set-as-ready-notification": "Vous êtes maintenant défini comme prêt",
"set-as-not-ready-notification": "Vous êtes maintenant défini comme non prêt",
"autoplaying-notification": "Lecture automatique dans {}...", # Number of seconds until playback will start
"identifying-as-controller-notification": "Identification en tant qu'opérateur de salle avec le mot de passe '{}'...",
"failed-to-identify-as-controller-notification": "{} n'a pas réussi à s'identifier en tant qu'opérateur de salle.",
"authenticated-as-controller-notification": "{} authentifié en tant qu'opérateur de salle",
"created-controlled-room-notification": "Salle gérée créée «{}» avec le mot de passe «{}». Veuillez conserver ces informations pour référence future !\n\nDans les salons gérés, tout le monde est synchronisé avec le ou les opérateurs de salon qui sont les seuls à pouvoir mettre en pause, reprendre, se déplacer dans la lecture et modifier la liste de lecture.\n\nVous devez demander aux spectateurs réguliers de rejoindre le salon '{}' mais les opérateurs de salon peuvent rejoindre le salon '{}' pour s'authentifier automatiquement.", # RoomName, operatorPassword, roomName, roomName:operatorPassword
"file-different-notification": "Le fichier que vous lisez semble être différent de celui de {}", # User
"file-differences-notification": "Votre fichier diffère de la (des) manière(s) suivante(s): {}", # Differences
"room-file-differences": "Différences de fichiers: {}", # File differences (filename, size, and/or duration)
"file-difference-filename": "Nom",
"file-difference-filesize": "Taille",
"file-difference-duration": "durée",
"alone-in-the-room": "Vous êtes seul dans le salon",
"different-filesize-notification": "(leur taille de fichier est différente de la vôtre!)",
"userlist-playing-notification": "{} est en train de jouer:", # Username
"file-played-by-notification": "Fichier: {} est lu par:", # File
"no-file-played-notification": "{} ne lit pas de fichier", # Username
"notplaying-notification": "Les personnes qui ne lisent aucun fichier:",
"userlist-room-notification": "Dans la chambre '{}':", # Room
"userlist-file-notification": "Fichier",
"controller-userlist-userflag": "Opérateur",
"ready-userlist-userflag": "Prêt",
"update-check-failed-notification": "Impossible de vérifier automatiquement si Syncplay {} est à jour. Vous voulez visiter https://syncplay.pl/ pour vérifier manuellement les mises à jour?", # Syncplay version
"syncplay-uptodate-notification": "Syncplay est à jour",
"syncplay-updateavailable-notification": "Une nouvelle version de Syncplay est disponible. Voulez-vous visiter la page de publication?",
"mplayer-file-required-notification": "Syncplay à l'aide de mplayer nécessite que vous fournissiez un fichier au démarrage",
"mplayer-file-required-notification/example": "Exemple d'utilisation: syncplay [options] [url|chemin/]nom de fichier",
"mplayer2-required": "Syncplay est incompatible avec MPlayer 1.x, veuillez utiliser mplayer2 ou mpv",
"unrecognized-command-notification": "commande non reconnue",
"commandlist-notification": "Commandes disponibles:",
"commandlist-notification/room": "\tr [nom] - changer de chambre",
"commandlist-notification/list": "\tl - afficher la liste des utilisateurs",
"commandlist-notification/undo": "\tu - annuler la dernière recherche",
"commandlist-notification/pause": "\tp - basculer sur pause",
"commandlist-notification/seek": "\t[s][+-]temps - recherche la valeur de temps donnée, si + ou - n'est pas spécifié c'est le temps absolu en secondes ou min:sec",
"commandlist-notification/help": "\th - cette aide",
"commandlist-notification/toggle": "\tt - bascule si vous êtes prêt à regarder ou non",
"commandlist-notification/create": "\tc [nom] - crée une salle gérée en utilisant le nom de la salle actuelle",
"commandlist-notification/auth": "\tun [mot de passe] - s'authentifier en tant qu'opérateur de salle avec le mot de passe opérateur",
"commandlist-notification/chat": "\tch [message] - envoyer un message de chat dans une pièce",
"commandList-notification/queue": "\tqa [fichier/url] - ajoute un fichier ou une URL au bas de la liste de lecture",
"commandList-notification/playlist": "\tql - afficher la liste de lecture actuelle",
"commandList-notification/select": "\tqs [index] - sélectionnez l'entrée donnée dans la liste de lecture",
"commandList-notification/delete": "\tqd [index] - supprime l'entrée donnée de la liste de lecture",
"syncplay-version-notification": "Version de Syncplay: {}", # syncplay.version
"more-info-notification": "Plus d'informations disponibles sur: {}", # projectURL
"gui-data-cleared-notification": "Syncplay a effacé les données d'état de chemin et de fenêtre utilisées par l'interface graphique.",
"language-changed-msgbox-label": "La langue sera modifiée lorsque vous exécuterez Syncplay.",
"promptforupdate-label": "Est-ce que Syncplay peut vérifier automatiquement les mises à jour de temps en temps?",
"media-player-latency-warning": "Avertissement: Le lecteur multimédia a mis {}secondes à répondre. Si vous rencontrez des problèmes de synchronisation, fermez les applications pour libérer des ressources système, et si cela ne fonctionne pas, essayez un autre lecteur multimédia.", # Seconds to respond
"mpv-unresponsive-error": "mpv n'a pas répondu pendant {} secondes et semble donc avoir mal fonctionné. Veuillez redémarrer Syncplay.", # Seconds to respond
# Client prompts
"enter-to-exit-prompt": "Appuyez sur entrée pour quitter",
# Client errors
"missing-arguments-error": "Certains arguments nécessaires sont manquants, reportez-vous à --help",
"server-timeout-error": "La connexion avec le serveur a expiré",
"mpc-slave-error": "Impossible de démarrer MPC en mode esclave!",
"mpc-version-insufficient-error": "La version MPC n'est pas suffisante, veuillez utiliser `mpc-hc` >= `{}`",
"mpc-be-version-insufficient-error": "La version MPC n'est pas suffisante, veuillez utiliser `mpc-be` >= `{}`",
"mpv-version-error": "Syncplay n'est pas compatible avec cette version de mpv. Veuillez utiliser une version différente de mpv (par exemple Git HEAD).",
"mpv-failed-advice": "La raison pour laquelle mpv ne peut pas démarrer peut être due à l'utilisation d'arguments de ligne de commande non pris en charge ou à une version non prise en charge de mpv.",
"player-file-open-error": "Le lecteur n'a pas réussi à ouvrir le fichier",
"player-path-error": "Le chemin du lecteur n'est pas défini correctement. Les lecteurs pris en charge sont : mpv, mpv.net, VLC, MPC-HC, MPC-BE, mplayer2 et IINA",
"hostname-empty-error": "Le nom d'hôte ne peut pas être vide",
"empty-error": "{} ne peut pas être vide", # Configuration
"media-player-error": "Media player error: \"{}\"", # Error line
"unable-import-gui-error": "Impossible d'importer les bibliothèques GUI. Si vous n'avez pas installé PySide, vous devrez l'installer pour que l'interface graphique fonctionne.",
"unable-import-twisted-error": "Impossible d'importer Twisted. Veuillez installer Twisted v16.4.0 ou une version ultérieure.",
"arguments-missing-error": "Certains arguments nécessaires sont manquants, reportez-vous à --help",
"unable-to-start-client-error": "Impossible de démarrer le client",
"player-path-config-error": "Le chemin du lecteur n'est pas défini correctement. Les lecteurs pris en charge sont : mpv, mpv.net, VLC, MPC-HC, MPC-BE, mplayer2 et IINA.",
"no-file-path-config-error": "Le fichier doit être sélectionné avant de démarrer votre lecteur",
"no-hostname-config-error": "Le nom d'hôte ne peut pas être vide",
"invalid-port-config-error": "Le port doit être valide",
"empty-value-config-error": "{} ne peut pas être vide", # Config option
"not-json-error": "Pas une chaîne encodée en json",
"hello-arguments-error": "Pas assez d'arguments pour Hello", # DO NOT TRANSLATE
"version-mismatch-error": "Non-concordance entre les versions du client et du serveur",
"vlc-failed-connection": "Échec de la connexion à VLC. Si vous n'avez pas installé syncplay.lua et utilisez la dernière version de VLC, veuillez vous référer à https://syncplay.pl/LUA/ pour obtenir des instructions. Syncplay et VLC 4 ne sont actuellement pas compatibles, utilisez donc VLC 3 ou une alternative telle que mpv.",
"vlc-failed-noscript": "VLC a signalé que le script d'interface syncplay.lua n'a pas été installé. Veuillez vous référer à https://syncplay.pl/LUA/ pour obtenir des instructions.",
"vlc-failed-versioncheck": "Cette version de VLC n'est pas prise en charge par Syncplay.",
"vlc-initial-warning": "VLC ne fournit pas toujours des informations de position précises à Syncplay, en particulier pour les fichiers .mp4 et .avi. Si vous rencontrez des problèmes de recherche erronée, essayez un autre lecteur multimédia tel que <a href=\"https://mpv.io/\">mpv</a> (ou <a href=\"https://github.com/stax76/mpv.net/\">mpv.net</a> pour les utilisateurs de Windows).",
"feature-sharedPlaylists": "listes de lecture partagées", # used for not-supported-by-server-error
"feature-chat": "chat", # used for not-supported-by-server-error
"feature-readiness": "préparation", # used for not-supported-by-server-error
"feature-managedRooms": "salons gérés", # used for not-supported-by-server-error
"not-supported-by-server-error": "La fonctionnalité {} n'est pas prise en charge par ce serveur.", # feature
"shared-playlists-not-supported-by-server-error": "La fonctionnalité de listes de lecture partagées peut ne pas être prise en charge par le serveur. Pour s'assurer qu'il fonctionne correctement, il faut un serveur exécutant Syncplay {}+, mais le serveur exécute Syncplay {}.", # minVersion, serverVersion
"shared-playlists-disabled-by-server-error": "La fonctionnalité de liste de lecture partagée a été désactivée dans la configuration du serveur. Pour utiliser cette fonctionnalité, vous devrez vous connecter à un autre serveur.",
"invalid-seek-value": "Valeur de recherche non valide",
"invalid-offset-value": "Valeur de décalage non valide",
"switch-file-not-found-error": "Impossible de passer au fichier ''. Syncplay recherche dans les répertoires multimédias spécifiés.", # File not found
"folder-search-timeout-error": "La recherche de médias dans les répertoires de médias a été abandonnée car la recherche dans '{}' a pris trop de temps. Cela se produira si vous sélectionnez un dossier avec trop de sous-dossiers dans votre liste de dossiers multimédias à parcourir. Pour que le basculement automatique des fichiers fonctionne à nouveau, veuillez sélectionner Fichier->Définir les répertoires multimédias dans la barre de menu et supprimez ce répertoire ou remplacez-le par un sous-dossier approprié. Si le dossier est correct, vous pouvez le réactiver en sélectionnant Fichier->Définir les répertoires multimédias et en appuyant sur «OK».", # Folder
"folder-search-first-file-timeout-error": "La recherche de média dans '{}' a été abandonnée car elle a pris trop de temps pour accéder au répertoire. Cela peut arriver s'il s'agit d'un lecteur réseau ou si vous configurez votre lecteur pour qu'il ralentisse après une période d'inactivité. Pour que le basculement automatique des fichiers fonctionne à nouveau, accédez à Fichier-> Définir les répertoires multimédias et supprimez le répertoire ou résolvez le problème (par exemple en modifiant les paramètres d'économie d'énergie).", # Folder
"added-file-not-in-media-directory-error": "Vous avez chargé un fichier dans '{}' qui n'est pas un répertoire média connu. Vous pouvez l'ajouter en tant que répertoire multimédia en sélectionnant Fichier->Définir les répertoires multimédias dans la barre de menus.", # Folder
"no-media-directories-error": "Aucun répertoire multimédia n'a été défini. Pour que les fonctionnalités de liste de lecture partagée et de changement de fichier fonctionnent correctement, sélectionnez Fichier-> Définir les répertoires multimédias et spécifiez où Syncplay doit rechercher les fichiers multimédias.",
"cannot-find-directory-error": "Impossible de trouver le répertoire multimédia '{}'. Pour mettre à jour votre liste de répertoires multimédias, veuillez sélectionner Fichier->Définir les répertoires multimédias dans la barre de menu et spécifiez où Syncplay doit chercher pour trouver les fichiers multimédias.",
"failed-to-load-server-list-error": "Échec du chargement de la liste des serveurs publics. Veuillez visiter https://www.syncplay.pl/ dans votre navigateur.",
# Client arguments
"argument-description": "Solution pour synchroniser la lecture de plusieurs instances de lecteur multimédia sur le réseau.",
"argument-epilog": "Si aucune option n'est fournie, les valeurs _config seront utilisées",
"nogui-argument": "masquer l'interface graphique",
"host-argument": "adresse du serveur",
"name-argument": "nom d'utilisateur souhaité",
"debug-argument": "Mode débogage",
"force-gui-prompt-argument": "faire apparaître l'invite de configuration",
"no-store-argument": "ne pas stocker de valeurs dans .syncplay",
"room-argument": "salon par défaut",
"password-argument": "Mot de passe du serveur",
"player-path-argument": "chemin d'accès à l'exécutable de votre lecteur",
"file-argument": "fichier à lire",
"args-argument": 'player options, if you need to pass options starting with - prepend them with single \'--\' argument',
"clear-gui-data-argument": "réinitialise les données GUI du chemin et de l'état de la fenêtre stockées en tant que QSettings",
"language-argument": "langue pour les messages Syncplay (de/en/ru/it/es/pt_BR/pt_PT/tr/fr)",
"version-argument": "imprime votre version",
"version-message": "Vous utilisez Syncplay version {} ({})",
"load-playlist-from-file-argument": "charge la liste de lecture à partir d'un fichier texte (une entrée par ligne)",
# Client labels
"config-window-title": "configuration Syncplay",
"connection-group-title": "Paramètres de connexion",
"host-label": "Adresse du serveur:",
"name-label": "Nom d'utilisateur (facultatif):",
"password-label": "Mot de passe du serveur (le cas échéant):",
"room-label": "Salon par défaut:",
"roomlist-msgbox-label": "Modifier la liste des salons (une par ligne)",
"media-setting-title": "Paramètres du lecteur multimédia",
"executable-path-label": "Chemin d'accès au lecteur multimédia:",
"media-path-label": "Chemin d'accès à la vidéo (facultatif):",
"player-arguments-label": "Arguments du joueur (le cas échéant):",
"browse-label": "Parcourir",
"update-server-list-label": "Mettre à jour la liste",
"more-title": "Afficher plus de paramètres",
"never-rewind-value": "Jamais",
"seconds-suffix": "secs",
"privacy-sendraw-option": "Envoyer brut",
"privacy-sendhashed-option": "Envoyer haché",
"privacy-dontsend-option": "Ne pas envoyer",
"filename-privacy-label": "Informations sur le nom de fichier:",
"filesize-privacy-label": "Informations sur la taille du fichier:",
"checkforupdatesautomatically-label": "Rechercher automatiquement les mises à jour de Syncplay",
"autosavejoinstolist-label": "Ajouter les salons que vous rejoignez à la liste des salons",
"slowondesync-label": "Ralentissement en cas de désynchronisation mineure (non pris en charge sur MPC-HC/BE)",
"rewindondesync-label": "Retour en arrière en cas de désynchronisation majeure (recommandé)",
"fastforwardondesync-label": "Avance rapide en cas de retard (recommandé)",
"dontslowdownwithme-label": "Ne jamais ralentir ou rembobiner les autres (expérimental)",
"pausing-title": "Pause",
"pauseonleave-label": "Pause lorsque l'utilisateur quitte (par exemple s'il est déconnecté)",
"readiness-title": "État de préparation initial",
"readyatstart-label": "Définissez-moi comme «prêt à regarder» par défaut",
"forceguiprompt-label": "Ne pas toujours afficher la fenêtre de configuration Syncplay", # (Inverted)
"showosd-label": "Activer les Messages OSD",
"showosdwarnings-label": "Inclure des avertissements (par exemple, lorsque les fichiers sont différents, les utilisateurs ne sont pas prêts)",
"showsameroomosd-label": "Inclure des événements dans votre salon",
"shownoncontrollerosd-label": "Inclure les événements des non-opérateurs dans les salons gérés",
"showdifferentroomosd-label": "Inclure des événements dans d'autres salons",
"showslowdownosd-label": "Inclure les notifications de ralentissement/annulation",
"language-label": "Langue:",
"automatic-language": "Défaut ({})", # Default language
"showdurationnotification-label": "Avertir des incohérences de durée de média",
"basics-label": "Réglages de base",
"readiness-label": "Jouer pause",
"misc-label": "Divers",
"core-behaviour-title": "Comportement du salon principal",
"syncplay-internals-title": "procédures internes",
"syncplay-mediasearchdirectories-title": "Répertoires pour rechercher des médias",
"syncplay-mediasearchdirectories-label": "Répertoires pour rechercher des médias (un chemin par ligne)",
"sync-label": "Synchroniser",
"sync-otherslagging-title": "Si d'autres sont à la traîne...",
"sync-youlaggging-title": "Si vous êtes à la traîne...",
"messages-label": "Messages",
"messages-osd-title": "Paramètres d'affichage à l'écran",
"messages-other-title": "Autres paramètres d'affichage",
"chat-label": "Chat",
"privacy-label": "Sécurité données", # Currently unused, but will be brought back if more space is needed in Misc tab
"privacy-title": "Paramètres de confidentialité",
"unpause-title": "Si vous appuyez sur play, définissez comme prêt et:",
"unpause-ifalreadyready-option": "Annuler la pause si déjà défini comme prêt",
"unpause-ifothersready-option": "Reprendre la pause si déjà prêt ou si d'autres personnes dans la pièce sont prêtes (par défaut)",
"unpause-ifminusersready-option": "Annuler la pause si déjà prêt ou si tous les autres sont prêts et utilisateurs minimum prêts",
"unpause-always": "Toujours reprendre",
"syncplay-trusteddomains-title": "Domaines de confiance (pour les services de streaming et le contenu hébergé)",
"chat-title": "Saisie du message de discussion",
"chatinputenabled-label": "Activer la saisie de discussion via mpv",
"chatdirectinput-label": "Autoriser la saisie de discussion instantanée (éviter d'avoir à appuyer sur la touche Entrée pour discuter)",
"chatinputfont-label": "Police de caractères pour la saisie sur le Chat ",
"chatfont-label": "Définir la fonte",
"chatcolour-label": "Définir la couleur",
"chatinputposition-label": "Position de la zone de saisie des messages dans mpv",
"chat-top-option": "Haut",
"chat-middle-option": "Milieu",
"chat-bottom-option": "Bas",
"chatoutputheader-label": "Sortie du message de discussion",
"chatoutputfont-label": "Police de sortie du chat",
"chatoutputenabled-label": "Activer la sortie du chat dans le lecteur multimédia (mpv uniquement pour l'instant)",
"chatoutputposition-label": "Mode de sortie",
"chat-chatroom-option": "Style de salon de discussion",
"chat-scrolling-option": "Style de défilement",
"mpv-key-tab-hint": "[TAB] pour basculer l'accès aux raccourcis des touches de la ligne alphabétique.",
"mpv-key-hint": "[ENTER] pour envoyer un message. [ESC] pour quitter le mode chat.",
"alphakey-mode-warning-first-line": "Vous pouvez temporairement utiliser les anciennes liaisons mpv avec les touches az.",
"alphakey-mode-warning-second-line": "Appuyez sur [TAB] pour revenir au mode de discussion Syncplay.",
"help-label": "Aider",
"reset-label": "Réinitialiser",
"run-label": "Exécuter Syncplay",
"storeandrun-label": "Stocker la configuration et exécuter Syncplay",
"contact-label": "Feel free to e-mail <a href=\"mailto:dev@syncplay.pl\"><nobr>dev@syncplay.pl</nobr></a>, <a href=\"https://github.com/Syncplay/syncplay/issues\"><nobr>create an issue</nobr></a> to report a bug/problem via GitHub, <a href=\"https://github.com/Syncplay/syncplay/discussions\"><nobr>start a discussion</nobr></a> to make a suggestion or ask a question via GitHub, <a href=\"https://www.facebook.com/SyncplaySoftware\"><nobr>like us on Facebook</nobr></a>, <a href=\"https://twitter.com/Syncplay/\"><nobr>follow us on Twitter</nobr></a>, or visit <a href=\"https://syncplay.pl/\"><nobr>https://syncplay.pl/</nobr></a>. Do not use Syncplay to send sensitive information.",
"joinroom-label": "Rejoindre la salle",
"joinroom-menu-label": "Rejoindre la salle {}",
"seektime-menu-label": "Chercher le temps",
"undoseek-menu-label": "Annuler la recherche",
"play-menu-label": "Jouer",
"pause-menu-label": "Pause",
"playbackbuttons-menu-label": "Afficher les boutons de lecture",
"autoplay-menu-label": "Afficher le bouton de lecture automatique",
"autoplay-guipushbuttonlabel": "Jouer quand tout est prêt",
"autoplay-minimum-label": "Utilisateurs minimum:",
"sendmessage-label": "Envoyer",
"ready-guipushbuttonlabel": "Je suis prêt à regarder !",
"roomuser-heading-label": "Salon / Utilisateur",
"size-heading-label": "Taille",
"duration-heading-label": "Durée",
"filename-heading-label": "Nom de fichier",
"notifications-heading-label": "Notifications",
"userlist-heading-label": "Liste de qui joue quoi",
"browseformedia-label": "Parcourir les fichiers multimédias",
"file-menu-label": "&Fichier", # & precedes shortcut key
"openmedia-menu-label": "&Ouvrir le fichier multimédia",
"openstreamurl-menu-label": "Ouvrir l'URL du &flux multimédia",
"setmediadirectories-menu-label": "Définir les &répertoires multimédias",
"loadplaylistfromfile-menu-label": "&Charger la liste de lecture à partir du fichier",
"saveplaylisttofile-menu-label": "&Enregistrer la liste de lecture dans un fichier",
"exit-menu-label": "Sortir",
"advanced-menu-label": "&Avancée",
"window-menu-label": "&Fenêtre",
"setoffset-menu-label": "Définir &décalage",
"createcontrolledroom-menu-label": "&Créer une salon à gérer",
"identifyascontroller-menu-label": "&Identifier en tant qu'opérateur de salon",
"settrusteddomains-menu-label": "Définir des &domaines de confiance",
"addtrusteddomain-menu-label": "Ajouter {} comme domaine de confiance", # Domain
"edit-menu-label": "&Éditer",
"cut-menu-label": "Couper",
"copy-menu-label": "&Copier",
"paste-menu-label": "&Coller",
"selectall-menu-label": "&Tout sélectionner",
"playback-menu-label": "&Relecture",
"help-menu-label": "&Aide",
"userguide-menu-label": "Ouvrir le &guide de l'utilisateur",
"update-menu-label": "Rechercher et mettre à jour",
"startTLS-initiated": "Tentative de connexion sécurisée",
"startTLS-secure-connection-ok": "Connexion sécurisée établie ({})",
"startTLS-server-certificate-invalid": "Échec de la Connexion Sécurisée. Le serveur utilise un certificat de sécurité non valide. Cette communication pourrait être interceptée par un tiers. Pour plus de détails et de dépannage, voir <a href=\"https://syncplay.pl/trouble\">ici</a> .",
"startTLS-server-certificate-invalid-DNS-ID": "Syncplay ne fait pas confiance à ce serveur car il utilise un certificat qui n'est pas valide pour son nom d'hôte.",
"startTLS-not-supported-client": "Ce client ne prend pas en charge TLS",
"startTLS-not-supported-server": "Ce serveur ne prend pas en charge TLS",
# TLS certificate dialog
"tls-information-title": "Détails du certificat",
"tls-dialog-status-label": "<strong>Syncplay utilise une connexion cryptée à {}.</strong>",
"tls-dialog-desc-label": "Le cryptage avec un certificat numérique préserve la confidentialité des informations lorsqu'elles sont envoyées vers ou depuis le serveur {}.",
"tls-dialog-connection-label": "Informations chiffrées à l'aide de Transport Layer Security (TLS), version {} avec la suite de chiffrement: {}.",
"tls-dialog-certificate-label": "Certificat délivré par {} valable jusqu'au {}.",
# About dialog
"about-menu-label": "&À propos de la lecture synchronisée",
"about-dialog-title": "À propos de Syncplay",
"about-dialog-release": "Version {} release {}",
"about-dialog-license-text": "Sous licence Apache, version 2.0",
"about-dialog-license-button": "Licence",
"about-dialog-dependencies": "Dépendances",
"setoffset-msgbox-label": "Définir le décalage",
"offsetinfo-msgbox-label": "Offset (voir https://syncplay.pl/guide/ pour les instructions d'utilisation):",
"promptforstreamurl-msgbox-label": "Ouvrir l'URL du flux multimédia",
"promptforstreamurlinfo-msgbox-label": "URL de diffusion",
"addfolder-label": "Ajouter le dossier",
"adduris-msgbox-label": "Ajouter des URL à la liste de lecture (une par ligne)",
"editplaylist-msgbox-label": "Définir la liste de lecture (une par ligne)",
"trusteddomains-msgbox-label": "Domaines vers lesquels vous pouvez basculer automatiquement (un par ligne)",
"createcontrolledroom-msgbox-label": "Créer un salon à gérer",
"controlledroominfo-msgbox-label": "Enter name of managed room\r\n(see https://syncplay.pl/guide/ for usage instructions):",
"identifyascontroller-msgbox-label": "S'identifier en tant qu'opérateur de salon",
"identifyinfo-msgbox-label": "Enter operator password for this room\r\n(see https://syncplay.pl/guide/ for usage instructions):",
"public-server-msgbox-label": "Sélectionnez le serveur public pour cette session de visualisation",
"megabyte-suffix": "Mo",
# Tooltips
"host-tooltip": "Nom d'hôte ou IP auquel se connecter, incluant éventuellement le port (par exemple syncplay.pl:8999). Uniquement synchronisé avec des personnes sur le même serveur/port.",
"name-tooltip": "Surnom sous lequel vous serez connu. Pas d'inscription, donc peut facilement changer plus tard. Nom aléatoire généré si aucun n'est spécifié.",
"password-tooltip": "Les mots de passe ne sont nécessaires que pour se connecter à des serveurs privés.",
"room-tooltip": "Le salon à rejoindre lors de la connexion peut être presque n'importe quoi, mais vous ne serez synchronisé qu'avec des personnes dans le même salon.",
"edit-rooms-tooltip": "Modifier la liste des salons.",
"executable-path-tooltip": "Emplacement du lecteur multimédia pris en charge que vous avez choisi (mpv, mpv.net, VLC, MPC-HC/BE, mplayer2 ou IINA).",
"media-path-tooltip": "Emplacement de la vidéo ou du flux à ouvrir. Nécessaire pour mplayer2.",
"player-arguments-tooltip": "Arguments/commutateurs de ligne de commande supplémentaires à transmettre à ce lecteur multimédia.",
"mediasearcdirectories-arguments-tooltip": "Répertoires dans lesquels Syncplay recherchera les fichiers multimédias, par exemple lorsque vous utilisez la fonctionalité cliquer pour basculer. Syncplay recherchera récursivement dans les sous-dossiers.",
"more-tooltip": "Afficher les paramètres moins fréquemment utilisés.",
"filename-privacy-tooltip": "Mode de confidentialité pour l'envoi du nom de fichier en cours de lecture au serveur.",
"filesize-privacy-tooltip": "Mode de confidentialité pour l'envoi de la taille du fichier en cours de lecture au serveur.",
"privacy-sendraw-tooltip": "Envoyez ces informations sans brouillage. Il s'agit de l'option par défaut avec la plupart des fonctionnalités.",
"privacy-sendhashed-tooltip": "Envoyez une version hachée des informations, les rendant moins visibles pour les autres clients.",
"privacy-dontsend-tooltip": "N'envoyez pas ces informations au serveur. Cela garantit une confidentialité maximale.",
"checkforupdatesautomatically-tooltip": "Vérifiez régulièrement sur le site Web de Syncplay si une nouvelle version de Syncplay est disponible.",
"autosavejoinstolist-tooltip": "Lorsque vous rejoignez un salon sur un serveur, mémorisez automatiquement le nom de la salle dans la liste des salons à rejoindre.",
"slowondesync-tooltip": "Réduisez temporairement le taux de lecture si nécessaire pour vous synchroniser avec les autres téléspectateurs. Non pris en charge sur MPC-HC/BE.",
"dontslowdownwithme-tooltip": "Cela signifie que les autres ne sont pas ralentis ou rembobinés si votre lecture est en retard. Utile pour les opérateurs de salon.",
"pauseonleave-tooltip": "Mettez la lecture en pause si vous êtes déconnecté ou si quelqu'un quitte votre salon.",
"readyatstart-tooltip": "Définissez-vous comme «prêt» au début (sinon, vous êtes défini comme «pas prêt» jusqu'à ce que vous changiez votre état de préparation)",
"forceguiprompt-tooltip": "La boîte de dialogue de configuration ne s'affiche pas lors de l'ouverture d'un fichier avec Syncplay.", # (Inverted)
"nostore-tooltip": "Exécutez Syncplay avec la configuration donnée, mais ne stockez pas les modifications de manière permanente.", # (Inverted)
"rewindondesync-tooltip": "Revenez en arrière au besoin pour vous synchroniser. La désactivation de cette option peut entraîner des désynchronisations majeures!",
"fastforwardondesync-tooltip": "Avancez en cas de désynchronisation avec l'opérateur de la salle (ou votre position factice si l'option «Ne jamais ralentir ou rembobiner les autres» est activée).",
"showosd-tooltip": "Envoie des messages Syncplay à l'OSD du lecteur multimédia.",
"showosdwarnings-tooltip": "Afficher des avertissements en cas de lecture d'un fichier différent, seul dans la pièce, utilisateurs non prêts, etc.",
"showsameroomosd-tooltip": "Afficher les notifications OSD pour les événements liés à l'utilisateur du salon.",
"shownoncontrollerosd-tooltip": "Afficher les notifications OSD pour les événements relatifs aux non-opérateurs qui se trouvent dans les salles gérées.",
"showdifferentroomosd-tooltip": "Afficher les notifications OSD pour les événements liés à l'absence de l'utilisateur du salon.",
"showslowdownosd-tooltip": "Afficher les notifications de ralentissement / de retour au décalage temps.",
"showdurationnotification-tooltip": "Utile lorsqu'un segment dans un fichier en plusieurs parties est manquant, mais peut entraîner des faux positifs.",
"language-tooltip": "Langue à utiliser par Syncplay.",
"unpause-always-tooltip": "Si vous appuyez sur unpause, cela vous définit toujours comme prêt et non-pause, plutôt que de simplement vous définir comme prêt.",
"unpause-ifalreadyready-tooltip": "Si vous appuyez sur unpause lorsque vous n'êtes pas prêt, cela vous mettra comme prêt - appuyez à nouveau sur unpause pour reprendre la pause.",
"unpause-ifothersready-tooltip": "Si vous appuyez sur unpause lorsque vous n'êtes pas prêt, il ne reprendra la pause que si d'autres sont prêts.",
"unpause-ifminusersready-tooltip": "Si vous appuyez sur annuler la pause lorsqu'il n'est pas prêt, il ne s'arrêtera que si d'autres personnes sont prêtes et que le seuil minimal d'utilisateurs est atteint.",
"trusteddomains-arguments-tooltip": "Domaines vers lesquels Syncplay peut basculer automatiquement lorsque les listes de lecture partagées sont activées.",
"chatinputenabled-tooltip": "Activer la saisie du chat dans mpv (appuyez sur Entrée pour discuter, Entrée pour envoyer, Échap pour annuler)",
"chatdirectinput-tooltip": "Évitez d'avoir à appuyer sur «enter» pour passer en mode de saisie de discussion dans mpv. Appuyez sur TAB dans mpv pour désactiver temporairement cette fonctionnalité.",
"font-label-tooltip": "Police utilisée lors de la saisie de messages de discussion dans mpv. Côté client uniquement, n'affecte donc pas ce que les autres voient.",
"set-input-font-tooltip": "Famille de polices utilisée lors de la saisie de messages de discussion dans mpv. Côté client uniquement, n'affecte donc pas ce que les autres voient.",
"set-input-colour-tooltip": "Couleur de police utilisée lors de la saisie de messages de discussion dans mpv. Côté client uniquement, n'affecte donc pas ce que les autres voient.",
"chatinputposition-tooltip": "Emplacement dans mpv où le texte d'entrée de discussion apparaîtra lorsque vous appuyez sur Entrée et tapez.",
"chatinputposition-top-tooltip": "Placez l'entrée de discussion en haut de la fenêtre mpv.",
"chatinputposition-middle-tooltip": "Placez l'entrée de discussion au point mort de la fenêtre mpv.",
"chatinputposition-bottom-tooltip": "Placez l'entrée de discussion en bas de la fenêtre mpv.",
"chatoutputenabled-tooltip": "Afficher les messages de discussion dans l'OSD (si pris en charge par le lecteur multimédia).",
"font-output-label-tooltip": "Police de sortie du chat.",
"set-output-font-tooltip": "Police utilisée pour l'affichage des messages de discussion.",
"chatoutputmode-tooltip": "Comment les messages de chat sont affichés.",
"chatoutputmode-chatroom-tooltip": "Affichez les nouvelles lignes de discussion directement sous la ligne précédente.",
"chatoutputmode-scrolling-tooltip": "Faites défiler le texte du chat de droite à gauche.",
"help-tooltip": "Ouvre le guide de l'utilisateur de Syncplay.pl.",
"reset-tooltip": "Réinitialisez tous les paramètres à la configuration par défaut.",
"update-server-list-tooltip": "Connectez-vous à syncplay.pl pour mettre à jour la liste des serveurs publics.",
"sslconnection-tooltip": "Connecté en toute sécurité au serveur. Cliquez pour obtenir les détails du certificat.",
"joinroom-tooltip": "Quitter la salle actuelle et rejoindre le salon spécifié.",
"seektime-msgbox-label": "Aller au temps spécifié (en secondes / min:sec). Utilisez +/- pour la recherche relative.",
"ready-tooltip": "Indique si vous êtes prêt à regarder.",
"autoplay-tooltip": "Lecture automatique lorsque tous les utilisateurs qui ont un indicateur de disponibilité sont prêts et que le seuil d'utilisateur minimum est atteint.",
"switch-to-file-tooltip": "Double-cliquez pour passer à {}", # Filename
"sendmessage-tooltip": "Envoyer un message au salon",
# In-userlist notes (GUI)
"differentsize-note": "Différentes tailles!",
"differentsizeandduration-note": "Taille et durée différentes !",
"differentduration-note": "Durée différente !",
"nofile-note": "(Aucun fichier en cours de lecture)",
# Server messages to client
"new-syncplay-available-motd-message": "Vous utilisez Syncplay {} mais une version plus récente est disponible sur https://syncplay.pl", # ClientVersion
# Server notifications
"welcome-server-notification": "Bienvenue sur le serveur Syncplay, ver.", # version
"client-connected-room-server-notification": "({2}) connecté à la salle '{1}'", # username, host, room
"client-left-server-notification": "a quitté le serveur", # name
"no-salt-notification": "VEUILLEZ NOTER: Pour permettre aux mots de passe d'opérateur de salle générés par cette instance de serveur de fonctionner lorsque le serveur est redémarré, veuillez ajouter l'argument de ligne de commande suivant lors de l'exécution du serveur Syncplay à l'avenir: --salt {}", # Salt
# Server arguments
"server-argument-description": "Solution pour synchroniser la lecture de plusieurs instances de lecteur multimédia sur le réseau. Instance de serveur",
"server-argument-epilog": "Si aucune option n'est fournie, les valeurs _config seront utilisées",
"server-port-argument": "port TCP du serveur",
"server-password-argument": "Mot de passe du serveur",
"server-isolate-room-argument": "faut-il isoler les salons ?",
"server-salt-argument": "chaîne aléatoire utilisée pour générer les mots de passe des salons gérés",
"server-disable-ready-argument": "désactiver la fonction de préparation",
"server-motd-argument": "chemin vers le fichier à partir duquel motd sera récupéré",
"server-chat-argument": "Le chat doit-il être désactivé?",
"server-chat-maxchars-argument": "Nombre maximum de caractères dans un message de discussion (la valeur par défaut est {})", # Default number of characters
"server-maxusernamelength-argument": "Nombre maximum de caractères dans un nom d'utilisateur (la valeur par défaut est {})",
"server-stats-db-file-argument": "Activer les statistiques du serveur à l'aide du fichier db SQLite fourni",
"server-startTLS-argument": "Activer les connexions TLS à l'aide des fichiers de certificat dans le chemin fourni",
"server-messed-up-motd-unescaped-placeholders": "Le message du jour a des espaces réservés non échappés. Tous les signes $ doivent être doublés ($$).",
"server-messed-up-motd-too-long": "Le message du jour est trop long: {}caractères maximum, {} donnés.",
# Server errors
"unknown-command-server-error": "Commande inconnue {}", # message
"not-json-server-error": "Pas une chaîne encodée json {}", # message
"line-decode-server-error": "Pas une chaîne utf-8",
"not-known-server-error": "Vous devez être connu du serveur avant d'envoyer cette commande",
"client-drop-server-error": "Client drop: {} -- {}", # host, error
"password-required-server-error": "Mot de passe requis",
"wrong-password-server-error": "Mauvais mot de passe fourni",
"hello-server-error": "Pas assez d'arguments pour Hello", # DO NOT TRANSLATE
# Playlists
"playlist-selection-changed-notification": "{} a modifié la sélection de la liste de lecture", # Username
"playlist-contents-changed-notification": "{} a mis à jour la liste de lecture", # Username
"cannot-find-file-for-playlist-switch-error": "Impossible de trouver le fichier {} dans les répertoires multimédias pour le changement de liste de lecture!", # Filename
"cannot-add-duplicate-error": "Impossible d'ajouter la deuxième entrée pour '{}' à la liste de lecture car aucun doublon n'est autorisé.", # Filename
"cannot-add-unsafe-path-error": "Impossible de charger automatiquement {}, car il ne se trouve pas sur un domaine approuvé. Vous pouvez basculer manuellement vers l'URL en double-cliquant dessus dans la liste de lecture et ajouter des domaines de confiance via Fichier->Avancé->Définir les domaines de confiance. Si vous faites un clic droit sur une URL, vous pouvez ajouter son domaine en tant que domaine de confiance via le menu contextuel.", # Filename
"sharedplaylistenabled-label": "Activer les listes de lecture partagées",
"removefromplaylist-menu-label": "Supprimer de la liste de lecture",
"shuffleremainingplaylist-menu-label": "Mélanger la liste de lecture restante",
"shuffleentireplaylist-menu-label": "Mélanger toute la liste de lecture",
"undoplaylist-menu-label": "Annuler la dernière modification de la liste de lecture",
"addfilestoplaylist-menu-label": "Ajouter des fichiers au bas de la liste de lecture",
"addurlstoplaylist-menu-label": "Ajouter des URL au bas de la liste de lecture",
"editplaylist-menu-label": "Modifier la liste de lecture",
"open-containing-folder": "Ouvrir le dossier contenant ce fichier",
"addyourfiletoplaylist-menu-label": "Ajoutez votre fichier à la liste de lecture",
"addotherusersfiletoplaylist-menu-label": "Ajouter le fichier de {} à la liste de lecture", # [Username]
"addyourstreamstoplaylist-menu-label": "Ajoutez votre flux à la liste de lecture",
"addotherusersstreamstoplaylist-menu-label": "Ajouter {}' stream à la playlist", # [Username]
"openusersstream-menu-label": "Ouvrir le flux de {}", # [username]'s
"openusersfile-menu-label": "Ouvrir le fichier de {}", # [username]'s
"playlist-instruction-item-message": "Faites glisser le fichier ici pour l'ajouter à la liste de lecture partagée.",
"sharedplaylistenabled-tooltip": "Les opérateurs de salle peuvent ajouter des fichiers à une liste de lecture synchronisée pour permettre à tout le monde de regarder facilement la même chose. Configurez les répertoires multimédias sous «Divers».",
"playlist-empty-error": "La liste de lecture est actuellement vide.",
"playlist-invalid-index-error": "Index de liste de lecture non valide",
}

View File

@ -0,0 +1,18 @@
[Script Info]
; Script generated by Aegisub 3.2.2
; http://www.aegisub.org/
Title: Default Aegisub file
ScriptType: v4.00+
WrapStyle: 0
ScaledBorderAndShadow: yes
YCbCr Matrix: None
[Aegisub Project Garbage]
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Arial,20,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,2,2,2,10,10,10,1
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:00.00,0:00:05.00,Default,,0,0,0,,

View File

@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
from .font_utility import Font
from .ass_core import Ass, Meta, Style, Line, Word, Syllable, Char
from .convert import Convert, ColorModel
from .shape import Shape
from .utils import Utils, FrameUtility, ColorUtility
__version__ = "0.9.10-reloaded"

1427
syncplay/pyonfx/ass_core.py Normal file

File diff suppressed because it is too large Load Diff

765
syncplay/pyonfx/convert.py Normal file
View File

@ -0,0 +1,765 @@
# -*- coding: utf-8 -*-
# PyonFX: An easy way to create KFX (Karaoke Effects) and complex typesetting using the ASS format (Advanced Substation Alpha).
# Copyright (C) 2019 Antonio Strippoli (CoffeeStraw/YellowFlash)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PyonFX is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see http://www.gnu.org/licenses/.
from __future__ import annotations
import re
import math
import colorsys
from enum import Enum
from typing import List, NamedTuple, Tuple, Union, TYPE_CHECKING
from .font_utility import Font
if TYPE_CHECKING:
from .ass_core import Line, Word, Syllable, Char
from .shape import Shape
# A simple NamedTuple to represent pixels
Pixel = NamedTuple("Pixel", [("x", float), ("y", float), ("alpha", int)])
class ColorModel(Enum):
ASS = "&HBBGGRR&"
ASS_STYLE = "&HAABBGGRR"
RGB = "(r, g, b)"
RGB_STR = "#RRGGBB"
RGBA = "(r, g, b, a)"
RGBA_STR = "#RRGGBBAA"
HSV = "(h, s, v)"
class Convert:
"""
This class is a collection of static methods that will help
the user to convert everything needed to the ASS format.
"""
@staticmethod
def time(ass_ms: Union[int, str]) -> Union[str, int, ValueError]:
"""Converts between milliseconds and ASS timestamp.
You can probably ignore that function, you will not make use of it for KFX or typesetting generation.
Parameters:
ass_ms (int or str): If int, than milliseconds are expected, else ASS timestamp as str is expected.
Returns:
If milliseconds -> ASS timestamp, else if ASS timestamp -> milliseconds, else ValueError will be raised.
"""
# Milliseconds?
if type(ass_ms) is int and ass_ms >= 0:
return "{:d}:{:02d}:{:02d}.{:02d}".format(
math.floor(ass_ms / 3600000) % 10,
math.floor(ass_ms % 3600000 / 60000),
math.floor(ass_ms % 60000 / 1000),
math.floor(ass_ms % 1000 / 10),
)
# ASS timestamp?
elif type(ass_ms) is str and re.match(r"^\d:\d+:\d+\.\d+$", ass_ms):
return (
int(ass_ms[0]) * 3600000
+ int(ass_ms[2:4]) * 60000
+ int(ass_ms[5:7]) * 1000
+ int(ass_ms[8:10]) * 10
)
else:
raise ValueError("Milliseconds or ASS timestamp expected")
@staticmethod
def alpha_ass_to_dec(alpha_ass: str) -> int:
"""Converts from ASS alpha string to corresponding decimal value.
Parameters:
alpha_ass (str): A string in the format '&HXX&'.
Returns:
A decimal in [0, 255] representing ``alpha_ass`` converted.
Examples:
.. code-block:: python3
print(Convert.alpha_ass_to_dec("&HFF&"))
>>> 255
"""
try:
match = re.fullmatch(r"&H([0-9A-F]{2})&", alpha_ass)
return int(match.group(1), 16)
except TypeError as e:
raise TypeError(
f"Provided ASS alpha was expected of type 'str', but you provided a '{type(alpha_ass)}'."
) from e
except AttributeError as e:
raise ValueError(
f"Provided ASS alpha string '{alpha_ass}' is not in the expected format '&HXX&'."
) from e
@staticmethod
def alpha_dec_to_ass(alpha_dec: Union[int, float]) -> str:
"""Converts from decimal value to corresponding ASS alpha string.
Parameters:
alpha_dec (int or float): Decimal in [0, 255] representing an alpha value.
Returns:
A string in the format '&HXX&' representing ``alpha_dec`` converted.
Examples:
.. code-block:: python3
print(Convert.alpha_dec_to_ass(255))
print(Convert.alpha_dec_to_ass(255.0))
>>> "&HFF&"
>>> "&HFF&"
"""
try:
if not 0 <= alpha_dec <= 255:
raise ValueError(
f"Provided alpha decimal '{alpha_dec}' is out of the range [0, 255]."
)
except TypeError as e:
raise TypeError(
f"Provided alpha decimal was expected of type 'int' or 'float', but you provided a '{type(alpha_dec)}'."
) from e
return f"&H{round(alpha_dec):02X}&"
@staticmethod
def color(
c: Union[
str,
Union[
Tuple[
Union[int, float],
Union[int, float],
Union[int, float],
],
Tuple[
Union[int, float],
Union[int, float],
Union[int, float],
Union[int, float],
],
],
],
input_format: ColorModel,
output_format: ColorModel,
round_output: bool = True,
) -> Union[
str,
Tuple[int, int, int],
Tuple[int, int, int, int],
Tuple[float, float, float],
Tuple[float, float, float, float],
]:
"""Converts a provided color from a color model to another.
Parameters:
c (str or tuple of int or tuple of float): A color in the format ``input_format``.
input_format (ColorModel): The color format of ``c``.
output_format (ColorModel): The color format for the output.
round_output (bool): A boolean to determine whether the output should be rounded or not.
Returns:
A color in the format ``output_format``.
Examples:
.. code-block:: python3
print(Convert.color("&H0000FF&", ColorModel.ASS, ColorModel.RGB))
>>> (255, 0, 0)
"""
try:
# Text for exception if input is out of ranges
input_range_e = f"Provided input '{c}' has value(s) out of the range "
# Parse input, obtaining its corresponding (r,g,b,a) values
if input_format == ColorModel.ASS:
match = re.fullmatch(r"&H([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})&", c)
(b, g, r), a = map(lambda x: int(x, 16), match.groups()), 255
elif input_format == ColorModel.ASS_STYLE:
match = re.fullmatch(
r"&H([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})", c
)
a, b, g, r = map(lambda x: int(x, 16), match.groups())
elif input_format == ColorModel.RGB:
if not all(0 <= n <= 255 for n in c):
raise ValueError(input_range_e + "[0, 255].")
(r, g, b), a = c, 255
elif input_format == ColorModel.RGB_STR:
match = re.fullmatch(r"#([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})", c)
(r, g, b), a = map(lambda x: int(x, 16), match.groups()), 255
elif input_format == ColorModel.RGBA:
if not all(0 <= n <= 255 for n in c):
raise ValueError(input_range_e + "[0, 255].")
r, g, b, a = c
elif input_format == ColorModel.RGBA_STR:
match = re.fullmatch(
r"#([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})", c
)
r, g, b, a = map(lambda x: int(x, 16), match.groups())
elif input_format == ColorModel.HSV:
if not (0 <= c[0] < 360 and 0 <= c[1] <= 100 and 0 <= c[2] <= 100):
raise ValueError(
input_range_e + "( [0, 360), [0, 100], [0, 100] )."
)
h, s, v = c[0] / 360, c[1] / 100, c[2] / 100
(r, g, b), a = map(lambda x: 255 * x, colorsys.hsv_to_rgb(h, s, v)), 255
except (AttributeError, ValueError, TypeError) as e:
# AttributeError -> re.fullmatch failed
# ValueError -> too many values to unpack
# TypeError -> in case the provided tuple is not a list of numbers
raise ValueError(
f"Provided input '{c}' is not in the format '{input_format}'."
) from e
# Convert (r,g,b,a) to the desired output_format
try:
if output_format == ColorModel.ASS:
return f"&H{round(b):02X}{round(g):02X}{round(r):02X}&"
elif output_format == ColorModel.ASS_STYLE:
return f"&H{round(a):02X}{round(b):02X}{round(g):02X}{round(r):02X}"
elif output_format == ColorModel.RGB:
method = round if round_output else float
return tuple(map(method, (r, g, b)))
elif output_format == ColorModel.RGB_STR:
return f"#{round(r):02X}{round(g):02X}{round(b):02X}"
elif output_format == ColorModel.RGBA:
method = round if round_output else float
return tuple(map(method, (r, g, b, a)))
elif output_format == ColorModel.RGBA_STR:
return f"#{round(r):02X}{round(g):02X}{round(b):02X}{round(a):02X}"
elif output_format == ColorModel.HSV:
method = round if round_output else float
h, s, v = colorsys.rgb_to_hsv(r / 255, g / 255, b / 255)
return method(h * 360) % 360, method(s * 100), method(v * 100)
else:
raise ValueError(f"Unsupported output_format ('{output_format}').")
except NameError as e:
raise ValueError(f"Unsupported input_format ('{input_format}').") from e
@staticmethod
def color_ass_to_rgb(
color_ass: str, as_str: bool = False
) -> Union[str, Tuple[int, int, int]]:
"""Converts from ASS color string to corresponding RGB color.
Parameters:
color_ass (str): A string in the format '&HBBGGRR&'.
as_str (bool): A boolean to determine the output type format.
Returns:
The output represents ``color_ass`` converted. If ``as_str`` = False, the output is a tuple of integers in range *[0, 255]*.
Else, the output is a string in the format '#RRGGBB'.
Examples:
.. code-block:: python3
print(Convert.color_ass_to_rgb("&HABCDEF&"))
print(Convert.color_ass_to_rgb("&HABCDEF&", as_str=True))
>>> (239, 205, 171)
>>> "#EFCDAB"
"""
return Convert.color(
color_ass, ColorModel.ASS, ColorModel.RGB_STR if as_str else ColorModel.RGB
)
@staticmethod
def color_ass_to_hsv(
color_ass: str, round_output: bool = True
) -> Union[Tuple[int, int, int], Tuple[float, float, float]]:
"""Converts from ASS color string to corresponding HSV color.
Parameters:
color_ass (str): A string in the format '&HBBGGRR&'.
round_output (bool): A boolean to determine whether the output should be rounded or not.
Returns:
The output represents ``color_ass`` converted. If ``round_output`` = True, the output is a tuple of integers in range *( [0, 360), [0, 100], [0, 100] )*.
Else, the output is a tuple of floats in range *( [0, 360), [0, 100], [0, 100] )*.
Examples:
.. code-block:: python3
print(Convert.color_ass_to_hsv("&HABCDEF&"))
print(Convert.color_ass_to_hsv("&HABCDEF&", round_output=False))
>>> (30, 28, 94)
>>> (30.000000000000014, 28.451882845188294, 93.72549019607843)
"""
return Convert.color(color_ass, ColorModel.ASS, ColorModel.HSV, round_output)
@staticmethod
def color_rgb_to_ass(
color_rgb: Union[
str, Tuple[Union[int, float], Union[int, float], Union[int, float]]
]
) -> str:
"""Converts from RGB color to corresponding ASS color.
Parameters:
color_rgb (str or tuple of int or tuple of float): Either a string in the format '#RRGGBB' or a tuple of three integers (or floats) in the range *[0, 255]*.
Returns:
A string in the format '&HBBGGRR&' representing ``color_rgb`` converted.
Examples:
.. code-block:: python3
print(Convert.color_rgb_to_ass("#ABCDEF"))
>>> "&HEFCDAB&"
"""
return Convert.color(
color_rgb,
ColorModel.RGB_STR if type(color_rgb) is str else ColorModel.RGB,
ColorModel.ASS,
)
@staticmethod
def color_rgb_to_hsv(
color_rgb: Union[
str, Tuple[Union[int, float], Union[int, float], Union[int, float]]
],
round_output: bool = True,
) -> Union[Tuple[int, int, int], Tuple[float, float, float]]:
"""Converts from RGB color to corresponding HSV color.
Parameters:
color_rgb (str or tuple of int or tuple of float): Either a string in the format '#RRGGBB' or a tuple of three integers (or floats) in the range *[0, 255]*.
round_output (bool): A boolean to determine whether the output should be rounded or not.
Returns:
The output represents ``color_rgb`` converted. If ``round_output`` = True, the output is a tuple of integers in range *( [0, 360), [0, 100], [0, 100] )*.
Else, the output is a tuple of floats in range *( [0, 360), [0, 100], [0, 100] )*.
Examples:
.. code-block:: python3
print(Convert.color_rgb_to_hsv("#ABCDEF"))
print(Convert.color_rgb_to_hsv("#ABCDEF"), round_output=False)
>>> (210, 28, 94)
>>> (210.0, 28.451882845188294, 93.72549019607843)
"""
return Convert.color(
color_rgb,
ColorModel.RGB_STR if type(color_rgb) is str else ColorModel.RGB,
ColorModel.HSV,
round_output,
)
@staticmethod
def color_hsv_to_ass(
color_hsv: Tuple[Union[int, float], Union[int, float], Union[int, float]]
) -> str:
"""Converts from HSV color string to corresponding ASS color.
Parameters:
color_hsv (tuple of int/float): A tuple of three integers (or floats) in the range *( [0, 360), [0, 100], [0, 100] )*.
Returns:
A string in the format '&HBBGGRR&' representing ``color_hsv`` converted.
Examples:
.. code-block:: python3
print(Convert.color_hsv_to_ass((100, 100, 100)))
>>> "&H00FF55&"
"""
return Convert.color(color_hsv, ColorModel.HSV, ColorModel.ASS)
@staticmethod
def color_hsv_to_rgb(
color_hsv: Tuple[Union[int, float], Union[int, float], Union[int, float]],
as_str: bool = False,
round_output: bool = True,
) -> str:
"""Converts from HSV color string to corresponding RGB color.
Parameters:
color_hsv (tuple of int/float): A tuple of three integers (or floats) in the range *( [0, 360), [0, 100], [0, 100] )*.
as_str (bool): A boolean to determine the output type format.
round_output (bool): A boolean to determine whether the output should be rounded or not.
Returns:
The output represents ``color_hsv`` converted. If ``as_str`` = False, the output is a tuple
( also, if ``round_output`` = True, the output is a tuple of integers in range *( [0, 360), [0, 100], [0, 100] )*, else a tuple of float in range *( [0, 360), [0, 100], [0, 100] ) )*.
Else, the output is a string in the format '#RRGGBB'.
Examples:
.. code-block:: python3
print(Convert.color_hsv_to_rgb((100, 100, 100)))
print(Convert.color_hsv_to_rgb((100, 100, 100), as_str=True))
print(Convert.color_hsv_to_rgb((100, 100, 100), round_output=False))
>>> (85, 255, 0)
>>> "#55FF00"
>>> (84.99999999999999, 255.0, 0.0)
"""
return Convert.color(
color_hsv,
ColorModel.HSV,
ColorModel.RGB_STR if as_str else ColorModel.RGB,
round_output,
)
@staticmethod
def text_to_shape(
obj: Union[Line, Word, Syllable, Char], fscx: float = None, fscy: float = None
) -> Shape:
"""Converts text with given style information to an ASS shape.
**Tips:** *You can easily create impressive deforming effects.*
Parameters:
obj (Line, Word, Syllable or Char): An object of class Line, Word, Syllable or Char.
fscx (float, optional): The scale_x value for the shape.
fscy (float, optional): The scale_y value for the shape.
Returns:
A Shape object, representing the text with the style format values of the object.
Examples:
.. code-block:: python3
line = Line.copy(lines[1])
line.text = "{\\\\an7\\\\pos(%.3f,%.3f)\\\\p1}%s" % (line.left, line.top, Convert.text_to_shape(line))
io.write_line(line)
"""
# Obtaining information and editing values of style if requested
original_scale_x = obj.styleref.scale_x
original_scale_y = obj.styleref.scale_y
# Editing temporary the style to properly get the shape
if fscx is not None:
obj.styleref.scale_x = fscx
if fscy is not None:
obj.styleref.scale_y = fscy
# Obtaining font information from style and obtaining shape
font = Font(obj.styleref)
shape = font.text_to_shape(obj.text)
# Clearing resources to not let overflow errors take over
del font
# Restoring values of style and returning the shape converted
if fscx is not None:
obj.styleref.scale_x = original_scale_x
if fscy is not None:
obj.styleref.scale_y = original_scale_y
return shape
@staticmethod
def text_to_clip(
obj: Union[Line, Word, Syllable, Char],
an: int = 5,
fscx: float = None,
fscy: float = None,
) -> Shape:
"""Converts text with given style information to an ASS shape, applying some translation/scaling to it since
it is not possible to position a shape with \\pos() once it is in a clip.
This is an high level function since it does some additional operations, check text_to_shape for further infromations.
**Tips:** *You can easily create text masks even for growing/shrinking text without too much effort.*
Parameters:
obj (Line, Word, Syllable or Char): An object of class Line, Word, Syllable or Char.
an (integer, optional): The alignment wanted for the shape.
fscx (float, optional): The scale_x value for the shape.
fscy (float, optional): The scale_y value for the shape.
Returns:
A Shape object, representing the text with the style format values of the object.
Examples:
.. code-block:: python3
line = Line.copy(lines[1])
line.text = "{\\\\an5\\\\pos(%.3f,%.3f)\\\\clip(%s)}%s" % (line.center, line.middle, Convert.text_to_clip(line), line.text)
io.write_line(line)
"""
# Checking for errors
if an < 1 or an > 9:
raise ValueError("Alignment value must be an integer between 1 and 9")
# Setting default values
if fscx is None:
fscx = obj.styleref.scale_x
if fscy is None:
fscy = obj.styleref.scale_y
# Obtaining text converted to shape
shape = Convert.text_to_shape(obj, fscx, fscy)
# Setting mult_x based on alignment
if an % 3 == 1: # an=1 or an=4 or an=7
mult_x = 0
elif an % 3 == 2: # an=2 or an=5 or an=8
mult_x = 1 / 2
else:
mult_x = 1
# Setting mult_y based on alignment
if an < 4:
mult_y = 1
elif an < 7:
mult_y = 1 / 2
else:
mult_y = 0
# Calculating offsets
cx = (
obj.left
- obj.width * mult_x * (fscx - obj.styleref.scale_x) / obj.styleref.scale_x
)
cy = (
obj.top
- obj.height * mult_y * (fscy - obj.styleref.scale_y) / obj.styleref.scale_y
)
return shape.move(cx, cy)
@staticmethod
def text_to_pixels(
obj: Union[Line, Word, Syllable, Char], supersampling: int = 8
) -> List[Pixel]:
"""| Converts text with given style information to a list of pixel data.
| A pixel data is a dictionary containing 'x' (horizontal position), 'y' (vertical position) and 'alpha' (alpha/transparency).
It is highly suggested to create a dedicated style for pixels,
because you will write less tags for line in your pixels, which means less size for your .ass file.
| The style suggested is:
| - **an=7 (very important!);**
| - bord=0;
| - shad=0;
| - For Font informations leave whatever the default is;
**Tips:** *It allows easy creation of text decaying or light effects.*
Parameters:
obj (Line, Word, Syllable or Char): An object of class Line, Word, Syllable or Char.
supersampling (int): Value used for supersampling. Higher value means smoother and more precise anti-aliasing (and more computational time for generation).
Returns:
A list of dictionaries representing each individual pixel of the input text styled.
Examples:
.. code-block:: python3
line = lines[2].copy()
line.style = "p"
p_sh = Shape.rectangle()
for pixel in Convert.text_to_pixels(line):
x, y = math.floor(line.left) + pixel['x'], math.floor(line.top) + pixel['y']
alpha = "\\alpha" + Convert.color_alpha_to_ass(pixel['alpha']) if pixel['alpha'] != 255 else ""
line.text = "{\\p1\\pos(%d,%d)%s}%s" % (x, y, alpha, p_sh)
io.write_line(line)
"""
shape = Convert.text_to_shape(obj).move(obj.left % 1, obj.top % 1)
return Convert.shape_to_pixels(shape, supersampling)
@staticmethod
def shape_to_pixels(shape: Shape, supersampling: int = 8) -> List[Pixel]:
"""| Converts a Shape object to a list of pixel data.
| A pixel data is a dictionary containing 'x' (horizontal position), 'y' (vertical position) and 'alpha' (alpha/transparency).
It is highly suggested to create a dedicated style for pixels,
because you will write less tags for line in your pixels, which means less size for your .ass file.
| The style suggested is:
| - **an=7 (very important!);**
| - bord=0;
| - shad=0;
| - For Font informations leave whatever the default is;
**Tips:** *As for text, even shapes can decay!*
Parameters:
shape (Shape): An object of class Shape.
supersampling (int): Value used for supersampling. Higher value means smoother and more precise anti-aliasing (and more computational time for generation).
Returns:
A list of dictionaries representing each individual pixel of the input shape.
Examples:
.. code-block:: python3
line = lines[2].copy()
line.style = "p"
p_sh = Shape.rectangle()
for pixel in Convert.shape_to_pixels(Shape.heart(100)):
# Random circle to pixel effect just to show
x, y = math.floor(line.left) + pixel.x, math.floor(line.top) + pixel.y
alpha = "\\alpha" + Convert.color_alpha_to_ass(pixel.alpha) if pixel.alpha != 255 else ""
line.text = "{\\p1\\pos(%d,%d)%s\\fad(0,%d)}%s" % (x, y, alpha, l.dur/4, p_sh)
io.write_line(line)
"""
# Scale values for supersampled rendering
upscale = supersampling
downscale = 1 / upscale
# Upscale shape for later downsampling
shape.map(lambda x, y: (x * upscale, y * upscale))
# Bring shape near origin in positive room
x1, y1, x2, y2 = shape.bounding()
shift_x, shift_y = -1 * (x1 - x1 % upscale), -1 * (y1 - y1 % upscale)
shape.move(shift_x, shift_y)
# Create image
width, height = (
math.ceil((x2 + shift_x) * downscale) * upscale,
math.ceil((y2 + shift_y) * downscale) * upscale,
)
image = [False for i in range(width * height)]
# Renderer (on binary image with aliasing)
lines, last_point, last_move = [], {}, {}
def collect_lines(x, y, typ):
# Collect lines (points + vectors)
nonlocal lines, last_point, last_move
x, y = int(round(x)), int(round(y)) # Use integers to avoid rounding errors
# Move
if typ == "m":
# Close figure with non-horizontal line in image
if (
last_move
and last_move["y"] != last_point["y"]
and not (last_point["y"] < 0 and last_move["y"] < 0)
and not (last_point["y"] > height and last_move["y"] > height)
):
lines.append(
[
last_point["x"],
last_point["y"],
last_move["x"] - last_point["x"],
last_move["y"] - last_point["y"],
]
)
last_move = {"x": x, "y": y}
# Non-horizontal line in image
elif (
last_point
and last_point["y"] != y
and not (last_point["y"] < 0 and y < 0)
and not (last_point["y"] > height and y > height)
):
lines.append(
[
last_point["x"],
last_point["y"],
x - last_point["x"],
y - last_point["y"],
]
)
# Remember last point
last_point = {"x": x, "y": y}
shape.flatten().map(collect_lines)
# Close last figure with non-horizontal line in image
if (
last_move
and last_move["y"] != last_point["y"]
and not (last_point["y"] < 0 and last_move["y"] < 0)
and not (last_point["y"] > height and last_move["y"] > height)
):
lines.append(
[
last_point["x"],
last_point["y"],
last_move["x"] - last_point["x"],
last_move["y"] - last_point["y"],
]
)
# Calculates line x horizontal line intersection
def line_x_hline(x, y, vx, vy, y2):
if vy != 0:
s = (y2 - y) / vy
if s >= 0 and s <= 1:
return x + s * vx
return None
# Scan image rows in shape
_, y1, _, y2 = shape.bounding()
for y in range(max(math.floor(y1), 0), min(math.ceil(y2), height)):
# Collect row intersections with lines
row_stops = []
for line in lines:
cx = line_x_hline(line[0], line[1], line[2], line[3], y + 0.5)
if cx is not None:
row_stops.append(
[max(0, min(cx, width)), 1 if line[3] > 0 else -1]
) # image trimmed stop position & line vertical direction
# Enough intersections / something to render?
if len(row_stops) > 1:
# Sort row stops by horizontal position
row_stops.sort(key=lambda x: x[0])
# Render!
status, row_index = 0, y * width
for i in range(0, len(row_stops) - 1):
status = status + row_stops[i][1]
if status != 0:
for x in range(
math.ceil(row_stops[i][0] - 0.5),
math.floor(row_stops[i + 1][0] + 0.5),
):
image[row_index + x] = True
# Extract pixels from image
pixels = []
for y in range(0, height, upscale):
for x in range(0, width, upscale):
opacity = 0
for yy in range(0, upscale):
for xx in range(0, upscale):
if image[(y + yy) * width + (x + xx)]:
opacity = opacity + 255
if opacity > 0:
pixels.append(
Pixel(
x=(x - shift_x) * downscale,
y=(y - shift_y) * downscale,
alpha=round(opacity * downscale ** 2),
)
)
return pixels
@staticmethod
def image_to_ass(image):
pass
@staticmethod
def image_to_pixels(image):
pass

View File

@ -0,0 +1,353 @@
# -*- coding: utf-8 -*-
# PyonFX: An easy way to create KFX (Karaoke Effects) and complex typesetting using the ASS format (Advanced Substation Alpha).
# Copyright (C) 2019 Antonio Strippoli (CoffeeStraw/YellowFlash)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PyonFX is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see http://www.gnu.org/licenses/.
"""
This module contains the Font class definition, which has some functions
to help getting informations from a specific font
"""
from __future__ import annotations
import sys
from typing import Tuple, TYPE_CHECKING
from .shape import Shape
if sys.platform == "win32":
import win32gui # pylint: disable=import-error
import win32ui # pylint: disable=import-error
import win32con # pylint: disable=import-error
elif sys.platform in ["linux", "darwin"] and not "sphinx" in sys.modules:
import cairo # pylint: disable=import-error
import gi # pylint: disable=import-error
gi.require_version("Pango", "1.0")
gi.require_version("PangoCairo", "1.0")
from gi.repository import Pango, PangoCairo # pylint: disable=import-error
import html
if TYPE_CHECKING:
from .ass_core import Style
# CONFIGURATION
FONT_PRECISION = 64 # Font scale for better precision output from native font system
LIBASS_FONTHACK = True # Scale font data to fontsize? (no effect on windows)
PANGO_SCALE = 1024 # The PANGO_SCALE macro represents the scale between dimensions used for Pango distances and device units.
class Font:
"""
Font class definition
"""
def __init__(self, style: Style):
self.family = style.fontname
self.bold = style.bold
self.italic = style.italic
self.underline = style.underline
self.strikeout = style.strikeout
self.size = style.fontsize
self.xscale = style.scale_x / 100
self.yscale = style.scale_y / 100
self.hspace = style.spacing
self.upscale = FONT_PRECISION
self.downscale = 1 / FONT_PRECISION
if sys.platform == "win32":
# Create device context
self.dc = win32gui.CreateCompatibleDC(None)
# Set context coordinates mapping mode
win32gui.SetMapMode(self.dc, win32con.MM_TEXT)
# Set context backgrounds to transparent
win32gui.SetBkMode(self.dc, win32con.TRANSPARENT)
# Create font handle
font_spec = {
"height": int(self.size * self.upscale),
"width": 0,
"escapement": 0,
"orientation": 0,
"weight": win32con.FW_BOLD if self.bold else win32con.FW_NORMAL,
"italic": int(self.italic),
"underline": int(self.underline),
"strike out": int(self.strikeout),
"charset": win32con.DEFAULT_CHARSET,
"out precision": win32con.OUT_TT_PRECIS,
"clip precision": win32con.CLIP_DEFAULT_PRECIS,
"quality": win32con.ANTIALIASED_QUALITY,
"pitch and family": win32con.DEFAULT_PITCH + win32con.FF_DONTCARE,
"name": self.family,
}
self.pycfont = win32ui.CreateFont(font_spec)
win32gui.SelectObject(self.dc, self.pycfont.GetSafeHandle())
# Calculate metrics
self.metrics = win32gui.GetTextMetrics(self.dc)
elif sys.platform == "linux" or sys.platform == "darwin":
surface = cairo.ImageSurface(cairo.Format.A8, 1, 1)
self.context = cairo.Context(surface)
self.layout = PangoCairo.create_layout(self.context)
font_description = Pango.FontDescription()
font_description.set_family(self.family)
font_description.set_absolute_size(self.size * self.upscale * PANGO_SCALE)
font_description.set_weight(
Pango.Weight.BOLD if self.bold else Pango.Weight.NORMAL
)
font_description.set_style(
Pango.Style.ITALIC if self.italic else Pango.Style.NORMAL
)
self.layout.set_font_description(font_description)
self.metrics = Pango.Context.get_metrics(
self.layout.get_context(), self.layout.get_font_description()
)
if LIBASS_FONTHACK:
self.fonthack_scale = self.size / (
(self.metrics.get_ascent() + self.metrics.get_descent())
/ PANGO_SCALE
* self.downscale
)
else:
self.fonthack_scale = 1
else:
raise NotImplementedError
def __del__(self):
if sys.platform == "win32":
win32gui.DeleteObject(self.pycfont.GetSafeHandle())
win32gui.DeleteDC(self.dc)
def get_metrics(self) -> Tuple[float, float, float, float]:
if sys.platform == "win32":
const = self.downscale * self.yscale
return (
# 'height': self.metrics['Height'] * const,
self.metrics["Ascent"] * const,
self.metrics["Descent"] * const,
self.metrics["InternalLeading"] * const,
self.metrics["ExternalLeading"] * const,
)
elif sys.platform == "linux" or sys.platform == "darwin":
const = self.downscale * self.yscale * self.fonthack_scale / PANGO_SCALE
return (
# 'height': (self.metrics.get_ascent() + self.metrics.get_descent()) * const,
self.metrics.get_ascent() * const,
self.metrics.get_descent() * const,
0.0,
self.layout.get_spacing() * const,
)
else:
raise NotImplementedError
def get_text_extents(self, text: str) -> Tuple[float, float]:
if sys.platform == "win32":
cx, cy = win32gui.GetTextExtentPoint32(self.dc, text)
return (
(cx * self.downscale + self.hspace * (len(text) - 1)) * self.xscale,
cy * self.downscale * self.yscale,
)
elif sys.platform == "linux" or sys.platform == "darwin":
if not text:
return 0.0, 0.0
def get_rect(new_text):
self.layout.set_markup(
f"<span "
f'strikethrough="{str(self.strikeout).lower()}" '
f'underline="{"single" if self.underline else "none"}"'
f">"
f"{html.escape(new_text)}"
f"</span>",
-1,
)
return self.layout.get_pixel_extents()[1]
width = 0
for char in text:
width += get_rect(char).width
return (
(
width * self.downscale * self.fonthack_scale
+ self.hspace * (len(text) - 1)
)
* self.xscale,
get_rect(text).height
* self.downscale
* self.yscale
* self.fonthack_scale,
)
else:
raise NotImplementedError
def text_to_shape(self, text: str) -> Shape:
if sys.platform == "win32":
# TODO: Calcultating distance between origins of character cells (just in case of spacing)
# Add path to device context
win32gui.BeginPath(self.dc)
win32gui.ExtTextOut(self.dc, 0, 0, 0x0, None, text)
win32gui.EndPath(self.dc)
# Getting Path produced by Microsoft API
points, type_points = win32gui.GetPath(self.dc)
# Checking for errors
if len(points) == 0 or len(points) != len(type_points):
raise RuntimeError(
"This should never happen: function win32gui.GetPath has returned something unexpected.\nPlease report this to the developer"
)
# Defining variables
shape, last_type = [], None
mult_x, mult_y = self.downscale * self.xscale, self.downscale * self.yscale
# Convert points to shape
i = 0
while i < len(points):
cur_point, cur_type = points[i], type_points[i]
if cur_type == win32con.PT_MOVETO:
if last_type != win32con.PT_MOVETO:
# Avoid repetition of command tags
shape.append("m")
last_type = cur_type
shape.extend(
[
Shape.format_value(cur_point[0] * mult_x),
Shape.format_value(cur_point[1] * mult_y),
]
)
i += 1
elif cur_type == win32con.PT_LINETO or cur_type == (
win32con.PT_LINETO | win32con.PT_CLOSEFIGURE
):
if last_type != win32con.PT_LINETO:
# Avoid repetition of command tags
shape.append("l")
last_type = cur_type
shape.extend(
[
Shape.format_value(cur_point[0] * mult_x),
Shape.format_value(cur_point[1] * mult_y),
]
)
i += 1
elif cur_type == win32con.PT_BEZIERTO or cur_type == (
win32con.PT_BEZIERTO | win32con.PT_CLOSEFIGURE
):
if last_type != win32con.PT_BEZIERTO:
# Avoid repetition of command tags
shape.append("b")
last_type = cur_type
shape.extend(
[
Shape.format_value(cur_point[0] * mult_x),
Shape.format_value(cur_point[1] * mult_y),
Shape.format_value(points[i + 1][0] * mult_x),
Shape.format_value(points[i + 1][1] * mult_y),
Shape.format_value(points[i + 2][0] * mult_x),
Shape.format_value(points[i + 2][1] * mult_y),
]
)
i += 3
else: # If there is an invalid type -> skip, for safeness
i += 1
# Clear device context path
win32gui.AbortPath(self.dc)
return Shape(" ".join(shape))
elif sys.platform == "linux" or sys.platform == "darwin":
# Defining variables
shape, last_type = [], None
def shape_from_text(new_text, x_add):
nonlocal shape, last_type
self.layout.set_markup(
f"<span "
f'strikethrough="{str(self.strikeout).lower()}" '
f'underline="{"single" if self.underline else "none"}"'
f">"
f"{html.escape(new_text)}"
f"</span>",
-1,
)
self.context.save()
self.context.scale(
self.downscale * self.xscale * self.fonthack_scale,
self.downscale * self.yscale * self.fonthack_scale,
)
PangoCairo.layout_path(self.context, self.layout)
self.context.restore()
path = self.context.copy_path()
# Convert points to shape
for current_entry in path:
current_type = current_entry[0]
current_path = current_entry[1]
if current_type == 0: # MOVE_TO
if last_type != current_type:
# Avoid repetition of command tags
shape.append("m")
last_type = current_type
shape.extend(
[
Shape.format_value(current_path[0] + x_add),
Shape.format_value(current_path[1]),
]
)
elif current_type == 1: # LINE_TO
if last_type != current_type:
# Avoid repetition of command tags
shape.append("l")
last_type = current_type
shape.extend(
[
Shape.format_value(current_path[0] + x_add),
Shape.format_value(current_path[1]),
]
)
elif current_type == 2: # CURVE_TO
if last_type != current_type:
# Avoid repetition of command tags
shape.append("b")
last_type = current_type
shape.extend(
[
Shape.format_value(current_path[0] + x_add),
Shape.format_value(current_path[1]),
Shape.format_value(current_path[2] + x_add),
Shape.format_value(current_path[3]),
Shape.format_value(current_path[4] + x_add),
Shape.format_value(current_path[5]),
]
)
self.context.new_path()
curr_width = 0
for i, char in enumerate(text):
shape_from_text(char, curr_width + self.hspace * self.xscale * i)
curr_width += self.get_text_extents(char)[0]
return Shape(" ".join(shape))
else:
raise NotImplementedError

994
syncplay/pyonfx/shape.py Normal file
View File

@ -0,0 +1,994 @@
# -*- coding: utf-8 -*-
# PyonFX: An easy way to create KFX (Karaoke Effects) and complex typesetting using the ASS format (Advanced Substation Alpha).
# Copyright (C) 2019 Antonio Strippoli (CoffeeStraw/YellowFlash)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PyonFX is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see http://www.gnu.org/licenses/.
from __future__ import annotations
import math
from typing import Callable, Optional, List, Tuple, Union
from pyquaternion import Quaternion
from inspect import signature
class Shape:
"""
This class can be used to define a Shape object (by passing its drawing commands)
and then apply functions to it in order to accomplish some tasks, like analyzing its bounding box, apply transformations, splitting curves into segments...
Args:
drawing_cmds (str): The shape's drawing commands in ASS format as a string.
"""
def __init__(self, drawing_cmds: str):
# Assure that drawing_cmds is a string
if not isinstance(drawing_cmds, str):
raise TypeError(
f"A string containing the shape's drawing commands is expected, but you passed a {type(drawing_cmds)}"
)
self.drawing_cmds = drawing_cmds
def __repr__(self):
# We return drawing commands as a string rapresentation of the object
return self.drawing_cmds
def __eq__(self, other: Shape):
# Method used to compare two shapes
return type(other) is type(self) and self.drawing_cmds == other.drawing_cmds
@staticmethod
def format_value(x: float, prec: int = 3) -> str:
# Utility function to properly format values for shapes also returning them as a string
return f"{x:.{prec}f}".rstrip("0").rstrip(".")
def has_error(self) -> Union[bool, str]:
"""Utility function that checks if the shape is valid.
Returns:
False if no error has been found, else a string with the first error encountered.
"""
# Obtain commands and points
cad = self.drawing_cmds.split()
n = len(cad)
mode = ""
# Prepare usefull lists
two_args_cmds = ["m", "n", "l", "p"]
six_args_cmds = ["b", "s"]
# Iterate over commands and points
i = 0
while i < n:
if cad[i] in two_args_cmds:
# Check if we have an unexpected and of the shape
if n - i < 3:
return (
f"Unexpected end of shape ('{cad[i]}' expect at least two args)"
)
# Check if we have two numeric values after command
try:
float(cad[i + 1])
float(cad[i + 2])
except ValueError:
return (
f"Expected numeric value at: '{cad[i]} {cad[i+1]} {cad[i+2]}'"
)
# Valid, go on
mode = cad[i]
i += 3
elif cad[i] in six_args_cmds:
# Check if we have an unexpected and of the shape
if n - i < 7:
return (
f"Unexpected end of shape ('{cad[i]}' expect at least six args)"
)
# Check if we have six numeric values after command
try:
for x in range(1, 7):
float(cad[i + x])
except ValueError:
return f"Expected numeric value at: '{cad[i]} {' '.join(cad[i+1:i+7])}'"
# Valid, go on
mode = cad[i]
i += 7
elif cad[i] == "c":
# 'c' expect no arguments, skip
mode = ""
i += 1
elif mode in two_args_cmds:
# Check if we have an unexpected and of the shape
if n - i < 2:
return (
f"Unexpected end of shape ('{cad[i]}' expect at least two args)"
)
# Check if we have two numeric values
try:
float(cad[i])
float(cad[i + 1])
except ValueError:
return (
f"Expected numeric value at: '{mode} ... {cad[i]} {cad[i+1]}'"
)
# Valid, go on
i += 2
elif mode in six_args_cmds:
# Check if we have an unexpected and of the shape
if n - i < 6:
return (
f"Unexpected end of shape ('{cad[i]}' expect at least six args)"
)
# Check if we have six numeric values
try:
for x in range(6):
float(cad[i + x])
except ValueError:
return f"Expected numeric value at: '{mode} ... {' '.join(cad[i:i+6])}'"
# Valid, go on
i += 6
else:
# Wtf is this?
return f"Unexpected command '{cad[i]}'"
return False
def map(
self, fun: Callable[[float, float, Optional[str]], Tuple[float, float]]
) -> Shape:
"""Sends every point of a shape through given transformation function to change them.
**Tips:** *Working with outline points can be used to deform the whole shape and make f.e. a wobble effect.*
Parameters:
fun (function): A function with two (or optionally three) parameters. It will define how each coordinate will be changed. The first two parameters represent the x and y coordinates of each point. The third optional it represents the type of each point (move, line, bezier...).
Returns:
A pointer to the current object.
Examples:
.. code-block:: python3
original = Shape("m 0 0 l 20 0 20 10 0 10")
print ( original.map(lambda x, y: (x+10, y+5) ) )
>>> m 10 5 l 30 5 30 15 10 15
"""
if not callable(fun):
raise TypeError("(Lambda) function expected")
# Getting all points and commands in a list
cmds_and_points = self.drawing_cmds.split()
i = 0
n = len(cmds_and_points)
# Checking whether the function take the typ parameter or not
if len(signature(fun).parameters) == 2:
while i < n:
try:
# Applying transformation
x, y = fun(float(cmds_and_points[i]), float(cmds_and_points[i + 1]))
except TypeError:
# Values weren't returned, so we don't need to modify them
i += 2
continue
except ValueError:
# We have found a string, let's skip this
i += 1
continue
except IndexError:
raise ValueError("Unexpected end of the shape")
# Convert back to string the results for later
cmds_and_points[i : i + 2] = (
Shape.format_value(x),
Shape.format_value(y),
)
i += 2
else:
typ = ""
while i < n:
try:
# Applying transformation
x, y = fun(
float(cmds_and_points[i]), float(cmds_and_points[i + 1]), typ
)
except TypeError:
# Values weren't returned, so we don't need to modify them
i += 2
continue
except ValueError:
# We have found a string, let's skip this
typ = cmds_and_points[i]
i += 1
continue
except IndexError:
raise ValueError("Unexpected end of the shape")
# Convert back to string the results for later
cmds_and_points[i : i + 2] = (
Shape.format_value(x),
Shape.format_value(y),
)
i += 2
# Sew up everything back and update shape
self.drawing_cmds = " ".join(cmds_and_points)
return self
def bounding(self) -> Tuple[float, float, float, float]:
"""Calculates shape bounding box.
**Tips:** *Using this you can get more precise information about a shape (width, height, position).*
Returns:
A tuple (x0, y0, x1, y1) containing coordinates of the bounding box.
Examples:
.. code-block:: python3
print("Left-top: %d %d\\nRight-bottom: %d %d" % ( Shape("m 10 5 l 25 5 25 42 10 42").bounding() ) )
>>> Left-top: 10 5
>>> Right-bottom: 25 42
"""
# Bounding data
x0: float = None
y0: float = None
x1: float = None
y1: float = None
# Calculate minimal and maximal coordinates
def compute_edges(x, y):
nonlocal x0, y0, x1, y1
if x0 is not None:
x0, y0, x1, y1 = min(x0, x), min(y0, y), max(x1, x), max(y1, y)
else:
x0, y0, x1, y1 = x, y, x, y
return x, y
self.map(compute_edges)
return x0, y0, x1, y1
def move(self, x: float = None, y: float = None) -> Shape:
"""Moves shape coordinates in given direction.
| If neither x and y are passed, it will automatically center the shape to the origin (0,0).
| This function is an high level function, it just uses Shape.map, which is more advanced. Additionally, it is an easy way to center a shape.
Parameters:
x (int or float): Displacement along the x-axis.
y (int or float): Displacement along the y-axis.
Returns:
A pointer to the current object.
Examples:
.. code-block:: python3
print( Shape("m 0 0 l 30 0 30 20 0 20").move(-5, 10) )
>>> m -5 10 l 25 10 25 30 -5 30
"""
if x is None and y is None:
x, y = [-1 * el for el in self.bounding()[0:2]]
elif x is None:
x = 0
elif y is None:
y = 0
# Update shape
self.map(lambda cx, cy: (cx + x, cy + y))
return self
def flatten(self, tolerance: float = 1.0) -> Shape:
"""Splits shape's bezier curves into lines.
| This is a low level function. Instead, you should use :func:`split` which already calls this function.
Parameters:
tolerance (float): Angle in degree to define a curve as flat (increasing it will boost performance during reproduction, but lower accuracy)
Returns:
A pointer to the current object.
Returns:
The shape as a string, with bezier curves converted to lines.
"""
# TO DO: Make this function iterative, recursion is bad.
if tolerance < 0:
raise ValueError("Tolerance must be a positive number")
# Inner functions definitions
# 4th degree curve subdivider (De Casteljau)
def curve4_subdivide(
x0,
y0,
x1,
y1,
x2,
y2,
x3,
y3,
pct,
):
# Calculate points on curve vectors
x01, y01, x12, y12, x23, y23 = (
(x0 + x1) * pct,
(y0 + y1) * pct,
(x1 + x2) * pct,
(y1 + y2) * pct,
(x2 + x3) * pct,
(y2 + y3) * pct,
)
x012, y012, x123, y123 = (
(x01 + x12) * pct,
(y01 + y12) * pct,
(x12 + x23) * pct,
(y12 + y23) * pct,
)
x0123, y0123 = (x012 + x123) * pct, (y012 + y123) * pct
# Return new 2 curves
return (
x0,
y0,
x01,
y01,
x012,
y012,
x0123,
y0123,
x0123,
y0123,
x123,
y123,
x23,
y23,
x3,
y3,
)
# Check flatness of 4th degree curve with angles
def curve4_is_flat(
x0,
y0,
x1,
y1,
x2,
y2,
x3,
y3,
):
# Pack curve vectors (only ones non zero)
vecs = [[x1 - x0, y1 - y0], [x2 - x1, y2 - y1], [x3 - x2, y3 - y2]]
vecs = [el for el in vecs if not (el[0] == 0 and el[1] == 0)]
# Inner functions to calculate degrees between two 2d vectors
def dotproduct(v1, v2):
return sum((a * b) for a, b in zip(v1, v2))
def length(v):
return math.sqrt(dotproduct(v, v))
def get_angle(v1, v2):
calc = max(
min(dotproduct(v1, v2) / (length(v1) * length(v2)), 1), -1
) # Clamping value to prevent errors
angle = math.degrees(math.acos(calc))
if (v1[0] * v2[1] - v1[1] * v2[0]) < 0:
return -angle
return angle
# Check flatness on vectors
for i in range(1, len(vecs)):
if abs(get_angle(vecs[i - 1], vecs[i])) > tolerance:
return False
return True
# Inner function to convert 4th degree curve to line points
def curve4_to_lines(
x0,
y0,
x1,
y1,
x2,
y2,
x3,
y3,
):
# Line points buffer
pts = ""
# Conversion in recursive processing
def convert_recursive(x0, y0, x1, y1, x2, y2, x3, y3):
if curve4_is_flat(x0, y0, x1, y1, x2, y2, x3, y3):
nonlocal pts
x3, y3 = Shape.format_value(x3), Shape.format_value(y3)
pts += f"{x3} {y3} "
return
(
x10,
y10,
x11,
y11,
x12,
y12,
x13,
y13,
x20,
y20,
x21,
y21,
x22,
y22,
x23,
y23,
) = curve4_subdivide(x0, y0, x1, y1, x2, y2, x3, y3, 0.5)
convert_recursive(x10, y10, x11, y11, x12, y12, x13, y13)
convert_recursive(x20, y20, x21, y21, x22, y22, x23, y23)
# Splitting curve recursively until we're not satisfied (angle <= tolerance)
convert_recursive(x0, y0, x1, y1, x2, y2, x3, y3)
# Return resulting points
return " ".join(
pts[:-1].split(" ")[:-2]
) # Delete last space and last two float values
# Getting all points and commands in a list
cmds_and_points = self.drawing_cmds.split()
i = 0
n = len(cmds_and_points)
# Scanning all commands and points (improvable)
while i < n:
if (
cmds_and_points[i] == "b"
): # We've found a curve, let's split it into lines
try:
# Getting all the points: if we don't have exactly 8 points, shape is not valid
x0, y0 = (
float(cmds_and_points[i - 2]),
float(cmds_and_points[i - 1]),
)
x1, y1 = (
float(cmds_and_points[i + 1]),
float(cmds_and_points[i + 2]),
)
x2, y2 = (
float(cmds_and_points[i + 3]),
float(cmds_and_points[i + 4]),
)
x3, y3 = (
float(cmds_and_points[i + 5]),
float(cmds_and_points[i + 6]),
)
except IndexError:
raise ValueError(
"Shape providen is not valid (not enough points for a curve)"
)
# Obtaining the converted curve and saving it for later
cmds_and_points[i] = "l"
cmds_and_points[i + 1] = curve4_to_lines(x0, y0, x1, y1, x2, y2, x3, y3)
i += 2
n -= 3
# Deleting the remaining points
for _ in range(3):
del cmds_and_points[i]
# Going to the next point
i += 2
# Check if we're at the end of the shape
if i < n:
# Check for implicit bezier curve
try:
float(cmds_and_points[i]) # Next number is a float?
cmds_and_points.insert(i, "b")
n += 1
except ValueError:
pass
elif cmds_and_points[i] == "c": # Deleting c tag?
del cmds_and_points[i]
n -= 1
else:
i += 1
# Update shape
self.drawing_cmds = " ".join(cmds_and_points)
return self
def split(self, max_len: float = 16, tolerance: float = 1.0) -> Shape:
"""Splits shape bezier curves into lines and splits lines into shorter segments with maximum given length.
**Tips:** *You can call this before using :func:`map` to work with more outline points for smoother deforming.*
Parameters:
max_len (int or float): The max length that you want all the lines to be
tolerance (float): Angle in degree to define a bezier curve as flat (increasing it will boost performance during reproduction, but lower accuracy)
Returns:
A pointer to the current object.
Examples:
.. code-block:: python3
print( Shape("m -100.5 0 l 100 0 b 100 100 -100 100 -100.5 0 c").split() )
>>> m -100.5 0 l -100 0 -90 0 -80 0 -70 0 -60 0 -50 0 -40 0 -30 0 -20 0 -10 0 0 0 10 0 20 0 30 0 40 0 50 0 60 0 70 0 80 0 90 0 100 0 l 99.964 2.325 99.855 4.614 99.676 6.866 99.426 9.082 99.108 11.261 98.723 13.403 98.271 15.509 97.754 17.578 97.173 19.611 96.528 21.606 95.822 23.566 95.056 25.488 94.23 27.374 93.345 29.224 92.403 31.036 91.405 32.812 90.352 34.552 89.246 36.255 88.086 37.921 86.876 39.551 85.614 41.144 84.304 42.7 82.945 44.22 81.54 45.703 80.088 47.15 78.592 48.56 77.053 49.933 75.471 51.27 73.848 52.57 72.184 53.833 70.482 55.06 68.742 56.25 66.965 57.404 65.153 58.521 63.307 59.601 61.427 60.645 59.515 61.652 57.572 62.622 55.599 63.556 53.598 64.453 51.569 65.314 49.514 66.138 47.433 66.925 45.329 67.676 43.201 68.39 41.052 69.067 38.882 69.708 36.692 70.312 34.484 70.88 32.259 71.411 27.762 72.363 23.209 73.169 18.61 73.828 13.975 74.341 9.311 74.707 4.629 74.927 -0.062 75 -4.755 74.927 -9.438 74.707 -14.103 74.341 -18.741 73.828 -23.343 73.169 -27.9 72.363 -32.402 71.411 -34.63 70.88 -36.841 70.312 -39.033 69.708 -41.207 69.067 -43.359 68.39 -45.49 67.676 -47.599 66.925 -49.683 66.138 -51.743 65.314 -53.776 64.453 -55.782 63.556 -57.759 62.622 -59.707 61.652 -61.624 60.645 -63.509 59.601 -65.361 58.521 -67.178 57.404 -68.961 56.25 -70.707 55.06 -72.415 53.833 -74.085 52.57 -75.714 51.27 -77.303 49.933 -78.85 48.56 -80.353 47.15 -81.811 45.703 -83.224 44.22 -84.59 42.7 -85.909 41.144 -87.178 39.551 -88.397 37.921 -89.564 36.255 -90.68 34.552 -91.741 32.812 -92.748 31.036 -93.699 29.224 -94.593 27.374 -95.428 25.488 -96.205 23.566 -96.92 21.606 -97.575 19.611 -98.166 17.578 -98.693 15.509 -99.156 13.403 -99.552 11.261 -99.881 9.082 -100.141 6.866 -100.332 4.614 -100.452 2.325 -100.5 0
"""
if max_len <= 0:
raise ValueError(
"The length of segments must be a positive and non-zero value"
)
# Internal function to help splitting a line
def line_split(x0: float, y0: float, x1: float, y1: float):
x0, y0, x1, y1 = float(x0), float(y0), float(x1), float(y1)
# Line direction & length
rel_x, rel_y = x1 - x0, y1 - y0
distance = math.sqrt(rel_x * rel_x + rel_y * rel_y)
# If the line is too long -> split
if distance > max_len:
lines: list[str] = []
distance_rest = distance % max_len
cur_distance = distance_rest if distance_rest > 0 else max_len
while cur_distance <= distance:
pct = cur_distance / distance
x, y = (
Shape.format_value(x0 + rel_x * pct),
Shape.format_value(y0 + rel_y * pct),
)
lines.append(f"{x} {y}")
cur_distance += max_len
return " ".join(lines), lines[-1].split()
else: # No line split
x1, y1 = Shape.format_value(x1), Shape.format_value(y1)
return f"{x1} {y1}", [x1, y1]
# Getting all points and commands in a list
cmds_and_points = self.flatten().drawing_cmds.split()
i = 0
n = len(cmds_and_points)
# Utility variables
is_line = False
previous_two = None
last_move = None
# Splitting everything splittable, probably improvable
while i < n:
current = cmds_and_points[i]
if current == "l":
# Activate line mode, save previous two points
is_line = True
if (
not previous_two
): # If we're not running into contiguous line, we need to save the previous two
previous_two = [cmds_and_points[i - 2], cmds_and_points[i - 1]]
i += 1
elif (
current == "m"
or current == "n"
or current == "b"
or current == "s"
or current == "p"
or current == "c"
):
if current == "m":
if (
last_move
): # If we had a previous move, we need to close the previous figure before proceding
x0, y0 = None, None
if (
previous_two
): # If I don't have previous point, I can read them on cmds_and_points, else I wil take 'em
x0, y0 = previous_two[0], previous_two[1]
else:
x0, y0 = cmds_and_points[i - 2], cmds_and_points[i - 1]
if not (
x0 == last_move[0] and y0 == last_move[1]
): # Closing last figure
cmds_and_points[i] = (
line_split(x0, y0, last_move[0], last_move[1])[0] + " m"
)
last_move = [cmds_and_points[i + 1], cmds_and_points[i + 2]]
# Disabling line mode, removing previous two points
is_line = False
previous_two = None
i += 1
elif is_line:
# Do the work with the two points found and the previous two
cmds_and_points[i], previous_two = line_split(
previous_two[0],
previous_two[1],
cmds_and_points[i],
cmds_and_points[i + 1],
)
del cmds_and_points[i + 1]
# Let's go to the next two points or tag
i += 1
n -= 1
else: # We're working with points that are not lines points, let's go forward
i += 2
# Close last figure of new shape, taking two last points and two last points of move
i = n
if not previous_two:
while i >= 0:
current = cmds_and_points[i]
current_prev = cmds_and_points[i - 1]
if (
current != "m"
and current != "n"
and current != "b"
and current != "s"
and current != "p"
and current != "c"
and current_prev != "m"
and current_prev != "n"
and current_prev != "b"
and current_prev != "s"
and current_prev != "p"
and current_prev != "c"
):
previous_two = [current, current_prev]
break
i -= 1
if not (
previous_two[0] == last_move[0] and previous_two[1] == last_move[1]
): # Split!
cmds_and_points.append(
"l "
+ line_split(
previous_two[0], previous_two[1], last_move[0], last_move[1]
)[0]
)
# Sew up everything back and update shape
self.drawing_cmds = " ".join(cmds_and_points)
return self
def __to_outline(
self, bord_xy: float, bord_y: float = None, mode: str = "round"
) -> Shape:
"""Converts shape command for filling to a shape command for stroking.
**Tips:** *You could use this for border textures.*
Parameters:
shape (str): The shape in ASS format as a string.
Returns:
A pointer to the current object.
Returns:
A new shape as string, representing the border of the input.
"""
raise NotImplementedError
@staticmethod
def ring(out_r: float, in_r: float) -> Shape:
"""Returns a shape object of a ring with given inner and outer radius, centered around (0,0).
**Tips:** *A ring with increasing inner radius, starting from 0, can look like an outfading point.*
Parameters:
out_r (int or float): The outer radius for the ring.
in_r (int or float): The inner radius for the ring.
Returns:
A shape object representing a ring.
"""
try:
out_r2, in_r2 = out_r * 2, in_r * 2
off = out_r - in_r
off_in_r = off + in_r
off_in_r2 = off + in_r2
except TypeError:
raise TypeError("Number(s) expected")
if in_r >= out_r:
raise ValueError(
"Valid number expected. Inner radius must be less than outer radius"
)
f = Shape.format_value
return Shape(
"m 0 %s "
"b 0 %s 0 0 %s 0 "
"%s 0 %s 0 %s %s "
"%s %s %s %s %s %s "
"%s %s 0 %s 0 %s "
"m %s %s "
"b %s %s %s %s %s %s "
"%s %s %s %s %s %s "
"%s %s %s %s %s %s "
"%s %s %s %s %s %s"
% (
f(out_r), # outer move
f(out_r),
f(out_r), # outer curve 1
f(out_r),
f(out_r2),
f(out_r2),
f(out_r), # outer curve 2
f(out_r2),
f(out_r),
f(out_r2),
f(out_r2),
f(out_r),
f(out_r2), # outer curve 3
f(out_r),
f(out_r2),
f(out_r2),
f(out_r), # outer curve 4
f(off),
f(off_in_r), # inner move
f(off),
f(off_in_r),
f(off),
f(off_in_r2),
f(off_in_r),
f(off_in_r2), # inner curve 1
f(off_in_r),
f(off_in_r2),
f(off_in_r2),
f(off_in_r2),
f(off_in_r2),
f(off_in_r), # inner curve 2
f(off_in_r2),
f(off_in_r),
f(off_in_r2),
f(off),
f(off_in_r),
f(off), # inner curve 3
f(off_in_r),
f(off),
f(off),
f(off),
f(off),
f(off_in_r), # inner curve 4
)
)
@staticmethod
def ellipse(w: float, h: float) -> Shape:
"""Returns a shape object of an ellipse with given width and height, centered around (0,0).
**Tips:** *You could use that to create rounded stribes or arcs in combination with blurring for light effects.*
Parameters:
w (int or float): The width for the ellipse.
h (int or float): The height for the ellipse.
Returns:
A shape object representing an ellipse.
"""
try:
w2, h2 = w / 2, h / 2
except TypeError:
raise TypeError("Number(s) expected")
f = Shape.format_value
return Shape(
"m 0 %s "
"b 0 %s 0 0 %s 0 "
"%s 0 %s 0 %s %s "
"%s %s %s %s %s %s "
"%s %s 0 %s 0 %s"
% (
f(h2), # move
f(h2),
f(w2), # curve 1
f(w2),
f(w),
f(w),
f(h2), # curve 2
f(w),
f(h2),
f(w),
f(h),
f(w2),
f(h), # curve 3
f(w2),
f(h),
f(h),
f(h2), # curve 4
)
)
@staticmethod
def heart(size: float, offset: float = 0) -> Shape:
"""Returns a shape object of a heart object with given size (width&height) and vertical offset of center point, centered around (0,0).
**Tips:** *An offset=size*(2/3) results in a splitted heart.*
Parameters:
size (int or float): The width&height for the heart.
offset (int or float): The vertical offset of center point.
Returns:
A shape object representing an heart.
"""
try:
mult = size / 30
except TypeError:
raise TypeError("Size parameter must be a number")
# Build shape from template
shape = Shape(
"m 15 30 b 27 22 30 18 30 14 30 8 22 0 15 10 8 0 0 8 0 14 0 18 3 22 15 30"
).map(lambda x, y: (x * mult, y * mult))
# Shift mid point of heart vertically
count = 0
def shift_mid_point(x, y):
nonlocal count
count += 1
if count == 7:
try:
return x, y + offset
except TypeError:
raise TypeError("Offset parameter must be a number")
return x, y
# Return result
return shape.map(shift_mid_point)
@staticmethod
def __glance_or_star(
edges: int, inner_size: float, outer_size: float, g_or_s: str
) -> Shape:
"""
General function to create a shape object representing star or glance.
"""
# Alias for utility functions
f = Shape.format_value
def rotate_on_axis_z(point, theta):
# Internal function to rotate a point around z axis by a given angle.
theta = math.radians(theta)
return Quaternion(axis=[0, 0, 1], angle=theta).rotate(point)
# Building shape
shape = ["m 0 %s %s" % (-outer_size, g_or_s)]
inner_p, outer_p = 0, 0
for i in range(1, edges + 1):
# Inner edge
inner_p = rotate_on_axis_z([0, -inner_size, 0], ((i - 0.5) / edges) * 360)
# Outer edge
outer_p = rotate_on_axis_z([0, -outer_size, 0], (i / edges) * 360)
# Add curve / line
if g_or_s == "l":
shape.append(
"%s %s %s %s"
% (f(inner_p[0]), f(inner_p[1]), f(outer_p[0]), f(outer_p[1]))
)
else:
shape.append(
"%s %s %s %s %s %s"
% (
f(inner_p[0]),
f(inner_p[1]),
f(inner_p[0]),
f(inner_p[1]),
f(outer_p[0]),
f(outer_p[1]),
)
)
shape = Shape(" ".join(shape))
# Return result centered
return shape.move()
@staticmethod
def star(edges: int, inner_size: float, outer_size: float) -> Shape:
"""Returns a shape object of a star object with given number of outer edges and sizes, centered around (0,0).
**Tips:** *Different numbers of edges and edge distances allow individual n-angles.*
Parameters:
edges (int): The number of edges of the star.
inner_size (int or float): The inner edges distance from center.
outer_size (int or float): The outer edges distance from center.
Returns:
A shape object as a string representing a star.
"""
return Shape.__glance_or_star(edges, inner_size, outer_size, "l")
@staticmethod
def glance(edges: int, inner_size: float, outer_size: float) -> Shape:
"""Returns a shape object of a glance object with given number of outer edges and sizes, centered around (0,0).
**Tips:** *Glance is similar to Star, but with curves instead of inner edges between the outer edges.*
Parameters:
edges (int): The number of edges of the star.
inner_size (int or float): The inner edges distance from center.
outer_size (int or float): The control points for bezier curves between edges distance from center.
Returns:
A shape object as a string representing a glance.
"""
return Shape.__glance_or_star(edges, inner_size, outer_size, "b")
@staticmethod
def rectangle(w: float = 1.0, h: float = 1.0) -> Shape:
"""Returns a shape object of a rectangle with given width and height, centered around (0,0).
**Tips:** *A rectangle with width=1 and height=1 is a pixel.*
Parameters:
w (int or float): The width for the rectangle.
h (int or float): The height for the rectangle.
Returns:
A shape object representing an rectangle.
"""
try:
f = Shape.format_value
return Shape("m 0 0 l %s 0 %s %s 0 %s 0 0" % (f(w), f(w), f(h), f(h)))
except TypeError:
raise TypeError("Number(s) expected")
@staticmethod
def triangle(size: float) -> Shape:
"""Returns a shape object of an equilateral triangle with given side length, centered around (0,0).
Parameters:
size (int or float): The side length for the triangle.
Returns:
A shape object representing an triangle.
"""
try:
h = math.sqrt(3) * size / 2
base = -h / 6
except TypeError:
raise TypeError("Number expected")
f = Shape.format_value
return Shape(
"m %s %s l %s %s 0 %s %s %s"
% (
f(size / 2),
f(base),
f(size),
f(base + h),
f(base + h),
f(size / 2),
f(base),
)
)

608
syncplay/pyonfx/utils.py Normal file
View File

@ -0,0 +1,608 @@
# -*- coding: utf-8 -*-
# PyonFX: An easy way to create KFX (Karaoke Effects) and complex typesetting using the ASS format (Advanced Substation Alpha).
# Copyright (C) 2019 Antonio Strippoli (CoffeeStraw/YellowFlash)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PyonFX is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see http://www.gnu.org/licenses/.
from __future__ import annotations
import math
import re
from typing import List, Union, TYPE_CHECKING
from .convert import Convert, ColorModel
if TYPE_CHECKING:
from .ass_core import Line, Word, Syllable, Char
class Utils:
"""
This class is a collection of static methods that will help the user in some tasks.
"""
@staticmethod
def all_non_empty(
lines_chars_syls_or_words: List[Union[Line, Word, Syllable, Char]]
) -> List[Union[Line, Word, Syllable, Char]]:
"""
Helps to not check everytime for text containing only spaces or object's duration equals to zero.
Parameters:
lines_chars_syls_or_words (list of :class:`Line<pyonfx.ass_utility.Line>`, :class:`Char<pyonfx.ass_utility.Char>`, :class:`Syllable<pyonfx.ass_utility.Syllable>` or :class:`Word<pyonfx.ass_utility.Word>`)
Returns:
A list containing lines_chars_syls_or_words without objects with duration equals to zero or blank text (no text or only spaces).
"""
out = []
for obj in lines_chars_syls_or_words:
if obj.text.strip() and obj.duration > 0:
out.append(obj)
return out
@staticmethod
def clean_tags(text: str) -> str:
# TODO: Cleans up ASS subtitle lines of badly-formed override. Returns a cleaned up text.
pass
@staticmethod
def accelerate(pct: float, accelerator: float) -> float:
# Modifies pct according to the acceleration provided.
# TODO: Implement acceleration based on bezier's curve
return pct ** accelerator
@staticmethod
def interpolate(
pct: float,
val1: Union[float, str],
val2: Union[float, str],
acc: float = 1.0,
) -> Union[str, float]:
"""
| Interpolates 2 given values (ASS colors, ASS alpha channels or numbers) by percent value as decimal number.
| You can also provide a http://cubic-bezier.com to accelerate based on bezier curves. (TO DO)
|
| You could use that for the calculation of color/alpha gradients.
Parameters:
pct (float): Percent value of the interpolation.
val1 (int, float or str): First value to interpolate (either string or number).
val2 (int, float or str): Second value to interpolate (either string or number).
acc (float, optional): Optional acceleration that influences final percent value.
Returns:
Interpolated value of given 2 values (so either a string or a number).
Examples:
.. code-block:: python3
print( Utils.interpolate(0.5, 10, 20) )
print( Utils.interpolate(0.9, "&HFFFFFF&", "&H000000&") )
>>> 15
>>> &HE5E5E5&
"""
if pct > 1.0 or pct < 0:
raise ValueError(
f"Percent value must be a float between 0.0 and 1.0, but yours was {pct}"
)
# Calculating acceleration (if requested)
pct = Utils.accelerate(pct, acc) if acc != 1.0 else pct
def interpolate_numbers(val1, val2):
nonlocal pct
return val1 + (val2 - val1) * pct
# Interpolating
if type(val1) is str and type(val2) is str:
if len(val1) != len(val2):
raise ValueError(
"ASS values must have the same type (either two alphas, two colors or two colors+alpha)."
)
if len(val1) == len("&HXX&"):
val1 = Convert.alpha_ass_to_dec(val1)
val2 = Convert.alpha_ass_to_dec(val2)
a = interpolate_numbers(val1, val2)
return Convert.alpha_dec_to_ass(a)
elif len(val1) == len("&HBBGGRR&"):
val1 = Convert.color_ass_to_rgb(val1)
val2 = Convert.color_ass_to_rgb(val2)
rgb = tuple(map(interpolate_numbers, val1, val2))
return Convert.color_rgb_to_ass(rgb)
elif len(val1) == len("&HAABBGGRR"):
val1 = Convert.color(val1, ColorModel.ASS, ColorModel.RGBA)
val2 = Convert.color(val2, ColorModel.ASS, ColorModel.RGBA)
rgba = tuple(map(interpolate_numbers, val1, val2))
return Convert.color(rgba, ColorModel.RGBA, ColorModel.ASS)
else:
raise ValueError(
f"Provided inputs '{val1}' and '{val2}' are not valid ASS strings."
)
elif type(val1) in [int, float] and type(val2) in [int, float]:
return interpolate_numbers(val1, val2)
else:
raise TypeError(
"Invalid input(s) type, either pass two strings or two numbers."
)
class FrameUtility:
"""
This class helps in the stressful calculation of frames per frame.
Parameters:
start_time (positive float): Initial time
end_time (positive float): Final time
fr (positive float, optional): Frame Duration
Returns:
Returns a Generator containing start_time, end_time, index and total number of frames for each step.
Examples:
.. code-block:: python3
:emphasize-lines: 1
FU = FrameUtility(0, 100)
for s, e, i, n in FU:
print(f"Frame {i}/{n}: {s} - {e}")
>>> Frame 1/3: 0 - 41.71
>>> Frame 2/3: 41.71 - 83.42
>>> Frame 3/3: 83.42 - 100
"""
def __init__(self, start_time: float, end_time: float, fr: float = 41.71):
# Checking for invalid values
if start_time < 0 or end_time < 0 or fr <= 0 or end_time < start_time:
raise ValueError("Positive values and/or end_time > start_time expected.")
# Calculating number of frames
self.n = math.ceil((end_time - start_time) / fr)
# Defining fields
self.start_time = start_time
self.end_time = end_time
self.current_time = fr
self.fr = fr
def __iter__(self):
# For loop for the first n-1 frames
for i in range(1, self.n):
yield (
round(self.start_time, 2),
round(self.start_time + self.fr, 2),
i,
self.n,
)
self.start_time += self.fr
self.current_time += self.fr
# Last frame, with end value clamped at end_time
yield (round(self.start_time, 2), round(self.end_time, 2), self.n, self.n)
# Resetting to make this object usable again
self.start_time = self.start_time - self.fr * max(self.n - 1, 0)
self.current_time = self.fr
def add(
self,
start_time: int,
end_time: int,
end_value: float,
accelerator: float = 1.0,
) -> float:
"""
This function makes a lot easier the calculation of tags value.
You can see this as a \"\\t\" tag usable in frame per frame operations.
Use it in a for loop which iterates a FrameUtility object, as you can see in the example.
Parameters:
start_time (int): Initial time
end_time (int): Final time
end_value (int or float): Value reached at end_time
accelerator (float): Accelerator value
Examples:
.. code-block:: python3
:emphasize-lines: 4,5
FU = FrameUtility(0, 105, 40)
for s, e, i, n in FU:
fsc = 100
fsc += FU.add(0, 50, 50)
fsc += FU.add(50, 100, -50)
print(f"Frame {i}/{n}: {s} - {e}; fsc: {fsc}")
>>> Frame 1/3: 0 - 40; fsc: 140.0
>>> Frame 2/3: 40 - 80; fsc: 120.0
>>> Frame 3/3: 80 - 105; fsc: 100
"""
if self.current_time < start_time:
return 0
elif self.current_time > end_time:
return end_value
pstart = self.current_time - start_time
pend = end_time - start_time
return Utils.interpolate(pstart / pend, 0, end_value, accelerator)
class ColorUtility:
"""
This class helps to obtain all the color transformations written in a list of lines
(usually all the lines of your input .ass)
to later retrieve all of those transformations that fit between the start_time and end_time of a line passed,
without having to worry about interpolating times or other stressfull tasks.
It is highly suggested to create this object just one time in your script, for performance reasons.
Note:
A few notes about the color transformations in your lines:
* Every color-tag has to be in the format of ``c&Hxxxxxx&``, do not forget the last &;
* You can put color changes without using transformations, like ``{\\1c&HFFFFFF&\\3c&H000000&}Test``, but those will be interpreted as ``{\\t(0,0,\\1c&HFFFFFF&\\3c&H000000&)}Test``;
* For an example of how color changes should be put in your lines, check `this <https://github.com/CoffeeStraw/PyonFX/blob/master/examples/2%20-%20Beginner/in2.ass#L34-L36>`_.
Also, it is important to remember that **color changes in your lines are treated as if they were continuous**.
For example, let's assume we have two lines:
#. ``{\\1c&HFFFFFF&\\t(100,150,\\1c&H000000&)}Line1``, starting at 0ms, ending at 100ms;
#. ``{}Line2``, starting at 100ms, ending at 200ms.
Even if the second line **doesn't have any color changes** and you would expect to have the style's colors,
**it will be treated as it has** ``\\1c&H000000&``. That could seem strange at first,
but thinking about your generated lines, **the majority** will have **start_time and end_time different** from the ones of your original file.
Treating transformations as if they were continous, **ColorUtility will always know the right colors** to pick for you.
Also, remember that even if you can't always see them directly on Aegisub, you can use transformations
with negative times or with times that exceed line total duration.
Parameters:
lines (list of Line): List of lines to be parsed
offset (integer, optional): Milliseconds you may want to shift all the color changes
Returns:
Returns a ColorUtility object.
Examples:
.. code-block:: python3
:emphasize-lines: 2, 4
# Parsing all the lines in the file
CU = ColorUtility(lines)
# Parsing just a single line (the first in this case) in the file
CU = ColorUtility([ line[0] ])
"""
def __init__(self, lines: List[Line], offset: int = 0):
self.color_changes = []
self.c1_req = False
self.c3_req = False
self.c4_req = False
# Compiling regex
tag_all = re.compile(r"{.*?}")
tag_t = re.compile(r"\\t\( *?(-?\d+?) *?, *?(-?\d+?) *?, *(.+?) *?\)")
tag_c1 = re.compile(r"\\1c(&H.{6}&)")
tag_c3 = re.compile(r"\\3c(&H.{6}&)")
tag_c4 = re.compile(r"\\4c(&H.{6}&)")
for line in lines:
# Obtaining all tags enclosured in curly brackets
tags = tag_all.findall(line.raw_text)
# Let's search all color changes in the tags
for tag in tags:
# Get everything beside \t to see if there are some colors there
other_tags = tag_t.sub("", tag)
# Searching for colors in the other tags
c1, c3, c4 = (
tag_c1.search(other_tags),
tag_c3.search(other_tags),
tag_c4.search(other_tags),
)
# If we found something, add to the list as a color change
if c1 or c3 or c4:
if c1:
c1 = c1.group(0)
self.c1_req = True
if c3:
c3 = c3.group(0)
self.c3_req = True
if c4:
c4 = c4.group(0)
self.c4_req = True
self.color_changes.append(
{
"start": line.start_time + offset,
"end": line.start_time + offset,
"acc": 1,
"c1": c1,
"c3": c3,
"c4": c4,
}
)
# Find all transformation in tag
ts = tag_t.findall(tag)
# Working with each transformation
for t in ts:
# Parsing start, end, optional acceleration and colors
start, end, acc_colors = int(t[0]), int(t[1]), t[2].split(",")
acc, c1, c3, c4 = 1, None, None, None
# Do we have also acceleration?
if len(acc_colors) == 1:
c1, c3, c4 = (
tag_c1.search(acc_colors[0]),
tag_c3.search(acc_colors[0]),
tag_c4.search(acc_colors[0]),
)
elif len(acc_colors) == 2:
acc = float(acc_colors[0])
c1, c3, c4 = (
tag_c1.search(acc_colors[1]),
tag_c3.search(acc_colors[1]),
tag_c4.search(acc_colors[1]),
)
else:
# This transformation is malformed (too many ','), let's skip this
continue
# If found, extract from groups
if c1:
c1 = c1.group(0)
self.c1_req = True
if c3:
c3 = c3.group(0)
self.c3_req = True
if c4:
c4 = c4.group(0)
self.c4_req = True
# Saving in the list
self.color_changes.append(
{
"start": line.start_time + start + offset,
"end": line.start_time + end + offset,
"acc": acc,
"c1": c1,
"c3": c3,
"c4": c4,
}
)
def get_color_change(
self, line: Line, c1: bool = None, c3: bool = None, c4: bool = None
) -> str:
"""Returns all the color_changes in the object that fit (in terms of time) between line.start_time and line.end_time.
Parameters:
line (Line object): The line of which you want to get the color changes
c1 (bool, optional): If False, you will not get color values containing primary color
c3 (bool, optional): If False, you will not get color values containing border color
c4 (bool, optional): If False, you will not get color values containing shadow color
Returns:
A string containing color changes interpolated.
Note:
If c1, c3 or c4 is/are None, the script will automatically recognize what you used in the color changes in the lines and put only the ones considered essential.
Examples:
.. code-block:: python3
:emphasize-lines: 6
# Assume that we have l as a copy of line and we're iterating over all the syl in the current line
# All the fun stuff of the effect creation...
l.start_time = line.start_time + syl.start_time
l.end_time = line.start_time + syl.end_time
l.text = "{\\\\an5\\\\pos(%.3f,%.3f)\\\\fscx120\\\\fscy120%s}%s" % (syl.center, syl.middle, CU.get_color_change(l), syl.text)
"""
transform = ""
# If we don't have user's settings, we set c values
# to the ones that we previously saved
if c1 is None:
c1 = self.c1_req
if c3 is None:
c3 = self.c3_req
if c4 is None:
c4 = self.c4_req
# Reading default colors
base_c1 = "\\1c" + line.styleref.color1
base_c3 = "\\3c" + line.styleref.color3
base_c4 = "\\4c" + line.styleref.color4
for color_change in self.color_changes:
if color_change["end"] <= line.start_time:
# Get base colors from this color change, since it is before my current line
# Last color change written in .ass wins
if color_change["c1"]:
base_c1 = color_change["c1"]
if color_change["c3"]:
base_c3 = color_change["c3"]
if color_change["c4"]:
base_c4 = color_change["c4"]
elif color_change["start"] <= line.end_time:
# We have found a valid color change, append it to the transform
start_time = color_change["start"] - line.start_time
end_time = color_change["end"] - line.start_time
# We don't want to have times = 0
start_time = 1 if start_time == 0 else start_time
end_time = 1 if end_time == 0 else end_time
transform += "\\t(%d,%d," % (start_time, end_time)
if color_change["acc"] != 1:
transform += str(color_change["acc"])
if c1 and color_change["c1"]:
transform += color_change["c1"]
if c3 and color_change["c3"]:
transform += color_change["c3"]
if c4 and color_change["c4"]:
transform += color_change["c4"]
transform += ")"
# Appending default color found, if requested
if c4:
transform = base_c4 + transform
if c3:
transform = base_c3 + transform
if c1:
transform = base_c1 + transform
return transform
def get_fr_color_change(
self, line: Line, c1: bool = None, c3: bool = None, c4: bool = None
) -> str:
"""Returns the single color(s) in the color_changes that fit the current frame (line.start_time) in your frame loop.
Note:
If you get errors, try either modifying your \\\\t values or set your **fr parameter** in FU object to **10**.
Parameters:
line (Line object): The line of which you want to get the color changes
c1 (bool, optional): If False, you will not get color values containing primary color.
c3 (bool, optional): If False, you will not get color values containing border color.
c4 (bool, optional): If False, you will not get color values containing shadow color.
Returns:
A string containing color changes interpolated.
Examples:
.. code-block:: python3
:emphasize-lines: 5
# Assume that we have l as a copy of line and we're iterating over all the syl in the current line and we're iterating over the frames
l.start_time = s
l.end_time = e
l.text = "{\\\\an5\\\\pos(%.3f,%.3f)\\\\fscx120\\\\fscy120%s}%s" % (syl.center, syl.middle, CU.get_fr_color_change(l), syl.text)
"""
# If we don't have user's settings, we set c values
# to the ones that we previously saved
if c1 is None:
c1 = self.c1_req
if c3 is None:
c3 = self.c3_req
if c4 is None:
c4 = self.c4_req
# Reading default colors
base_c1 = "\\1c" + line.styleref.color1
base_c3 = "\\3c" + line.styleref.color3
base_c4 = "\\4c" + line.styleref.color4
# Searching valid color_change
current_time = line.start_time
latest_index = -1
for i, color_change in enumerate(self.color_changes):
if current_time >= color_change["start"]:
latest_index = i
# If no color change is found, take default from style
if latest_index == -1:
colors = ""
if c1:
colors += base_c1
if c3:
colors += base_c3
if c4:
colors += base_c4
return colors
# If we have passed the end of the lastest color change available, then take the final values of it
if current_time >= self.color_changes[latest_index]["end"]:
colors = ""
if c1 and self.color_changes[latest_index]["c1"]:
colors += self.color_changes[latest_index]["c1"]
if c3 and self.color_changes[latest_index]["c3"]:
colors += self.color_changes[latest_index]["c3"]
if c4 and self.color_changes[latest_index]["c4"]:
colors += self.color_changes[latest_index]["c4"]
return colors
# Else, interpolate the latest color change
start = current_time - self.color_changes[latest_index]["start"]
end = (
self.color_changes[latest_index]["end"]
- self.color_changes[latest_index]["start"]
)
pct = start / end
# If we're in the first color_change, interpolate with base colors
if latest_index == 0:
colors = ""
if c1 and self.color_changes[latest_index]["c1"]:
colors += "\\1c" + Utils.interpolate(
pct,
base_c1[3:],
self.color_changes[latest_index]["c1"][3:],
self.color_changes[latest_index]["acc"],
)
if c3 and self.color_changes[latest_index]["c3"]:
colors += "\\3c" + Utils.interpolate(
pct,
base_c3[3:],
self.color_changes[latest_index]["c3"][3:],
self.color_changes[latest_index]["acc"],
)
if c4 and self.color_changes[latest_index]["c4"]:
colors += "\\4c" + Utils.interpolate(
pct,
base_c4[3:],
self.color_changes[latest_index]["c4"][3:],
self.color_changes[latest_index]["acc"],
)
return colors
# Else, we interpolate between current color change and previous
colors = ""
if c1:
colors += "\\1c" + Utils.interpolate(
pct,
self.color_changes[latest_index - 1]["c1"][3:],
self.color_changes[latest_index]["c1"][3:],
self.color_changes[latest_index]["acc"],
)
if c3:
colors += "\\3c" + Utils.interpolate(
pct,
self.color_changes[latest_index - 1]["c3"][3:],
self.color_changes[latest_index]["c3"][3:],
self.color_changes[latest_index]["acc"],
)
if c4:
colors += "\\4c" + Utils.interpolate(
pct,
self.color_changes[latest_index - 1]["c4"][3:],
self.color_changes[latest_index]["c4"][3:],
self.color_changes[latest_index]["acc"],
)
return colors