Merge 2.4 base - fix conflicts

This commit is contained in:
Ken Roberts 2016-06-25 10:32:32 -07:00
commit 319870aa83
24 changed files with 810 additions and 189 deletions

View File

@ -219,7 +219,8 @@ def qmd5_hash(salt, data=None):
log.debug('qmd5_hash(salt="%s"' % salt) log.debug('qmd5_hash(salt="%s"' % salt)
hash_obj = QHash(QHash.Md5) hash_obj = QHash(QHash.Md5)
hash_obj.addData(salt) hash_obj.addData(salt)
hash_obj.addData(data) if data:
hash_obj.addData(data)
hash_value = hash_obj.result().toHex() hash_value = hash_obj.result().toHex()
log.debug('qmd5_hash() returning "%s"' % hash_value) log.debug('qmd5_hash() returning "%s"' % hash_value)
return hash_value.data() return hash_value.data()

View File

@ -297,7 +297,11 @@ PJLINK_ERST_STATUS = {'0': ERROR_STRING[E_OK],
PJLINK_POWR_STATUS = {'0': S_STANDBY, PJLINK_POWR_STATUS = {'0': S_STANDBY,
'1': S_ON, '1': S_ON,
'2': S_COOLDOWN, '2': S_COOLDOWN,
'3': S_WARMUP} '3': S_WARMUP,
S_STANDBY: '0',
S_ON: '1',
S_COOLDOWN: '2',
S_WARMUP: '3'}
PJLINK_DEFAULT_SOURCES = {'1': translate('OpenLP.DB', 'RGB'), PJLINK_DEFAULT_SOURCES = {'1': translate('OpenLP.DB', 'RGB'),
'2': translate('OpenLP.DB', 'Video'), '2': translate('OpenLP.DB', 'Video'),

View File

@ -49,7 +49,7 @@ from codecs import decode
from PyQt5.QtCore import pyqtSignal, pyqtSlot from PyQt5.QtCore import pyqtSignal, pyqtSlot
from PyQt5.QtNetwork import QAbstractSocket, QTcpSocket from PyQt5.QtNetwork import QAbstractSocket, QTcpSocket
from openlp.core.common import translate, qmd5_hash from openlp.core.common import translate, md5_hash
from openlp.core.lib.projector.constants import * from openlp.core.lib.projector.constants import *
# Shortcuts # Shortcuts
@ -58,7 +58,7 @@ SocketSTate = QAbstractSocket.SocketState
PJLINK_PREFIX = '%' PJLINK_PREFIX = '%'
PJLINK_CLASS = '1' PJLINK_CLASS = '1'
PJLINK_HEADER = '%s%s' % (PJLINK_PREFIX, PJLINK_CLASS) PJLINK_HEADER = '{prefix}{linkclass}'.format(prefix=PJLINK_PREFIX, linkclass=PJLINK_CLASS)
PJLINK_SUFFIX = CR PJLINK_SUFFIX = CR
@ -91,7 +91,7 @@ class PJLink1(QTcpSocket):
:param poll_time: Time (in seconds) to poll connected projector :param poll_time: Time (in seconds) to poll connected projector
:param socket_timeout: Time (in seconds) to abort the connection if no response :param socket_timeout: Time (in seconds) to abort the connection if no response
""" """
log.debug('PJlink(args="%s" kwargs="%s")' % (args, kwargs)) log.debug('PJlink(args={args} kwargs={kwargs})'.format(args=args, kwargs=kwargs))
self.name = name self.name = name
self.ip = ip self.ip = ip
self.port = port self.port = port
@ -147,7 +147,7 @@ class PJLink1(QTcpSocket):
""" """
Reset projector-specific information to default Reset projector-specific information to default
""" """
log.debug('(%s) reset_information() connect status is %s' % (self.ip, self.state())) log.debug('({ip}) reset_information() connect status is {state}'.format(ip=self.ip, state=self.state()))
self.power = S_OFF self.power = S_OFF
self.pjlink_name = None self.pjlink_name = None
self.manufacturer = None self.manufacturer = None
@ -160,8 +160,10 @@ class PJLink1(QTcpSocket):
self.source = None self.source = None
self.other_info = None self.other_info = None
if hasattr(self, 'timer'): if hasattr(self, 'timer'):
log.debug('({ip}): Calling timer.stop()'.format(ip=self.ip))
self.timer.stop() self.timer.stop()
if hasattr(self, 'socket_timer'): if hasattr(self, 'socket_timer'):
log.debug('({ip}): Calling socket_timer.stop()'.format(ip=self.ip))
self.socket_timer.stop() self.socket_timer.stop()
self.send_queue = [] self.send_queue = []
self.send_busy = False self.send_busy = False
@ -170,7 +172,7 @@ class PJLink1(QTcpSocket):
""" """
Connects signals to methods when thread is started. Connects signals to methods when thread is started.
""" """
log.debug('(%s) Thread starting' % self.ip) log.debug('({ip}) Thread starting'.format(ip=self.ip))
self.i_am_running = True self.i_am_running = True
self.connected.connect(self.check_login) self.connected.connect(self.check_login)
self.disconnected.connect(self.disconnect_from_host) self.disconnected.connect(self.disconnect_from_host)
@ -180,7 +182,7 @@ class PJLink1(QTcpSocket):
""" """
Cleanups when thread is stopped. Cleanups when thread is stopped.
""" """
log.debug('(%s) Thread stopped' % self.ip) log.debug('({ip}) Thread stopped'.format(ip=self.ip))
try: try:
self.connected.disconnect(self.check_login) self.connected.disconnect(self.check_login)
except TypeError: except TypeError:
@ -206,7 +208,7 @@ class PJLink1(QTcpSocket):
Aborts connection and closes socket in case of brain-dead projectors. Aborts connection and closes socket in case of brain-dead projectors.
Should normally be called by socket_timer(). Should normally be called by socket_timer().
""" """
log.debug('(%s) socket_abort() - Killing connection' % self.ip) log.debug('({ip}) socket_abort() - Killing connection'.format(ip=self.ip))
self.disconnect_from_host(abort=True) self.disconnect_from_host(abort=True)
def poll_loop(self): def poll_loop(self):
@ -216,7 +218,7 @@ class PJLink1(QTcpSocket):
""" """
if self.state() != self.ConnectedState: if self.state() != self.ConnectedState:
return return
log.debug('(%s) Updating projector status' % self.ip) log.debug('({ip}) Updating projector status'.format(ip=self.ip))
# Reset timer in case we were called from a set command # Reset timer in case we were called from a set command
if self.timer.interval() < self.poll_time: if self.timer.interval() < self.poll_time:
# Reset timer to 5 seconds # Reset timer to 5 seconds
@ -276,11 +278,17 @@ class PJLink1(QTcpSocket):
self.status_connect = S_CONNECTED self.status_connect = S_CONNECTED
self.projector_status = status self.projector_status = status
(status_code, status_message) = self._get_status(self.status_connect) (status_code, status_message) = self._get_status(self.status_connect)
log.debug('(%s) status_connect: %s: %s' % (self.ip, status_code, status_message if msg is None else msg)) log.debug('({ip}) status_connect: {code}: "{message}"'.format(ip=self.ip,
code=status_code,
message=status_message if msg is None else msg))
(status_code, status_message) = self._get_status(self.projector_status) (status_code, status_message) = self._get_status(self.projector_status)
log.debug('(%s) projector_status: %s: %s' % (self.ip, status_code, status_message if msg is None else msg)) log.debug('({ip}) projector_status: {code}: "{message}"'.format(ip=self.ip,
code=status_code,
message=status_message if msg is None else msg))
(status_code, status_message) = self._get_status(self.error_status) (status_code, status_message) = self._get_status(self.error_status)
log.debug('(%s) error_status: %s: %s' % (self.ip, status_code, status_message if msg is None else msg)) log.debug('({ip}) error_status: {code}: "{message}"'.format(ip=self.ip,
code=status_code,
message=status_message if msg is None else msg))
self.changeStatus.emit(self.ip, status, message) self.changeStatus.emit(self.ip, status, message)
@pyqtSlot() @pyqtSlot()
@ -289,29 +297,31 @@ class PJLink1(QTcpSocket):
Processes the initial connection and authentication (if needed). Processes the initial connection and authentication (if needed).
Starts poll timer if connection is established. Starts poll timer if connection is established.
NOTE: Qt md5 hash function doesn't work with projector authentication. Use the python md5 hash function.
:param data: Optional data if called from another routine :param data: Optional data if called from another routine
""" """
log.debug('(%s) check_login(data="%s")' % (self.ip, data)) log.debug('({ip}) check_login(data="{data}")'.format(ip=self.ip, data=data))
if data is None: if data is None:
# Reconnected setup? # Reconnected setup?
if not self.waitForReadyRead(2000): if not self.waitForReadyRead(2000):
# Possible timeout issue # Possible timeout issue
log.error('(%s) Socket timeout waiting for login' % self.ip) log.error('({ip}) Socket timeout waiting for login'.format(ip=self.ip))
self.change_status(E_SOCKET_TIMEOUT) self.change_status(E_SOCKET_TIMEOUT)
return return
read = self.readLine(self.maxSize) read = self.readLine(self.maxSize)
dontcare = self.readLine(self.maxSize) # Clean out the trailing \r\n dontcare = self.readLine(self.maxSize) # Clean out the trailing \r\n
if read is None: if read is None:
log.warn('(%s) read is None - socket error?' % self.ip) log.warn('({ip}) read is None - socket error?'.format(ip=self.ip))
return return
elif len(read) < 8: elif len(read) < 8:
log.warn('(%s) Not enough data read)' % self.ip) log.warn('({ip}) Not enough data read)'.format(ip=self.ip))
return return
data = decode(read, 'ascii') data = decode(read, 'ascii')
# Possibility of extraneous data on input when reading. # Possibility of extraneous data on input when reading.
# Clean out extraneous characters in buffer. # Clean out extraneous characters in buffer.
dontcare = self.readLine(self.maxSize) dontcare = self.readLine(self.maxSize)
log.debug('(%s) check_login() read "%s"' % (self.ip, data.strip())) log.debug('({ip}) check_login() read "{data}"'.format(ip=self.ip, data=data.strip()))
# At this point, we should only have the initial login prompt with # At this point, we should only have the initial login prompt with
# possible authentication # possible authentication
# PJLink initial login will be: # PJLink initial login will be:
@ -326,26 +336,35 @@ class PJLink1(QTcpSocket):
else: else:
# Process initial connection # Process initial connection
data_check = data.strip().split(' ') data_check = data.strip().split(' ')
log.debug('(%s) data_check="%s"' % (self.ip, data_check)) log.debug('({ip}) data_check="{data}"'.format(ip=self.ip, data=data_check))
# Check for projector reporting an error # Check for projector reporting an error
if data_check[1].upper() == 'ERRA': if data_check[1].upper() == 'ERRA':
# Authentication error # Authentication error
self.disconnect_from_host() self.disconnect_from_host()
self.change_status(E_AUTHENTICATION) self.change_status(E_AUTHENTICATION)
log.debug('(%s) emitting projectorAuthentication() signal' % self.name) log.debug('({ip}) emitting projectorAuthentication() signal'.format(ip=self.name))
return return
elif data_check[1] == '0' and self.pin is not None: elif data_check[1] == '0' and self.pin is not None:
# Pin set and no authentication needed # Pin set and no authentication needed
log.warning('({ip}) Regular connection but PIN set'.format(ip=self.name))
self.disconnect_from_host() self.disconnect_from_host()
self.change_status(E_AUTHENTICATION) self.change_status(E_AUTHENTICATION)
log.debug('(%s) emitting projectorNoAuthentication() signal' % self.name) log.debug('({ip}) Emitting projectorNoAuthentication() signal'.format(ip=self.name))
self.projectorNoAuthentication.emit(self.name) self.projectorNoAuthentication.emit(self.name)
return return
elif data_check[1] == '1': elif data_check[1] == '1':
# Authenticated login with salt # Authenticated login with salt
log.debug('(%s) Setting hash with salt="%s"' % (self.ip, data_check[2])) if self.pin is None:
log.debug('(%s) pin="%s"' % (self.ip, self.pin)) log.warning('({ip}) Authenticated connection but no pin set'.format(ip=self.name))
salt = qmd5_hash(salt=data_check[2].encode('ascii'), data=self.pin.encode('ascii')) self.disconnect_from_host()
self.change_status(E_AUTHENTICATION)
log.debug('({ip}) Emitting projectorAuthentication() signal'.format(ip=self.name))
self.projectorAuthentication.emit(self.name)
return
else:
log.debug('({ip}) Setting hash with salt="{data}"'.format(ip=self.ip, data=data_check[2]))
log.debug('({ip}) pin="{data}"'.format(ip=self.ip, data=self.pin))
salt = md5_hash(salt=data_check[2].encode('ascii'), data=self.pin.encode('ascii'))
else: else:
salt = None salt = None
# We're connected at this point, so go ahead and do regular I/O # We're connected at this point, so go ahead and do regular I/O
@ -355,7 +374,7 @@ class PJLink1(QTcpSocket):
self.send_command(cmd='CLSS', salt=salt) self.send_command(cmd='CLSS', salt=salt)
self.waitForReadyRead() self.waitForReadyRead()
if (not self.no_poll) and (self.state() == self.ConnectedState): if (not self.no_poll) and (self.state() == self.ConnectedState):
log.debug('(%s) Starting timer' % self.ip) log.debug('({ip}) Starting timer'.format(ip=self.ip))
self.timer.setInterval(2000) # Set 2 seconds for initial information self.timer.setInterval(2000) # Set 2 seconds for initial information
self.timer.start() self.timer.start()
@ -364,15 +383,15 @@ class PJLink1(QTcpSocket):
""" """
Socket interface to retrieve data. Socket interface to retrieve data.
""" """
log.debug('(%s) get_data(): Reading data' % self.ip) log.debug('({ip}) get_data(): Reading data'.format(ip=self.ip))
if self.state() != self.ConnectedState: if self.state() != self.ConnectedState:
log.debug('(%s) get_data(): Not connected - returning' % self.ip) log.debug('({ip}) get_data(): Not connected - returning'.format(ip=self.ip))
self.send_busy = False self.send_busy = False
return return
read = self.readLine(self.maxSize) read = self.readLine(self.maxSize)
if read == -1: if read == -1:
# No data available # No data available
log.debug('(%s) get_data(): No data available (-1)' % self.ip) log.debug('({ip}) get_data(): No data available (-1)'.format(ip=self.ip))
self.send_busy = False self.send_busy = False
self.projectorReceivedData.emit() self.projectorReceivedData.emit()
return return
@ -382,11 +401,11 @@ class PJLink1(QTcpSocket):
data = data_in.strip() data = data_in.strip()
if len(data) < 7: if len(data) < 7:
# Not enough data for a packet # Not enough data for a packet
log.debug('(%s) get_data(): Packet length < 7: "%s"' % (self.ip, data)) log.debug('({ip}) get_data(): Packet length < 7: "{data}"'.format(ip=self.ip, data=data))
self.send_busy = False self.send_busy = False
self.projectorReceivedData.emit() self.projectorReceivedData.emit()
return return
log.debug('(%s) get_data(): Checking new data "%s"' % (self.ip, data)) log.debug('({ip}) get_data(): Checking new data "{data}"'.format(ip=self.ip, data=data))
if data.upper().startswith('PJLINK'): if data.upper().startswith('PJLINK'):
# Reconnected from remote host disconnect ? # Reconnected from remote host disconnect ?
self.check_login(data) self.check_login(data)
@ -394,7 +413,7 @@ class PJLink1(QTcpSocket):
self.projectorReceivedData.emit() self.projectorReceivedData.emit()
return return
elif '=' not in data: elif '=' not in data:
log.warn('(%s) get_data(): Invalid packet received' % self.ip) log.warn('({ip}) get_data(): Invalid packet received'.format(ip=self.ip))
self.send_busy = False self.send_busy = False
self.projectorReceivedData.emit() self.projectorReceivedData.emit()
return return
@ -402,15 +421,15 @@ class PJLink1(QTcpSocket):
try: try:
(prefix, class_, cmd, data) = (data_split[0][0], data_split[0][1], data_split[0][2:], data_split[1]) (prefix, class_, cmd, data) = (data_split[0][0], data_split[0][1], data_split[0][2:], data_split[1])
except ValueError as e: except ValueError as e:
log.warn('(%s) get_data(): Invalid packet - expected header + command + data' % self.ip) log.warn('({ip}) get_data(): Invalid packet - expected header + command + data'.format(ip=self.ip))
log.warn('(%s) get_data(): Received data: "%s"' % (self.ip, read)) log.warn('({ip}) get_data(): Received data: "{data}"'.format(ip=self.ip, data=data_in.strip()))
self.change_status(E_INVALID_DATA) self.change_status(E_INVALID_DATA)
self.send_busy = False self.send_busy = False
self.projectorReceivedData.emit() self.projectorReceivedData.emit()
return return
if not (self.pjlink_class in PJLINK_VALID_CMD and cmd in PJLINK_VALID_CMD[self.pjlink_class]): if not (self.pjlink_class in PJLINK_VALID_CMD and cmd in PJLINK_VALID_CMD[self.pjlink_class]):
log.warn('(%s) get_data(): Invalid packet - unknown command "%s"' % (self.ip, cmd)) log.warn('({ip}) get_data(): Invalid packet - unknown command "{data}"'.format(ip=self.ip, data=cmd))
self.send_busy = False self.send_busy = False
self.projectorReceivedData.emit() self.projectorReceivedData.emit()
return return
@ -424,7 +443,7 @@ class PJLink1(QTcpSocket):
:param err: Error code :param err: Error code
""" """
log.debug('(%s) get_error(err=%s): %s' % (self.ip, err, self.errorString())) log.debug('({ip}) get_error(err={error}): {data}'.format(ip=self.ip, error=err, data=self.errorString()))
if err <= 18: if err <= 18:
# QSocket errors. Redefined in projector.constants so we don't mistake # QSocket errors. Redefined in projector.constants so we don't mistake
# them for system errors # them for system errors
@ -453,7 +472,7 @@ class PJLink1(QTcpSocket):
:param queue: Option to force add to queue rather than sending directly :param queue: Option to force add to queue rather than sending directly
""" """
if self.state() != self.ConnectedState: if self.state() != self.ConnectedState:
log.warn('(%s) send_command(): Not connected - returning' % self.ip) log.warn('({ip}) send_command(): Not connected - returning'.format(ip=self.ip))
self.send_queue = [] self.send_queue = []
return return
self.projectorNetwork.emit(self.ip, S_NETWORK_SENDING) self.projectorNetwork.emit(self.ip, S_NETWORK_SENDING)
@ -467,18 +486,19 @@ class PJLink1(QTcpSocket):
out = '%s%s%s %s%s' % (salt, PJLINK_HEADER, cmd, opts, CR) out = '%s%s%s %s%s' % (salt, PJLINK_HEADER, cmd, opts, CR)
if out in self.send_queue: if out in self.send_queue:
# Already there, so don't add # Already there, so don't add
log.debug('(%s) send_command(out="%s") Already in queue - skipping' % (self.ip, out.strip())) log.debug('({ip}) send_command(out="{data}") Already in queue - skipping'.format(ip=self.ip,
data=out.strip()))
elif not queue and len(self.send_queue) == 0: elif not queue and len(self.send_queue) == 0:
# Nothing waiting to send, so just send it # Nothing waiting to send, so just send it
log.debug('(%s) send_command(out="%s") Sending data' % (self.ip, out.strip())) log.debug('({ip}) send_command(out="{data}") Sending data'.format(ip=self.ip, data=out.strip()))
return self._send_command(data=out) return self._send_command(data=out)
else: else:
log.debug('(%s) send_command(out="%s") adding to queue' % (self.ip, out.strip())) log.debug('({ip}) send_command(out="{data}") adding to queue'.format(ip=self.ip, data=out.strip()))
self.send_queue.append(out) self.send_queue.append(out)
self.projectorReceivedData.emit() self.projectorReceivedData.emit()
log.debug('(%s) send_command(): send_busy is %s' % (self.ip, self.send_busy)) log.debug('({ip}) send_command(): send_busy is {data}'.format(ip=self.ip, data=self.send_busy))
if not self.send_busy: if not self.send_busy:
log.debug('(%s) send_command() calling _send_string()') log.debug('({ip}) send_command() calling _send_string()'.format(ip=self.ip))
self._send_command() self._send_command()
@pyqtSlot() @pyqtSlot()
@ -488,10 +508,10 @@ class PJLink1(QTcpSocket):
:param data: Immediate data to send :param data: Immediate data to send
""" """
log.debug('(%s) _send_string()' % self.ip) log.debug('({ip}) _send_string()'.format(ip=self.ip))
log.debug('(%s) _send_string(): Connection status: %s' % (self.ip, self.state())) log.debug('({ip}) _send_string(): Connection status: {data}'.format(ip=self.ip, data=self.state()))
if self.state() != self.ConnectedState: if self.state() != self.ConnectedState:
log.debug('(%s) _send_string() Not connected - abort' % self.ip) log.debug('({ip}) _send_string() Not connected - abort'.format(ip=self.ip))
self.send_queue = [] self.send_queue = []
self.send_busy = False self.send_busy = False
return return
@ -500,18 +520,18 @@ class PJLink1(QTcpSocket):
return return
if data is not None: if data is not None:
out = data out = data
log.debug('(%s) _send_string(data=%s)' % (self.ip, out.strip())) log.debug('({ip}) _send_string(data="{data}")'.format(ip=self.ip, data=out.strip()))
elif len(self.send_queue) != 0: elif len(self.send_queue) != 0:
out = self.send_queue.pop(0) out = self.send_queue.pop(0)
log.debug('(%s) _send_string(queued data=%s)' % (self.ip, out.strip())) log.debug('({ip}) _send_string(queued data="{data}"%s)'.format(ip=self.ip, data=out.strip()))
else: else:
# No data to send # No data to send
log.debug('(%s) _send_string(): No data to send' % self.ip) log.debug('({ip}) _send_string(): No data to send'.format(ip=self.ip))
self.send_busy = False self.send_busy = False
return return
self.send_busy = True self.send_busy = True
log.debug('(%s) _send_string(): Sending "%s"' % (self.ip, out.strip())) log.debug('({ip}) _send_string(): Sending "{data}"'.format(ip=self.ip, data=out.strip()))
log.debug('(%s) _send_string(): Queue = %s' % (self.ip, self.send_queue)) log.debug('({ip}) _send_string(): Queue = {data}'.format(ip=self.ip, data=self.send_queue))
self.socket_timer.start() self.socket_timer.start()
try: try:
self.projectorNetwork.emit(self.ip, S_NETWORK_SENDING) self.projectorNetwork.emit(self.ip, S_NETWORK_SENDING)
@ -523,7 +543,9 @@ class PJLink1(QTcpSocket):
translate('OpenLP.PJLink1', 'Error while sending data to projector')) translate('OpenLP.PJLink1', 'Error while sending data to projector'))
except SocketError as e: except SocketError as e:
self.disconnect_from_host(abort=True) self.disconnect_from_host(abort=True)
self.changeStatus(E_NETWORK, '%s : %s' % (e.error(), e.errorString())) self.changeStatus(E_NETWORK,
translate('OpenLP.PJLink1', '{code} : {string}').format(code=e.error(),
string=e.errorString()))
def process_command(self, cmd, data): def process_command(self, cmd, data):
""" """
@ -532,19 +554,21 @@ class PJLink1(QTcpSocket):
:param cmd: Command to process :param cmd: Command to process
:param data: Data being processed :param data: Data being processed
""" """
log.debug('(%s) Processing command "%s"' % (self.ip, cmd)) log.debug('({ip}) Processing command "{data}"'.format(ip=self.ip, data=cmd))
if data in PJLINK_ERRORS: if data in PJLINK_ERRORS:
# Oops - projector error # Oops - projector error
log.error('({ip}) Projector returned error "{data}"'.format(ip=self.ip, data=data))
if data.upper() == 'ERRA': if data.upper() == 'ERRA':
# Authentication error # Authentication error
self.disconnect_from_host() self.disconnect_from_host()
self.change_status(E_AUTHENTICATION) self.change_status(E_AUTHENTICATION)
log.debug('(%s) emitting projectorAuthentication() signal' % self.ip) log.debug('({ip}) emitting projectorAuthentication() signal'.format(ip=self.ip))
self.projectorAuthentication.emit(self.name) self.projectorAuthentication.emit(self.name)
elif data.upper() == 'ERR1': elif data.upper() == 'ERR1':
# Undefined command # Undefined command
self.change_status(E_UNDEFINED, '%s "%s"' % self.change_status(E_UNDEFINED, '{error} "{data}"'.format(error=translate('OpenLP.PJLink1',
(translate('OpenLP.PJLink1', 'Undefined command:'), cmd)) 'Undefined command:'),
data=cmd))
elif data.upper() == 'ERR2': elif data.upper() == 'ERR2':
# Invalid parameter # Invalid parameter
self.change_status(E_PARAMETER) self.change_status(E_PARAMETER)
@ -559,7 +583,7 @@ class PJLink1(QTcpSocket):
return return
# Command succeeded - no extra information # Command succeeded - no extra information
elif data.upper() == 'OK': elif data.upper() == 'OK':
log.debug('(%s) Command returned OK' % self.ip) log.debug('({ip}) Command returned OK'.format(ip=self.ip))
# A command returned successfully, recheck data # A command returned successfully, recheck data
self.send_busy = False self.send_busy = False
self.projectorReceivedData.emit() self.projectorReceivedData.emit()
@ -568,7 +592,7 @@ class PJLink1(QTcpSocket):
if cmd in self.PJLINK1_FUNC: if cmd in self.PJLINK1_FUNC:
self.PJLINK1_FUNC[cmd](data) self.PJLINK1_FUNC[cmd](data)
else: else:
log.warn('(%s) Invalid command %s' % (self.ip, cmd)) log.warn('({ip}) Invalid command {data}'.format(ip=self.ip, data=cmd))
self.send_busy = False self.send_busy = False
self.projectorReceivedData.emit() self.projectorReceivedData.emit()
@ -587,7 +611,7 @@ class PJLink1(QTcpSocket):
fill = {'Hours': int(data_dict[0]), 'On': False if data_dict[1] == '0' else True} fill = {'Hours': int(data_dict[0]), 'On': False if data_dict[1] == '0' else True}
except ValueError: except ValueError:
# In case of invalid entry # In case of invalid entry
log.warn('(%s) process_lamp(): Invalid data "%s"' % (self.ip, data)) log.warn('({ip}) process_lamp(): Invalid data "{data}"'.format(ip=self.ip, data=data))
return return
lamps.append(fill) lamps.append(fill)
data_dict.pop(0) # Remove lamp hours data_dict.pop(0) # Remove lamp hours
@ -614,7 +638,7 @@ class PJLink1(QTcpSocket):
self.send_command('INST') self.send_command('INST')
else: else:
# Log unknown status response # Log unknown status response
log.warn('Unknown power response: %s' % data) log.warn('({ip}) Unknown power response: {data}'.format(ip=self.ip, data=data))
return return
def process_avmt(self, data): def process_avmt(self, data):
@ -639,7 +663,7 @@ class PJLink1(QTcpSocket):
shutter = True shutter = True
mute = True mute = True
else: else:
log.warn('Unknown shutter response: %s' % data) log.warn('({ip}) Unknown shutter response: {data}'.format(ip=self.ip, data=data))
update_icons = shutter != self.shutter update_icons = shutter != self.shutter
update_icons = update_icons or mute != self.mute update_icons = update_icons or mute != self.mute
self.shutter = shutter self.shutter = shutter
@ -656,6 +680,7 @@ class PJLink1(QTcpSocket):
:param data: Currently selected source :param data: Currently selected source
""" """
self.source = data self.source = data
log.info('({ip}) Setting data source to "{data}"'.format(ip=self.ip, data=self.source))
return return
def process_clss(self, data): def process_clss(self, data):
@ -674,7 +699,8 @@ class PJLink1(QTcpSocket):
else: else:
clss = data clss = data
self.pjlink_class = clss self.pjlink_class = clss
log.debug('(%s) Setting pjlink_class for this projector to "%s"' % (self.ip, self.pjlink_class)) log.debug('({ip}) Setting pjlink_class for this projector to "{data}"'.format(ip=self.ip,
data=self.pjlink_class))
return return
def process_name(self, data): def process_name(self, data):
@ -685,6 +711,7 @@ class PJLink1(QTcpSocket):
:param data: Projector name :param data: Projector name
""" """
self.pjlink_name = data self.pjlink_name = data
log.debug('({ip}) Setting projector PJLink name to "{data}"'.format(ip=self.ip, data=self.pjlink_name))
return return
def process_inf1(self, data): def process_inf1(self, data):
@ -695,6 +722,7 @@ class PJLink1(QTcpSocket):
:param data: Projector manufacturer :param data: Projector manufacturer
""" """
self.manufacturer = data self.manufacturer = data
log.debug('({ip}) Setting projector manufacturer data to "{data}"'.format(ip=self.ip, data=self.manufacturer))
return return
def process_inf2(self, data): def process_inf2(self, data):
@ -705,6 +733,7 @@ class PJLink1(QTcpSocket):
:param data: Model name :param data: Model name
""" """
self.model = data self.model = data
log.debug('({ip}) Setting projector model to "{data}"'.format(ip=self.ip, data=self.model))
return return
def process_info(self, data): def process_info(self, data):
@ -715,6 +744,7 @@ class PJLink1(QTcpSocket):
:param data: Projector other info :param data: Projector other info
""" """
self.other_info = data self.other_info = data
log.debug('({ip}) Setting projector other_info to "{data}"'.format(ip=self.ip, data=self.other_info))
return return
def process_inst(self, data): def process_inst(self, data):
@ -731,6 +761,8 @@ class PJLink1(QTcpSocket):
sources.sort() sources.sort()
self.source_available = sources self.source_available = sources
self.projectorUpdateIcons.emit() self.projectorUpdateIcons.emit()
log.debug('({ip}) Setting projector sources_available to "{data}"'.format(ip=self.ip,
data=self.source_available))
return return
def process_erst(self, data): def process_erst(self, data):
@ -780,7 +812,7 @@ class PJLink1(QTcpSocket):
Initiate connection to projector. Initiate connection to projector.
""" """
if self.state() == self.ConnectedState: if self.state() == self.ConnectedState:
log.warn('(%s) connect_to_host(): Already connected - returning' % self.ip) log.warn('({ip}) connect_to_host(): Already connected - returning'.format(ip=self.ip))
return return
self.change_status(S_CONNECTING) self.change_status(S_CONNECTING)
self.connectToHost(self.ip, self.port if type(self.port) is int else int(self.port)) self.connectToHost(self.ip, self.port if type(self.port) is int else int(self.port))
@ -792,9 +824,9 @@ class PJLink1(QTcpSocket):
""" """
if abort or self.state() != self.ConnectedState: if abort or self.state() != self.ConnectedState:
if abort: if abort:
log.warn('(%s) disconnect_from_host(): Aborting connection' % self.ip) log.warn('({ip}) disconnect_from_host(): Aborting connection'.format(ip=self.ip))
else: else:
log.warn('(%s) disconnect_from_host(): Not connected - returning' % self.ip) log.warn('({ip}) disconnect_from_host(): Not connected - returning'.format(ip=self.ip))
self.reset_information() self.reset_information()
self.disconnectFromHost() self.disconnectFromHost()
try: try:
@ -804,8 +836,8 @@ class PJLink1(QTcpSocket):
if abort: if abort:
self.change_status(E_NOT_CONNECTED) self.change_status(E_NOT_CONNECTED)
else: else:
log.debug('(%s) disconnect_from_host() Current status %s' % (self.ip, log.debug('({ip}) disconnect_from_host() '
self._get_status(self.status_connect)[0])) 'Current status {data}'.format(ip=self.ip, data=self._get_status(self.status_connect)[0]))
if self.status_connect != E_NOT_CONNECTED: if self.status_connect != E_NOT_CONNECTED:
self.change_status(S_NOT_CONNECTED) self.change_status(S_NOT_CONNECTED)
self.reset_information() self.reset_information()
@ -815,60 +847,70 @@ class PJLink1(QTcpSocket):
""" """
Send command to retrieve available source inputs. Send command to retrieve available source inputs.
""" """
log.debug('({ip}) Sending INST command'.format(ip=self.ip))
return self.send_command(cmd='INST') return self.send_command(cmd='INST')
def get_error_status(self): def get_error_status(self):
""" """
Send command to retrieve currently known errors. Send command to retrieve currently known errors.
""" """
log.debug('({ip}) Sending ERST command'.format(ip=self.ip))
return self.send_command(cmd='ERST') return self.send_command(cmd='ERST')
def get_input_source(self): def get_input_source(self):
""" """
Send command to retrieve currently selected source input. Send command to retrieve currently selected source input.
""" """
log.debug('({ip}) Sending INPT command'.format(ip=self.ip))
return self.send_command(cmd='INPT') return self.send_command(cmd='INPT')
def get_lamp_status(self): def get_lamp_status(self):
""" """
Send command to return the lap status. Send command to return the lap status.
""" """
log.debug('({ip}) Sending LAMP command'.format(ip=self.ip))
return self.send_command(cmd='LAMP') return self.send_command(cmd='LAMP')
def get_manufacturer(self): def get_manufacturer(self):
""" """
Send command to retrieve manufacturer name. Send command to retrieve manufacturer name.
""" """
log.debug('({ip}) Sending INF1 command'.format(ip=self.ip))
return self.send_command(cmd='INF1') return self.send_command(cmd='INF1')
def get_model(self): def get_model(self):
""" """
Send command to retrieve the model name. Send command to retrieve the model name.
""" """
log.debug('({ip}) Sending INF2 command'.format(ip=self.ip))
return self.send_command(cmd='INF2') return self.send_command(cmd='INF2')
def get_name(self): def get_name(self):
""" """
Send command to retrieve name as set by end-user (if set). Send command to retrieve name as set by end-user (if set).
""" """
log.debug('({ip}) Sending NAME command'.format(ip=self.ip))
return self.send_command(cmd='NAME') return self.send_command(cmd='NAME')
def get_other_info(self): def get_other_info(self):
""" """
Send command to retrieve extra info set by manufacturer. Send command to retrieve extra info set by manufacturer.
""" """
log.debug('({ip}) Sending INFO command'.format(ip=self.ip))
return self.send_command(cmd='INFO') return self.send_command(cmd='INFO')
def get_power_status(self): def get_power_status(self):
""" """
Send command to retrieve power status. Send command to retrieve power status.
""" """
log.debug('({ip}) Sending POWR command'.format(ip=self.ip))
return self.send_command(cmd='POWR') return self.send_command(cmd='POWR')
def get_shutter_status(self): def get_shutter_status(self):
""" """
Send command to retrieve shutter status. Send command to retrieve shutter status.
""" """
log.debug('({ip}) Sending AVMT command'.format(ip=self.ip))
return self.send_command(cmd='AVMT') return self.send_command(cmd='AVMT')
def set_input_source(self, src=None): def set_input_source(self, src=None):
@ -878,12 +920,12 @@ class PJLink1(QTcpSocket):
:param src: Video source to select in projector :param src: Video source to select in projector
""" """
log.debug('(%s) set_input_source(src=%s)' % (self.ip, src)) log.debug('({ip}) set_input_source(src="{data}")'.format(ip=self.ip, data=src))
if self.source_available is None: if self.source_available is None:
return return
elif src not in self.source_available: elif src not in self.source_available:
return return
log.debug('(%s) Setting input source to %s' % (self.ip, src)) log.debug('({ip}) Setting input source to "{data}"'.format(ip=self.ip, data=src))
self.send_command(cmd='INPT', opts=src) self.send_command(cmd='INPT', opts=src)
self.poll_loop() self.poll_loop()
@ -891,6 +933,7 @@ class PJLink1(QTcpSocket):
""" """
Send command to turn power to on. Send command to turn power to on.
""" """
log.debug('({ip}) Setting POWR to 1 (on)'.format(ip=self.ip))
self.send_command(cmd='POWR', opts='1') self.send_command(cmd='POWR', opts='1')
self.poll_loop() self.poll_loop()
@ -898,6 +941,7 @@ class PJLink1(QTcpSocket):
""" """
Send command to turn power to standby. Send command to turn power to standby.
""" """
log.debug('({ip}) Setting POWR to 0 (standby)'.format(ip=self.ip))
self.send_command(cmd='POWR', opts='0') self.send_command(cmd='POWR', opts='0')
self.poll_loop() self.poll_loop()
@ -905,6 +949,7 @@ class PJLink1(QTcpSocket):
""" """
Send command to set shutter to closed position. Send command to set shutter to closed position.
""" """
log.debug('({ip}) Setting AVMT to 11 (shutter closed)'.format(ip=self.ip))
self.send_command(cmd='AVMT', opts='11') self.send_command(cmd='AVMT', opts='11')
self.poll_loop() self.poll_loop()
@ -912,5 +957,6 @@ class PJLink1(QTcpSocket):
""" """
Send command to set shutter to open position. Send command to set shutter to open position.
""" """
log.debug('({ip}) Setting AVMT to "10" (shutter open)'.format(ip=self.ip))
self.send_command(cmd='AVMT', opts='10') self.send_command(cmd='AVMT', opts='10')
self.poll_loop() self.poll_loop()

View File

@ -59,6 +59,7 @@ class MediaShoutImport(SongImport):
songs = cursor.fetchall() songs = cursor.fetchall()
self.import_wizard.progress_bar.setMaximum(len(songs)) self.import_wizard.progress_bar.setMaximum(len(songs))
for song in songs: for song in songs:
topics = []
if self.stop_import_flag: if self.stop_import_flag:
break break
cursor.execute('SELECT Type, Number, Text FROM Verses WHERE Record = %s ORDER BY Type, Number' cursor.execute('SELECT Type, Number, Text FROM Verses WHERE Record = %s ORDER BY Type, Number'
@ -66,9 +67,10 @@ class MediaShoutImport(SongImport):
verses = cursor.fetchall() verses = cursor.fetchall()
cursor.execute('SELECT Type, Number, POrder FROM PlayOrder WHERE Record = %s ORDER BY POrder' % song.Record) cursor.execute('SELECT Type, Number, POrder FROM PlayOrder WHERE Record = %s ORDER BY POrder' % song.Record)
verse_order = cursor.fetchall() verse_order = cursor.fetchall()
cursor.execute('SELECT Name FROM Themes INNER JOIN SongThemes ON SongThemes.ThemeId = Themes.ThemeId ' if cursor.tables(table='TableName', tableType='TABLE').fetchone():
'WHERE SongThemes.Record = %s' % song.Record) cursor.execute('SELECT Name FROM Themes INNER JOIN SongThemes ON SongThemes.ThemeId = Themes.ThemeId '
topics = cursor.fetchall() 'WHERE SongThemes.Record = %s' % song.Record)
topics = cursor.fetchall()
cursor.execute('SELECT Name FROM Groups INNER JOIN SongGroups ON SongGroups.GroupId = Groups.GroupId ' cursor.execute('SELECT Name FROM Groups INNER JOIN SongGroups ON SongGroups.GroupId = Groups.GroupId '
'WHERE SongGroups.Record = %s' % song.Record) 'WHERE SongGroups.Record = %s' % song.Record)
topics += cursor.fetchall() topics += cursor.fetchall()

View File

@ -156,8 +156,8 @@ class OpenSongImport(SongImport):
ustring = str(root.__getattr__(attr)) ustring = str(root.__getattr__(attr))
if isinstance(fn_or_string, str): if isinstance(fn_or_string, str):
if attr in ['ccli']: if attr in ['ccli']:
ustring = ''.join(re.findall('\d+', ustring))
if ustring: if ustring:
ustring = ''.join(re.findall('\d+', ustring))
setattr(self, fn_or_string, int(ustring)) setattr(self, fn_or_string, int(ustring))
else: else:
setattr(self, fn_or_string, None) setattr(self, fn_or_string, None)

View File

@ -54,7 +54,13 @@ class PresentationManagerImport(SongImport):
# Open file with detected encoding and remove encoding declaration # Open file with detected encoding and remove encoding declaration
text = open(file_path, mode='r', encoding=encoding).read() text = open(file_path, mode='r', encoding=encoding).read()
text = re.sub('.+\?>\n', '', text) text = re.sub('.+\?>\n', '', text)
tree = etree.fromstring(text, parser=etree.XMLParser(recover=True)) try:
tree = etree.fromstring(text, parser=etree.XMLParser(recover=True))
except ValueError:
self.log_error(file_path,
translate('SongsPlugin.PresentationManagerImport',
'File is not in XML-format, which is the only format supported.'))
continue
root = objectify.fromstring(etree.tostring(tree)) root = objectify.fromstring(etree.tostring(tree))
self.process_song(root) self.process_song(root)

View File

@ -116,7 +116,13 @@ class SongShowPlusImport(SongImport):
null, verse_name_length, = struct.unpack("BB", song_data.read(2)) null, verse_name_length, = struct.unpack("BB", song_data.read(2))
verse_name = self.decode(song_data.read(verse_name_length)) verse_name = self.decode(song_data.read(verse_name_length))
length_descriptor_size, = struct.unpack("B", song_data.read(1)) length_descriptor_size, = struct.unpack("B", song_data.read(1))
log.debug(length_descriptor_size) log.debug('length_descriptor_size: %d' % length_descriptor_size)
# In the case of song_numbers the number is in the data from the
# current position to the next block starts
if block_key == SONG_NUMBER:
sn_bytes = song_data.read(length_descriptor_size - 1)
self.song_number = int.from_bytes(sn_bytes, byteorder='little')
continue
# Detect if/how long the length descriptor is # Detect if/how long the length descriptor is
if length_descriptor_size == 12 or length_descriptor_size == 20: if length_descriptor_size == 12 or length_descriptor_size == 20:
length_descriptor, = struct.unpack("I", song_data.read(4)) length_descriptor, = struct.unpack("I", song_data.read(4))

View File

@ -73,6 +73,14 @@ class VideoPsalmImport(SongImport):
processed_content += c processed_content += c
c = next(file_content_it) c = next(file_content_it)
processed_content += '"' + c processed_content += '"' + c
# Remove control characters
elif (c < chr(32)):
processed_content += ' '
# Handle escaped characters
elif c == '\\':
processed_content += c
c = next(file_content_it)
processed_content += c
else: else:
processed_content += c processed_content += c
songbook = json.loads(processed_content.strip()) songbook = json.loads(processed_content.strip())
@ -117,6 +125,6 @@ class VideoPsalmImport(SongImport):
if not self.finish(): if not self.finish():
self.log_error('Could not import %s' % self.title) self.log_error('Could not import %s' % self.title)
except Exception as e: except Exception as e:
self.log_error(translate('SongsPlugin.VideoPsalmImport', 'File %s' % file.name), self.log_error(self.import_source,
translate('SongsPlugin.VideoPsalmImport', 'Error: %s') % e) translate('SongsPlugin.VideoPsalmImport', 'Error: %s') % e)
song_file.close() song_file.close()

View File

@ -4548,7 +4548,7 @@ Dateiendung nicht unterstützt.</translation>
<message> <message>
<location filename="../../openlp/plugins/songs/lib/mediaitem.py" line="109"/> <location filename="../../openlp/plugins/songs/lib/mediaitem.py" line="109"/>
<source>&amp;Clone</source> <source>&amp;Clone</source>
<translation>&amp;Klonen</translation> <translation>&amp;Duplizieren</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/lib/mediamanageritem.py" line="379"/> <location filename="../../openlp/core/lib/mediamanageritem.py" line="379"/>

View File

@ -1592,7 +1592,8 @@ Complete</source>
<location filename="../../openlp/plugins/bibles/forms/bibleupgradeform.py" line="549"/> <location filename="../../openlp/plugins/bibles/forms/bibleupgradeform.py" line="549"/>
<source>Upgrading Bible(s): %(success)d successful%(failed_text)s <source>Upgrading Bible(s): %(success)d successful%(failed_text)s
Please note that verses from Web Bibles will be downloaded on demand and so an Internet connection is required.</source> Please note that verses from Web Bibles will be downloaded on demand and so an Internet connection is required.</source>
<translation type="unfinished"/> <translation>Aktualizowanie Biblii: %(success)d z powodzeniem %(failed_text)s
Należy pamiętać, że wersety z Biblii online będą pobierane na żądnie, więc połączenie z internetem jest wymagane.</translation>
</message> </message>
</context> </context>
<context> <context>
@ -1772,7 +1773,7 @@ Wtyczka Slajdu Tekstowego umożliwia umieszczanie zwykłych ciągów znaków na
<message> <message>
<location filename="../../openlp/plugins/custom/lib/mediaitem.py" line="190"/> <location filename="../../openlp/plugins/custom/lib/mediaitem.py" line="190"/>
<source>Are you sure you want to delete the &quot;%d&quot; selected custom slide(s)?</source> <source>Are you sure you want to delete the &quot;%d&quot; selected custom slide(s)?</source>
<translation type="unfinished"/> <translation>Czy na pewno chcesz usunąć %n zaznaczonych slajdów niestandardowych?</translation>
</message> </message>
</context> </context>
<context> <context>
@ -1998,7 +1999,7 @@ Czy mimo to chcesz dodać inne?</translation>
<message> <message>
<location filename="../../openlp/core/ui/media/systemplayer.py" line="249"/> <location filename="../../openlp/core/ui/media/systemplayer.py" line="249"/>
<source>This media player uses your operating system to provide media capabilities.</source> <source>This media player uses your operating system to provide media capabilities.</source>
<translation type="unfinished"/> <translation>Ten odtwarzacz multimediów wykorzystuje twój system operacyjny dla zapewnienia zdolności multimedialnych</translation>
</message> </message>
</context> </context>
<context> <context>
@ -2426,147 +2427,147 @@ OpenLP jest pisany i wspierany przez wolontariuszy. Jeśli chciałbyś, aby pows
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="157"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="157"/>
<source>Project Lead</source> <source>Project Lead</source>
<translation type="unfinished"/> <translation>Prowadzenie projektu</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="158"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="158"/>
<source>Developers</source> <source>Developers</source>
<translation type="unfinished"/> <translation>Deweloperzy</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="159"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="159"/>
<source>Contributors</source> <source>Contributors</source>
<translation type="unfinished"/> <translation>Uczestnicy</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="160"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="160"/>
<source>Packagers</source> <source>Packagers</source>
<translation type="unfinished"/> <translation>Paczki</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="161"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="161"/>
<source>Testers</source> <source>Testers</source>
<translation type="unfinished"/> <translation>Testerzy</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="162"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="162"/>
<source>Translators</source> <source>Translators</source>
<translation type="unfinished"/> <translation>Tłumacze</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="163"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="163"/>
<source>Afrikaans (af)</source> <source>Afrikaans (af)</source>
<translation type="unfinished"/> <translation>Afrykanerski (af)</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="164"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="164"/>
<source>Czech (cs)</source> <source>Czech (cs)</source>
<translation type="unfinished"/> <translation>Czeski (cs)</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="165"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="165"/>
<source>Danish (da)</source> <source>Danish (da)</source>
<translation type="unfinished"/> <translation>Duński (da)</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="166"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="166"/>
<source>German (de)</source> <source>German (de)</source>
<translation type="unfinished"/> <translation>Niemiecki (de)</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="167"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="167"/>
<source>Greek (el)</source> <source>Greek (el)</source>
<translation type="unfinished"/> <translation>Grecki (el)</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="168"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="168"/>
<source>English, United Kingdom (en_GB)</source> <source>English, United Kingdom (en_GB)</source>
<translation type="unfinished"/> <translation>Angielski, UK (en_GB)</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="169"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="169"/>
<source>English, South Africa (en_ZA)</source> <source>English, South Africa (en_ZA)</source>
<translation type="unfinished"/> <translation>Angielski, RPA (en_ZA)</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="170"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="170"/>
<source>Spanish (es)</source> <source>Spanish (es)</source>
<translation type="unfinished"/> <translation>Hiszpański (es)</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="171"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="171"/>
<source>Estonian (et)</source> <source>Estonian (et)</source>
<translation type="unfinished"/> <translation>Estoński (et)</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="172"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="172"/>
<source>Finnish (fi)</source> <source>Finnish (fi)</source>
<translation type="unfinished"/> <translation>Fiński (fi)</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="173"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="173"/>
<source>French (fr)</source> <source>French (fr)</source>
<translation type="unfinished"/> <translation>Francuski (fr)</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="174"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="174"/>
<source>Hungarian (hu)</source> <source>Hungarian (hu)</source>
<translation type="unfinished"/> <translation>Węgierski (hu)</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="175"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="175"/>
<source>Indonesian (id)</source> <source>Indonesian (id)</source>
<translation type="unfinished"/> <translation>Indonezyjski (id)</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="176"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="176"/>
<source>Japanese (ja)</source> <source>Japanese (ja)</source>
<translation type="unfinished"/> <translation>Japoński (ja)</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="177"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="177"/>
<source>Norwegian Bokmål (nb)</source> <source>Norwegian Bokmål (nb)</source>
<translation type="unfinished"/> <translation>Norweski (nb)</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="178"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="178"/>
<source>Dutch (nl)</source> <source>Dutch (nl)</source>
<translation type="unfinished"/> <translation>Holenderski (nl)</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="179"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="179"/>
<source>Polish (pl)</source> <source>Polish (pl)</source>
<translation type="unfinished"/> <translation>Polski (pl)</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="180"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="180"/>
<source>Portuguese, Brazil (pt_BR)</source> <source>Portuguese, Brazil (pt_BR)</source>
<translation type="unfinished"/> <translation>Portugalski (pt_BR)</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="181"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="181"/>
<source>Russian (ru)</source> <source>Russian (ru)</source>
<translation type="unfinished"/> <translation>Rosyjski (ru)</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="182"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="182"/>
<source>Swedish (sv)</source> <source>Swedish (sv)</source>
<translation type="unfinished"/> <translation>Szwedzki (sv)</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="183"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="183"/>
<source>Tamil(Sri-Lanka) (ta_LK)</source> <source>Tamil(Sri-Lanka) (ta_LK)</source>
<translation type="unfinished"/> <translation>Tamilski (Sri-Lanka) (ta_LK)</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="184"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="184"/>
<source>Chinese(China) (zh_CN)</source> <source>Chinese(China) (zh_CN)</source>
<translation type="unfinished"/> <translation>Chiński(Chiny) (zh_CN)</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="185"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="185"/>
<source>Documentation</source> <source>Documentation</source>
<translation type="unfinished"/> <translation>Dokumentacja</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="186"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="186"/>
@ -2577,7 +2578,12 @@ OpenLP jest pisany i wspierany przez wolontariuszy. Jeśli chciałbyś, aby pows
Oxygen Icons: http://techbase.kde.org/Projects/Oxygen/ Oxygen Icons: http://techbase.kde.org/Projects/Oxygen/
MuPDF: http://www.mupdf.com/ MuPDF: http://www.mupdf.com/
</source> </source>
<translation type="unfinished"/> <translation>Stworzono przy użyciu:
Python: http://www.python.org/
Qt5: http://qt.io
PyQt5: http://www.riverbankcomputing.co.uk/software/pyqt/intro
Oxygen Icons: http://techbase.kde.org/Projects/Oxygen/
MuPDF: http://www.mupdf.com/</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="192"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="192"/>
@ -2592,13 +2598,19 @@ OpenLP jest pisany i wspierany przez wolontariuszy. Jeśli chciałbyś, aby pows
on the cross, setting us free from sin. We on the cross, setting us free from sin. We
bring this software to you for free because bring this software to you for free because
He has set us free.</source> He has set us free.</source>
<translation type="unfinished"/> <translation>Końcowe podziękowanie:
&quot;Dla Boga, który tak umiłował świat, że Syna swego, jednorodzonego dał, aby każdy, kto w niego wierzy, nie zginął, ale miał życie wieczne&quot; J 3,16
Na koniec ale nie mniej ważnie, końcowe podziękowania dla Boga, naszego Ojca, za zesłanie nam swego Syna, by umarł za nas na krzyżu i uczynił nas wolnymi od grzechu.
Dostarczamy to oprogramowanie za darmo, ponieważ On uczynił nas wolnymi.
</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="303"/> <location filename="../../openlp/core/ui/aboutdialog.py" line="303"/>
<source>Copyright © 2004-2016 %s <source>Copyright © 2004-2016 %s
Portions copyright © 2004-2016 %s</source> Portions copyright © 2004-2016 %s</source>
<translation type="unfinished"/> <translation>Copyright © 2004-2016 %s
Portions copyright © 2004-2016 %s</translation>
</message> </message>
</context> </context>
<context> <context>
@ -3221,12 +3233,12 @@ Katalog z danymi zostanie przywrucony jak OpenLP zostanie zamknięty.</translati
<location filename="../../openlp/core/ui/exceptiondialog.py" line="92"/> <location filename="../../openlp/core/ui/exceptiondialog.py" line="92"/>
<source>Please enter a description of what you were doing to cause this error. If possible, write in English. <source>Please enter a description of what you were doing to cause this error. If possible, write in English.
(Minimum 20 characters)</source> (Minimum 20 characters)</source>
<translation type="unfinished"/> <translation>Proszę opisać czynności wykonywane bezpośrednio przed wystąpieniem błędu. Jeśli to możliwe, napisz w języku angielskim (min. 20 znaków).</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/exceptiondialog.py" line="96"/> <location filename="../../openlp/core/ui/exceptiondialog.py" line="96"/>
<source>Oops! OpenLP hit a problem, and couldn&apos;t recover. The text in the box below contains information that might be helpful to the OpenLP developers, so please e-mail it to bugs@openlp.org, along with a detailed description of what you were doing when the problem occurred. Also attach any files that triggered the problem.</source> <source>Oops! OpenLP hit a problem, and couldn&apos;t recover. The text in the box below contains information that might be helpful to the OpenLP developers, so please e-mail it to bugs@openlp.org, along with a detailed description of what you were doing when the problem occurred. Also attach any files that triggered the problem.</source>
<translation type="unfinished"/> <translation>Oops! OpenLP napotkał problem i już nie wstał... Poniższe pole tekstowe zawiera informacje, które mogą być pomocne dla programistów OpenLP. Proszę wysłać e-mail na adres bugs@openlp.org wraz z dokładnym opisem czynności jakie były wykonywane w momencie wystąpienia błędu.</translation>
</message> </message>
</context> </context>
<context> <context>
@ -3629,7 +3641,7 @@ By anulować kreatora pierwszego uruchomienia całkowicie (i nie uruchamiać Ope
<message> <message>
<location filename="../../openlp/core/ui/formattingtagcontroller.py" line="168"/> <location filename="../../openlp/core/ui/formattingtagcontroller.py" line="168"/>
<source>End tag %(end)s does not match end tag for start tag %(start)s</source> <source>End tag %(end)s does not match end tag for start tag %(start)s</source>
<translation type="unfinished"/> <translation>Końcowy znacznik %s nie pasuje do początkowego %s</translation>
</message> </message>
</context> </context>
<context> <context>
@ -4409,47 +4421,47 @@ Proces został zatrzymany i nie wykonano żadnych zmian.</translation>
<message> <message>
<location filename="../../openlp/core/ui/mainwindow.py" line="393"/> <location filename="../../openlp/core/ui/mainwindow.py" line="393"/>
<source>&amp;Recent Services</source> <source>&amp;Recent Services</source>
<translation type="unfinished"/> <translation>Ostatnio &amp;używane</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/mainwindow.py" line="404"/> <location filename="../../openlp/core/ui/mainwindow.py" line="404"/>
<source>&amp;New Service</source> <source>&amp;New Service</source>
<translation type="unfinished"/> <translation>Nowy plan nabożeństwa</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/mainwindow.py" line="407"/> <location filename="../../openlp/core/ui/mainwindow.py" line="407"/>
<source>&amp;Open Service</source> <source>&amp;Open Service</source>
<translation type="unfinished"/> <translation>Otwórz plan nabożeństwa.</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/mainwindow.py" line="410"/> <location filename="../../openlp/core/ui/mainwindow.py" line="410"/>
<source>&amp;Save Service</source> <source>&amp;Save Service</source>
<translation type="unfinished"/> <translation>Zapisz plan nabożeństwa</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/mainwindow.py" line="413"/> <location filename="../../openlp/core/ui/mainwindow.py" line="413"/>
<source>Save Service &amp;As...</source> <source>Save Service &amp;As...</source>
<translation type="unfinished"/> <translation>Zapisz plan nabożeństwa jako</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/mainwindow.py" line="460"/> <location filename="../../openlp/core/ui/mainwindow.py" line="460"/>
<source>&amp;Manage Plugins</source> <source>&amp;Manage Plugins</source>
<translation type="unfinished"/> <translation>Zarządzaj profilami</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/mainwindow.py" line="1101"/> <location filename="../../openlp/core/ui/mainwindow.py" line="1101"/>
<source>Exit OpenLP</source> <source>Exit OpenLP</source>
<translation type="unfinished"/> <translation>Wyjdź z OpenLP</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/mainwindow.py" line="1101"/> <location filename="../../openlp/core/ui/mainwindow.py" line="1101"/>
<source>Are you sure you want to exit OpenLP?</source> <source>Are you sure you want to exit OpenLP?</source>
<translation type="unfinished"/> <translation>Czy na pewno chcesz zamknąć OpenLP?</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/mainwindow.py" line="1108"/> <location filename="../../openlp/core/ui/mainwindow.py" line="1108"/>
<source>&amp;Exit OpenLP</source> <source>&amp;Exit OpenLP</source>
<translation type="unfinished"/> <translation>Wyjdź z OpenLP</translation>
</message> </message>
</context> </context>
<context> <context>
@ -4620,7 +4632,7 @@ Nieobsługiwany przyrostek.</translation>
<location filename="../../openlp/core/ui/media/playertab.py" line="256"/> <location filename="../../openlp/core/ui/media/playertab.py" line="256"/>
<source>NOTE: To use VLC you must install the %s version</source> <source>NOTE: To use VLC you must install the %s version</source>
<comment>Will insert &quot;32bit&quot; or &quot;64bit&quot;</comment> <comment>Will insert &quot;32bit&quot; or &quot;64bit&quot;</comment>
<translation type="unfinished"/> <translation>Uwaga: By używać VLC musisz zainstalować %s wersję</translation>
</message> </message>
</context> </context>
<context> <context>
@ -4663,7 +4675,7 @@ Nieobsługiwany przyrostek.</translation>
<message> <message>
<location filename="../../openlp/core/ui/plugindialog.py" line="81"/> <location filename="../../openlp/core/ui/plugindialog.py" line="81"/>
<source>Manage Plugins</source> <source>Manage Plugins</source>
<translation type="unfinished"/> <translation>Zarządzaj wtyczkami </translation>
</message> </message>
</context> </context>
<context> <context>
@ -5291,7 +5303,7 @@ Nieobsługiwany przyrostek.</translation>
<message> <message>
<location filename="../../openlp/core/ui/projector/manager.py" line="653"/> <location filename="../../openlp/core/ui/projector/manager.py" line="653"/>
<source>Shutter is</source> <source>Shutter is</source>
<translation type="unfinished"/> <translation>Przesłona</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/projector/manager.py" line="653"/> <location filename="../../openlp/core/ui/projector/manager.py" line="653"/>
@ -5351,12 +5363,12 @@ Nieobsługiwany przyrostek.</translation>
<message> <message>
<location filename="../../openlp/core/ui/projector/manager.py" line="476"/> <location filename="../../openlp/core/ui/projector/manager.py" line="476"/>
<source>Delete projector (%s) %s?</source> <source>Delete projector (%s) %s?</source>
<translation type="unfinished"/> <translation>Usunąć projektor (%s) %s?</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/projector/manager.py" line="478"/> <location filename="../../openlp/core/ui/projector/manager.py" line="478"/>
<source>Are you sure you want to delete this projector?</source> <source>Are you sure you want to delete this projector?</source>
<translation type="unfinished"/> <translation>Czy napewno chcesz usunąć ten projektor?</translation>
</message> </message>
</context> </context>
<context> <context>
@ -5379,7 +5391,7 @@ Nieobsługiwany przyrostek.</translation>
<message> <message>
<location filename="../../openlp/core/lib/projector/pjlink1.py" line="767"/> <location filename="../../openlp/core/lib/projector/pjlink1.py" line="767"/>
<source>Cover</source> <source>Cover</source>
<translation type="unfinished"/> <translation>Osłona</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/lib/projector/pjlink1.py" line="771"/> <location filename="../../openlp/core/lib/projector/pjlink1.py" line="771"/>
@ -6456,14 +6468,15 @@ Zostaną one usunięte, jeśli będziesz kontynuował/</translation>
<message> <message>
<location filename="../../openlp/core/ui/thememanager.py" line="768"/> <location filename="../../openlp/core/ui/thememanager.py" line="768"/>
<source>Unable to delete theme</source> <source>Unable to delete theme</source>
<translation type="unfinished"/> <translation>Nie można usunąć motywu</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/ui/thememanager.py" line="768"/> <location filename="../../openlp/core/ui/thememanager.py" line="768"/>
<source>Theme is currently used <source>Theme is currently used
%s</source> %s</source>
<translation type="unfinished"/> <translation>Motyw, który jest obecnie używany
%s</translation>
</message> </message>
</context> </context>
<context> <context>
@ -7510,19 +7523,19 @@ Proszę zaznaczyć go ręcznie.</translation>
<message> <message>
<location filename="../../openlp/core/common/uistrings.py" line="125"/> <location filename="../../openlp/core/common/uistrings.py" line="125"/>
<source>Replace live background is not available when the WebKit player is disabled.</source> <source>Replace live background is not available when the WebKit player is disabled.</source>
<translation type="unfinished"/> <translation>Zastępowanie tła live jest niedostępne gdy odtwarzacz WebKit jest zablokowany</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songs/lib/ui.py" line="38"/> <location filename="../../openlp/plugins/songs/lib/ui.py" line="38"/>
<source>Songbook</source> <source>Songbook</source>
<comment>Singular</comment> <comment>Singular</comment>
<translation type="unfinished"/> <translation>Śpiewnik</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songs/lib/ui.py" line="39"/> <location filename="../../openlp/plugins/songs/lib/ui.py" line="39"/>
<source>Songbooks</source> <source>Songbooks</source>
<comment>Plural</comment> <comment>Plural</comment>
<translation type="unfinished"/> <translation>Śpiewniki</translation>
</message> </message>
</context> </context>
<context> <context>
@ -7970,12 +7983,12 @@ Proszę zaznaczyć go ręcznie.</translation>
<message> <message>
<location filename="../../openlp/plugins/remotes/lib/remotetab.py" line="198"/> <location filename="../../openlp/plugins/remotes/lib/remotetab.py" line="198"/>
<source>iOS App</source> <source>iOS App</source>
<translation type="unfinished"/> <translation>Aplikacja iOS</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/remotes/lib/remotetab.py" line="199"/> <location filename="../../openlp/plugins/remotes/lib/remotetab.py" line="199"/>
<source>Scan the QR code or click &lt;a href=&quot;%s&quot;&gt;download&lt;/a&gt; to install the iOS app from the App Store.</source> <source>Scan the QR code or click &lt;a href=&quot;%s&quot;&gt;download&lt;/a&gt; to install the iOS app from the App Store.</source>
<translation type="unfinished"/> <translation>Zeskanuj kod QR lub kliknij &lt;a href=&quot;%s&quot;&gt;pobierz&lt;/a&gt;, aby zainstalować aplikację z App Store.</translation>
</message> </message>
</context> </context>
<context> <context>
@ -8712,42 +8725,43 @@ Proszę, wpisz zwrotki oddzielając je spacją.</translation>
<location filename="../../openlp/plugins/songs/forms/editsongform.py" line="189"/> <location filename="../../openlp/plugins/songs/forms/editsongform.py" line="189"/>
<source>There are no verses corresponding to &quot;%(invalid)s&quot;. Valid entries are %(valid)s. <source>There are no verses corresponding to &quot;%(invalid)s&quot;. Valid entries are %(valid)s.
Please enter the verses separated by spaces.</source> Please enter the verses separated by spaces.</source>
<translation type="unfinished"/> <translation>Nie ma zwrotek o oznaczeniu &quot;%(invalid)s&quot;. Właściwymi oznaczeniami %(valid)s.
Proszę, wpisz zwrotki oddzielając je spacją.</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="317"/> <location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="317"/>
<source>&amp;Manage Authors, Topics, Songbooks</source> <source>&amp;Manage Authors, Topics, Songbooks</source>
<translation type="unfinished"/> <translation>Zarządzaj autorami, &amp;tematami, śpiewnikami</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="322"/> <location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="322"/>
<source>Add &amp;to Song</source> <source>Add &amp;to Song</source>
<translation type="unfinished"/> <translation>Dodaj do &amp;pieśni</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="323"/> <location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="323"/>
<source>Re&amp;move</source> <source>Re&amp;move</source>
<translation type="unfinished"/> <translation>Usuń</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="324"/> <location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="324"/>
<source>Authors, Topics &amp;&amp; Songbooks</source> <source>Authors, Topics &amp;&amp; Songbooks</source>
<translation type="unfinished"/> <translation>Autorzy, tematy i śpiewniki</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songs/forms/editsongform.py" line="683"/> <location filename="../../openlp/plugins/songs/forms/editsongform.py" line="683"/>
<source>Add Songbook</source> <source>Add Songbook</source>
<translation type="unfinished"/> <translation>Dodaj Śpiewnik</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songs/forms/editsongform.py" line="683"/> <location filename="../../openlp/plugins/songs/forms/editsongform.py" line="683"/>
<source>This Songbook does not exist, do you want to add it?</source> <source>This Songbook does not exist, do you want to add it?</source>
<translation type="unfinished"/> <translation>Ten śpiewnik nie istnieje, czy chcesz go dodać?</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songs/forms/editsongform.py" line="700"/> <location filename="../../openlp/plugins/songs/forms/editsongform.py" line="700"/>
<source>This Songbook is already in the list.</source> <source>This Songbook is already in the list.</source>
<translation type="unfinished"/> <translation>Ten Śpiewnik już jest na liście</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songs/forms/editsongform.py" line="707"/> <location filename="../../openlp/plugins/songs/forms/editsongform.py" line="707"/>
@ -8764,7 +8778,9 @@ Please enter the verses separated by spaces.</source>
<source>Unable to find the following file: <source>Unable to find the following file:
%s %s
Do you want to remove the entry from the song?</source> Do you want to remove the entry from the song?</source>
<translation type="unfinished"/> <translation>Nie można znaleźć pliku:
%s
Czy chcesz usunąć wpis z pieśni?</translation>
</message> </message>
</context> </context>
<context> <context>
@ -9094,37 +9110,37 @@ Do you want to remove the entry from the song?</source>
<message> <message>
<location filename="../../openlp/plugins/songs/lib/importer.py" line="194"/> <location filename="../../openlp/plugins/songs/lib/importer.py" line="194"/>
<source>OpenLyrics or OpenLP 2 Exported Song</source> <source>OpenLyrics or OpenLP 2 Exported Song</source>
<translation type="unfinished"/> <translation>OpenLyrics lub wyeksportowane pieśni OpenLP 2.0</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songs/lib/importer.py" line="201"/> <location filename="../../openlp/plugins/songs/lib/importer.py" line="201"/>
<source>OpenLP 2 Databases</source> <source>OpenLP 2 Databases</source>
<translation type="unfinished"/> <translation>Bazy danych OpenLP 2.0</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songs/lib/importer.py" line="255"/> <location filename="../../openlp/plugins/songs/lib/importer.py" line="255"/>
<source>LyriX Files</source> <source>LyriX Files</source>
<translation type="unfinished"/> <translation>Pliki LyriX</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songs/lib/importer.py" line="256"/> <location filename="../../openlp/plugins/songs/lib/importer.py" line="256"/>
<source>LyriX (Exported TXT-files)</source> <source>LyriX (Exported TXT-files)</source>
<translation type="unfinished"/> <translation>LyriX (Eksportowane pliki txt)</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songs/lib/importer.py" line="343"/> <location filename="../../openlp/plugins/songs/lib/importer.py" line="343"/>
<source>VideoPsalm Files</source> <source>VideoPsalm Files</source>
<translation type="unfinished"/> <translation>pliki VideoPsalm</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songs/lib/importer.py" line="344"/> <location filename="../../openlp/plugins/songs/lib/importer.py" line="344"/>
<source>VideoPsalm</source> <source>VideoPsalm</source>
<translation type="unfinished"/> <translation>VideoPsalm</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songs/lib/importer.py" line="345"/> <location filename="../../openlp/plugins/songs/lib/importer.py" line="345"/>
<source>The VideoPsalm songbooks are normally located in %s</source> <source>The VideoPsalm songbooks are normally located in %s</source>
<translation type="unfinished"/> <translation>Śpiewniki VideoPsalm normalnie zlokalizowane w %s</translation>
</message> </message>
</context> </context>
<context> <context>
@ -9132,7 +9148,7 @@ Do you want to remove the entry from the song?</source>
<message> <message>
<location filename="../../openlp/plugins/songs/lib/importers/lyrix.py" line="109"/> <location filename="../../openlp/plugins/songs/lib/importers/lyrix.py" line="109"/>
<source>Error: %s</source> <source>Error: %s</source>
<translation type="unfinished"/> <translation>Błąd: %s</translation>
</message> </message>
</context> </context>
<context> <context>
@ -9204,12 +9220,12 @@ Do you want to remove the entry from the song?</source>
<message> <message>
<location filename="../../openlp/plugins/songs/lib/mediaitem.py" line="364"/> <location filename="../../openlp/plugins/songs/lib/mediaitem.py" line="364"/>
<source>Are you sure you want to delete the &quot;%d&quot; selected song(s)?</source> <source>Are you sure you want to delete the &quot;%d&quot; selected song(s)?</source>
<translation type="unfinished"/> <translation>Czy na pewno chcesz usunąć &quot;%d&quot; wybranych pieśn(i)?</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songs/lib/mediaitem.py" line="142"/> <location filename="../../openlp/plugins/songs/lib/mediaitem.py" line="142"/>
<source>Search Songbooks...</source> <source>Search Songbooks...</source>
<translation type="unfinished"/> <translation>Szukaj Śpiewników</translation>
</message> </message>
</context> </context>
<context> <context>
@ -9225,7 +9241,7 @@ Do you want to remove the entry from the song?</source>
<message> <message>
<location filename="../../openlp/plugins/songs/lib/importers/openlp.py" line="102"/> <location filename="../../openlp/plugins/songs/lib/importers/openlp.py" line="102"/>
<source>Not a valid OpenLP 2 song database.</source> <source>Not a valid OpenLP 2 song database.</source>
<translation type="unfinished"/> <translation>Niewłaściwa baza pieśni Openlp 2.0.</translation>
</message> </message>
</context> </context>
<context> <context>
@ -9297,7 +9313,7 @@ Do you want to remove the entry from the song?</source>
<message> <message>
<location filename="../../openlp/plugins/songs/forms/songbookdialog.py" line="66"/> <location filename="../../openlp/plugins/songs/forms/songbookdialog.py" line="66"/>
<source>Songbook Maintenance</source> <source>Songbook Maintenance</source>
<translation type="unfinished"/> <translation>Zarządzanie pieśniami</translation>
</message> </message>
</context> </context>
<context> <context>
@ -9605,7 +9621,7 @@ Zapisywanie nazwy użytkownika i hasła jest NIEBEZPIECZNE, twoje hasło jest za
<message> <message>
<location filename="../../openlp/plugins/songs/forms/songselectdialog.py" line="244"/> <location filename="../../openlp/plugins/songs/forms/songselectdialog.py" line="244"/>
<source>Stop</source> <source>Stop</source>
<translation type="unfinished"/> <translation>Stop</translation>
</message> </message>
</context> </context>
<context> <context>
@ -9702,7 +9718,7 @@ Zapisywanie nazwy użytkownika i hasła jest NIEBEZPIECZNE, twoje hasło jest za
<message> <message>
<location filename="../../openlp/plugins/songs/lib/importers/videopsalm.py" line="121"/> <location filename="../../openlp/plugins/songs/lib/importers/videopsalm.py" line="121"/>
<source>Error: %s</source> <source>Error: %s</source>
<translation type="unfinished"/> <translation>Błąd: %s</translation>
</message> </message>
</context> </context>
<context> <context>
@ -9710,7 +9726,7 @@ Zapisywanie nazwy użytkownika i hasła jest NIEBEZPIECZNE, twoje hasło jest za
<message> <message>
<location filename="../../openlp/plugins/songs/lib/importers/wordsofworship.py" line="109"/> <location filename="../../openlp/plugins/songs/lib/importers/wordsofworship.py" line="109"/>
<source>Invalid Words of Worship song file. Missing &quot;%s&quot; header.WoW File\nSong Words</source> <source>Invalid Words of Worship song file. Missing &quot;%s&quot; header.WoW File\nSong Words</source>
<translation type="unfinished"/> <translation>Niewłaściwy plik pieśni Words of Worship. Brakujący &quot;%s&quot; nagłówek. WoW File\nSong Words </translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songs/lib/importers/wordsofworship.py" line="119"/> <location filename="../../openlp/plugins/songs/lib/importers/wordsofworship.py" line="119"/>

View File

@ -2329,7 +2329,7 @@ Deseja continuar adicionando as outras imagens mesmo assim?</translation>
<message> <message>
<location filename="../../openlp/core/ui/mainwindow.py" line="435"/> <location filename="../../openlp/core/ui/mainwindow.py" line="435"/>
<source>&amp;Projector Manager</source> <source>&amp;Projector Manager</source>
<translation>Gerenciador de projetor</translation> <translation>Gerenciador de &amp;Projetor</translation>
</message> </message>
</context> </context>
<context> <context>
@ -7122,7 +7122,7 @@ See http://docs.python.org/library/datetime.html#strftime-strptime-behavior for
<message> <message>
<location filename="../../openlp/core/common/uistrings.py" line="73"/> <location filename="../../openlp/core/common/uistrings.py" line="73"/>
<source>&amp;Delete</source> <source>&amp;Delete</source>
<translation>&amp;Excluir</translation> <translation>E&amp;xcluir</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/core/common/uistrings.py" line="74"/> <location filename="../../openlp/core/common/uistrings.py" line="74"/>
@ -7989,7 +7989,7 @@ Por favor, tente selecionar individualmente.</translation>
<message> <message>
<location filename="../../openlp/plugins/remotes/lib/remotetab.py" line="198"/> <location filename="../../openlp/plugins/remotes/lib/remotetab.py" line="198"/>
<source>iOS App</source> <source>iOS App</source>
<translation>iOS App</translation> <translation>Aplicativo iOS</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/remotes/lib/remotetab.py" line="199"/> <location filename="../../openlp/plugins/remotes/lib/remotetab.py" line="199"/>
@ -8027,12 +8027,12 @@ Por favor, tente selecionar individualmente.</translation>
<message> <message>
<location filename="../../openlp/plugins/songusage/songusageplugin.py" line="103"/> <location filename="../../openlp/plugins/songusage/songusageplugin.py" line="103"/>
<source>Toggle Tracking</source> <source>Toggle Tracking</source>
<translation>Alternar Registro</translation> <translation>Liga/Desliga Registro</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songusage/songusageplugin.py" line="117"/> <location filename="../../openlp/plugins/songusage/songusageplugin.py" line="117"/>
<source>Toggle the tracking of song usage.</source> <source>Toggle the tracking of song usage.</source>
<translation>Alternar o registro de uso das músicas.</translation> <translation>Liga/Desliga o registro de uso das músicas.</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songusage/songusageplugin.py" line="237"/> <location filename="../../openlp/plugins/songusage/songusageplugin.py" line="237"/>
@ -8043,19 +8043,19 @@ Por favor, tente selecionar individualmente.</translation>
<location filename="../../openlp/plugins/songusage/songusageplugin.py" line="248"/> <location filename="../../openlp/plugins/songusage/songusageplugin.py" line="248"/>
<source>SongUsage</source> <source>SongUsage</source>
<comment>name singular</comment> <comment>name singular</comment>
<translation>UsoDaMúsica</translation> <translation>Música Utilizada</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songusage/songusageplugin.py" line="249"/> <location filename="../../openlp/plugins/songusage/songusageplugin.py" line="249"/>
<source>SongUsage</source> <source>SongUsage</source>
<comment>name plural</comment> <comment>name plural</comment>
<translation>UsoDaMúsica</translation> <translation>Músicas Utilizadas</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songusage/songusageplugin.py" line="253"/> <location filename="../../openlp/plugins/songusage/songusageplugin.py" line="253"/>
<source>SongUsage</source> <source>SongUsage</source>
<comment>container title</comment> <comment>container title</comment>
<translation>UsoDaMúsica</translation> <translation>Música Utilizada</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songusage/songusageplugin.py" line="153"/> <location filename="../../openlp/plugins/songusage/songusageplugin.py" line="153"/>
@ -8548,7 +8548,7 @@ A codificação é responsável pela correta representação dos caracteres.</tr
<message> <message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="304"/> <location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="304"/>
<source>Alt&amp;ernate title:</source> <source>Alt&amp;ernate title:</source>
<translation>Título &amp;Alternativo:</translation> <translation>Tít&amp;ulo Alternativo:</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="305"/> <location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="305"/>
@ -8558,12 +8558,12 @@ A codificação é responsável pela correta representação dos caracteres.</tr
<message> <message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="306"/> <location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="306"/>
<source>&amp;Verse order:</source> <source>&amp;Verse order:</source>
<translation>Ordem das &amp;estrofes:</translation> <translation>&amp;Ordem das estrofes:</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="309"/> <location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="309"/>
<source>Ed&amp;it All</source> <source>Ed&amp;it All</source>
<translation>&amp;Editar Todos</translation> <translation>Ed&amp;itar Todos</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="311"/> <location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="311"/>
@ -8573,7 +8573,7 @@ A codificação é responsável pela correta representação dos caracteres.</tr
<message> <message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="314"/> <location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="314"/>
<source>&amp;Add to Song</source> <source>&amp;Add to Song</source>
<translation>&amp;Adicionar à Música</translation> <translation>&amp;Adicionar Autor</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="338"/> <location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="338"/>
@ -8583,7 +8583,7 @@ A codificação é responsável pela correta representação dos caracteres.</tr
<message> <message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="319"/> <location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="319"/>
<source>A&amp;dd to Song</source> <source>A&amp;dd to Song</source>
<translation>A&amp;dicionar uma Música</translation> <translation>Adicionar &amp;Tópico</translation>
</message> </message>
<message> <message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="320"/> <location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="320"/>
@ -9247,7 +9247,7 @@ Você deseja remover a entrada da música?</translation>
<message> <message>
<location filename="../../openlp/plugins/songs/lib/importers/openlp.py" line="102"/> <location filename="../../openlp/plugins/songs/lib/importers/openlp.py" line="102"/>
<source>Not a valid OpenLP 2 song database.</source> <source>Not a valid OpenLP 2 song database.</source>
<translation>Não é uma base de dados de músicas válida do OpenLP 2.</translation> <translation>Não é uma base de dados de músicas válido do OpenLP 2.</translation>
</message> </message>
</context> </context>
<context> <context>

View File

@ -23,13 +23,12 @@
Package to test the openlp.core.ui.projector.networkutils package. Package to test the openlp.core.ui.projector.networkutils package.
""" """
import os
from unittest import TestCase from unittest import TestCase
from openlp.core.common import verify_ip_address, md5_hash, qmd5_hash from openlp.core.common import verify_ip_address, md5_hash, qmd5_hash
from tests.resources.projector.data import TEST_PIN, TEST_SALT, TEST_HASH from tests.resources.projector.data import TEST_PIN, TEST_SALT, TEST_HASH
salt = TEST_SALT salt = TEST_SALT
pin = TEST_PIN pin = TEST_PIN
test_hash = TEST_HASH test_hash = TEST_HASH
@ -148,7 +147,7 @@ class testProjectorUtilities(TestCase):
hash_ = qmd5_hash(salt=salt.encode('ascii'), data=pin.encode('ascii')) hash_ = qmd5_hash(salt=salt.encode('ascii'), data=pin.encode('ascii'))
# THEN: Validate return has is same # THEN: Validate return has is same
self.assertEquals(hash_.decode('ascii'), test_hash, 'Qt-MD5 should have returned a good hash') self.assertEquals(hash_, test_hash.encode('ascii'), 'Qt-MD5 should have returned a good hash')
def test_qmd5_hash_bad(self): def test_qmd5_hash_bad(self):
""" """
@ -158,7 +157,7 @@ class testProjectorUtilities(TestCase):
hash_ = qmd5_hash(salt=pin.encode('ascii'), data=salt.encode('ascii')) hash_ = qmd5_hash(salt=pin.encode('ascii'), data=salt.encode('ascii'))
# THEN: return data is different # THEN: return data is different
self.assertNotEquals(hash_.decode('ascii'), test_hash, 'Qt-MD5 should have returned a bad hash') self.assertNotEquals(hash_, test_hash, 'Qt-MD5 should have returned a bad hash')
def test_md5_non_ascii_string(self): def test_md5_non_ascii_string(self):
""" """

View File

@ -26,13 +26,29 @@ Package to test the openlp.core.lib.projector.pjlink1 package.
from unittest import TestCase from unittest import TestCase
from openlp.core.lib.projector.pjlink1 import PJLink1 from openlp.core.lib.projector.pjlink1 import PJLink1
from openlp.core.lib.projector.constants import E_PARAMETER, ERROR_STRING, S_OFF, S_STANDBY, S_WARMUP, S_ON, \
S_COOLDOWN, PJLINK_POWR_STATUS
from tests.functional import patch from tests.functional import patch
from tests.resources.projector.data import TEST_PIN, TEST_SALT, TEST_CONNECT_AUTHENTICATE from tests.resources.projector.data import TEST_PIN, TEST_SALT, TEST_CONNECT_AUTHENTICATE, TEST_HASH
pjlink_test = PJLink1(name='test', ip='127.0.0.1', pin=TEST_PIN, no_poll=True) pjlink_test = PJLink1(name='test', ip='127.0.0.1', pin=TEST_PIN, no_poll=True)
class DummyTimer(object):
'''
Dummy class to fake timers
'''
def __init__(self, *args, **kwargs):
pass
def start(self, *args, **kwargs):
pass
def stop(self, *args, **kwargs):
pass
class TestPJLink(TestCase): class TestPJLink(TestCase):
""" """
Tests for the PJLink module Tests for the PJLink module
@ -41,13 +57,10 @@ class TestPJLink(TestCase):
@patch.object(pjlink_test, 'send_command') @patch.object(pjlink_test, 'send_command')
@patch.object(pjlink_test, 'waitForReadyRead') @patch.object(pjlink_test, 'waitForReadyRead')
@patch('openlp.core.common.qmd5_hash') @patch('openlp.core.common.qmd5_hash')
def authenticated_connection_call_test(self, def test_authenticated_connection_call(self, mock_qmd5_hash, mock_waitForReadyRead, mock_send_command,
mock_qmd5_hash,
mock_waitForReadyRead,
mock_send_command,
mock_readyRead): mock_readyRead):
""" """
Fix for projector connect with PJLink authentication exception. Ticket 92187. Ticket 92187: Fix for projector connect with PJLink authentication exception.
""" """
# GIVEN: Test object # GIVEN: Test object
pjlink = pjlink_test pjlink = pjlink_test
@ -61,9 +74,23 @@ class TestPJLink(TestCase):
self.assertTrue(mock_qmd5_hash.called_with(TEST_PIN, self.assertTrue(mock_qmd5_hash.called_with(TEST_PIN,
"Connection request should have been called with TEST_PIN")) "Connection request should have been called with TEST_PIN"))
def non_standard_class_reply_test(self): def test_projector_class(self):
""" """
bugfix 1550891 - CLSS request returns non-standard 'Class N' reply Test class version from projector
"""
# GIVEN: Test object
pjlink = pjlink_test
# WHEN: Process class response
pjlink.process_clss('1')
# THEN: Projector class should be set to 1
self.assertEquals(pjlink.pjlink_class, '1',
'Projector should have returned class=1')
def test_non_standard_class_reply(self):
"""
Bugfix 1550891: CLSS request returns non-standard 'Class N' reply
""" """
# GIVEN: Test object # GIVEN: Test object
pjlink = pjlink_test pjlink = pjlink_test
@ -88,3 +115,284 @@ class TestPJLink(TestCase):
# THEN: Projector class should be set to 1 # THEN: Projector class should be set to 1
self.assertEquals(pjlink.pjlink_class, '1', self.assertEquals(pjlink.pjlink_class, '1',
'Projector should have returned class=1') 'Projector should have returned class=1')
@patch.object(pjlink_test, 'change_status')
def test_status_change(self, mock_change_status):
"""
Test process_command call with ERR2 (Parameter) status
"""
# GIVEN: Test object
pjlink = pjlink_test
# WHEN: process_command is called with "ERR2" status from projector
pjlink.process_command('POWR', 'ERR2')
# THEN: change_status should have called change_status with E_UNDEFINED
# as first parameter
mock_change_status.called_with(E_PARAMETER,
'change_status should have been called with "{}"'.format(
ERROR_STRING[E_PARAMETER]))
@patch.object(pjlink_test, 'process_inpt')
def test_projector_return_ok(self, mock_process_inpt):
"""
Test projector calls process_inpt command when process_command is called with INPT option
"""
# GIVEN: Test object
pjlink = pjlink_test
# WHEN: process_command is called with INST command and 31 input:
pjlink.process_command('INPT', '31')
# THEN: process_inpt method should have been called with 31
mock_process_inpt.called_with('31',
"process_inpt should have been called with 31")
@patch.object(pjlink_test, 'projectorReceivedData')
def test_projector_process_lamp(self, mock_projectorReceivedData):
"""
Test status lamp on/off and hours
"""
# GIVEN: Test object
pjlink = pjlink_test
# WHEN: Call process_command with lamp data
pjlink.process_command('LAMP', '22222 1')
# THEN: Lamp should have been set with status=ON and hours=22222
self.assertEquals(pjlink.lamp[0]['On'], True,
'Lamp power status should have been set to TRUE')
self.assertEquals(pjlink.lamp[0]['Hours'], 22222,
'Lamp hours should have been set to 22222')
@patch.object(pjlink_test, 'projectorReceivedData')
def test_projector_process_multiple_lamp(self, mock_projectorReceivedData):
"""
Test status multiple lamp on/off and hours
"""
# GIVEN: Test object
pjlink = pjlink_test
# WHEN: Call process_command with lamp data
pjlink.process_command('LAMP', '11111 1 22222 0 33333 1')
# THEN: Lamp should have been set with proper lamp status
self.assertEquals(len(pjlink.lamp), 3,
'Projector should have 3 lamps specified')
self.assertEquals(pjlink.lamp[0]['On'], True,
'Lamp 1 power status should have been set to TRUE')
self.assertEquals(pjlink.lamp[0]['Hours'], 11111,
'Lamp 1 hours should have been set to 11111')
self.assertEquals(pjlink.lamp[1]['On'], False,
'Lamp 2 power status should have been set to FALSE')
self.assertEquals(pjlink.lamp[1]['Hours'], 22222,
'Lamp 2 hours should have been set to 22222')
self.assertEquals(pjlink.lamp[2]['On'], True,
'Lamp 3 power status should have been set to TRUE')
self.assertEquals(pjlink.lamp[2]['Hours'], 33333,
'Lamp 3 hours should have been set to 33333')
@patch.object(pjlink_test, 'projectorReceivedData')
def test_projector_process_power_on(self, mock_projectorReceivedData):
"""
Test status power to ON
"""
# GIVEN: Test object and preset
pjlink = pjlink_test
pjlink.power = S_STANDBY
# WHEN: Call process_command with turn power on command
pjlink.process_command('POWR', PJLINK_POWR_STATUS[S_ON])
# THEN: Power should be set to ON
self.assertEquals(pjlink.power, S_ON, 'Power should have been set to ON')
@patch.object(pjlink_test, 'projectorReceivedData')
def test_projector_process_power_off(self, mock_projectorReceivedData):
"""
Test status power to STANDBY
"""
# GIVEN: Test object and preset
pjlink = pjlink_test
pjlink.power = S_ON
# WHEN: Call process_command with turn power on command
pjlink.process_command('POWR', PJLINK_POWR_STATUS[S_STANDBY])
# THEN: Power should be set to STANDBY
self.assertEquals(pjlink.power, S_STANDBY, 'Power should have been set to STANDBY')
@patch.object(pjlink_test, 'projectorUpdateIcons')
def test_projector_process_avmt_closed_unmuted(self, mock_projectorReceivedData):
"""
Test avmt status shutter closed and audio muted
"""
# GIVEN: Test object
pjlink = pjlink_test
pjlink.shutter = False
pjlink.mute = True
# WHEN: Called with setting shutter closed and mute off
pjlink.process_avmt('11')
# THEN: Shutter should be True and mute should be False
self.assertTrue(pjlink.shutter, 'Shutter should have been set to closed')
self.assertFalse(pjlink.mute, 'Audio should be off')
@patch.object(pjlink_test, 'projectorUpdateIcons')
def test_projector_process_avmt_open_muted(self, mock_projectorReceivedData):
"""
Test avmt status shutter open and mute on
"""
# GIVEN: Test object
pjlink = pjlink_test
pjlink.shutter = True
pjlink.mute = False
# WHEN: Called with setting shutter closed and mute on
pjlink.process_avmt('21')
# THEN: Shutter should be closed and mute should be True
self.assertFalse(pjlink.shutter, 'Shutter should have been set to closed')
self.assertTrue(pjlink.mute, 'Audio should be off')
@patch.object(pjlink_test, 'projectorUpdateIcons')
def test_projector_process_avmt_open_unmuted(self, mock_projectorReceivedData):
"""
Test avmt status shutter open and mute off off
"""
# GIVEN: Test object
pjlink = pjlink_test
pjlink.shutter = True
pjlink.mute = True
# WHEN: Called with setting shutter to closed and mute on
pjlink.process_avmt('30')
# THEN: Shutter should be closed and mute should be True
self.assertFalse(pjlink.shutter, 'Shutter should have been set to open')
self.assertFalse(pjlink.mute, 'Audio should be on')
@patch.object(pjlink_test, 'projectorUpdateIcons')
def test_projector_process_avmt_closed_muted(self, mock_projectorReceivedData):
"""
Test avmt status shutter closed and mute off
"""
# GIVEN: Test object
pjlink = pjlink_test
pjlink.shutter = False
pjlink.mute = False
# WHEN: Called with setting shutter to closed and mute on
pjlink.process_avmt('31')
# THEN: Shutter should be closed and mute should be True
self.assertTrue(pjlink.shutter, 'Shutter should have been set to closed')
self.assertTrue(pjlink.mute, 'Audio should be on')
def test_projector_process_input(self):
"""
Test input source status shows current input
"""
# GIVEN: Test object
pjlink = pjlink_test
pjlink.source = '0'
# WHEN: Called with input source
pjlink.process_inpt('1')
# THEN: Input selected should reflect current input
self.assertEquals(pjlink.source, '1', 'Input source should be set to "1"')
def test_projector_reset_information(self):
"""
Test reset_information() resets all information and stops timers
"""
# GIVEN: Test object and test data
pjlink = pjlink_test
pjlink.power = S_ON
pjlink.pjlink_name = 'OPENLPTEST'
pjlink.manufacturer = 'PJLINK'
pjlink.model = '1'
pjlink.shutter = True
pjlink.mute = True
pjlink.lamp = True
pjlink.fan = True
pjlink.source_available = True
pjlink.other_info = 'ANOTHER TEST'
pjlink.send_queue = True
pjlink.send_busy = True
pjlink.timer = DummyTimer()
pjlink.socket_timer = DummyTimer()
# WHEN: reset_information() is called
with patch.object(pjlink.timer, 'stop') as mock_timer:
with patch.object(pjlink.socket_timer, 'stop') as mock_socket_timer:
pjlink.reset_information()
# THEN: All information should be reset and timers stopped
self.assertEquals(pjlink.power, S_OFF, 'Projector power should be OFF')
self.assertIsNone(pjlink.pjlink_name, 'Projector pjlink_name should be None')
self.assertIsNone(pjlink.manufacturer, 'Projector manufacturer should be None')
self.assertIsNone(pjlink.model, 'Projector model should be None')
self.assertIsNone(pjlink.shutter, 'Projector shutter should be None')
self.assertIsNone(pjlink.mute, 'Projector shuttter should be None')
self.assertIsNone(pjlink.lamp, 'Projector lamp should be None')
self.assertIsNone(pjlink.fan, 'Projector fan should be None')
self.assertIsNone(pjlink.source_available, 'Projector source_available should be None')
self.assertIsNone(pjlink.source, 'Projector source should be None')
self.assertIsNone(pjlink.other_info, 'Projector other_info should be None')
self.assertEquals(pjlink.send_queue, [], 'Projector send_queue should be an empty list')
self.assertFalse(pjlink.send_busy, 'Projector send_busy should be False')
self.assertTrue(mock_timer.called, 'Projector timer.stop() should have been called')
self.assertTrue(mock_socket_timer.called, 'Projector socket_timer.stop() should have been called')
@patch.object(pjlink_test, 'send_command')
@patch.object(pjlink_test, 'waitForReadyRead')
@patch.object(pjlink_test, 'projectorAuthentication')
@patch.object(pjlink_test, 'timer')
@patch.object(pjlink_test, 'socket_timer')
def test_bug_1593882_no_pin_authenticated_connection(self, mock_socket_timer,
mock_timer,
mock_authentication,
mock_ready_read,
mock_send_command):
"""
Test bug 1593882 no pin and authenticated request exception
"""
# GIVEN: Test object and mocks
pjlink = pjlink_test
pjlink.pin = None
mock_ready_read.return_value = True
# WHEN: call with authentication request and pin not set
pjlink.check_login(data=TEST_CONNECT_AUTHENTICATE)
# THEN: No Authentication signal should have been sent
mock_authentication.emit.assert_called_with(pjlink.name)
@patch.object(pjlink_test, 'waitForReadyRead')
@patch.object(pjlink_test, 'state')
@patch.object(pjlink_test, '_send_command')
@patch.object(pjlink_test, 'timer')
@patch.object(pjlink_test, 'socket_timer')
def test_bug_1593883_pjlink_authentication(self, mock_socket_timer,
mock_timer,
mock_send_command,
mock_state,
mock_waitForReadyRead):
"""
Test bugfix 1593883 pjlink authentication
"""
# GIVEN: Test object and data
pjlink = pjlink_test
pjlink.pin = TEST_PIN
mock_state.return_value = pjlink.ConnectedState
mock_waitForReadyRead.return_value = True
# WHEN: Athenticated connection is called
pjlink.check_login(data=TEST_CONNECT_AUTHENTICATE)
# THEN: send_command should have the proper authentication
self.assertEquals("{test}".format(test=mock_send_command.call_args),
"call(data='{hash}%1CLSS ?\\r')".format(hash=TEST_HASH))

View File

@ -22,13 +22,13 @@
""" """
This module contains tests for the OpenSong song importer. This module contains tests for the OpenSong song importer.
""" """
import os import os
from unittest import TestCase from unittest import TestCase
from tests.helpers.songfileimport import SongImportTestHelper
from openlp.plugins.songs.lib.importers.opensong import OpenSongImport from openlp.plugins.songs.lib.importers.opensong import OpenSongImport
from openlp.core.common import Registry from openlp.core.common import Registry
from tests.helpers.songfileimport import SongImportTestHelper
from tests.functional import patch, MagicMock from tests.functional import patch, MagicMock
TEST_PATH = os.path.abspath( TEST_PATH = os.path.abspath(
@ -54,6 +54,8 @@ class TestOpenSongFileImport(SongImportTestHelper):
self.load_external_result_data(os.path.join(TEST_PATH, 'One, Two, Three, Four, Five.json'))) self.load_external_result_data(os.path.join(TEST_PATH, 'One, Two, Three, Four, Five.json')))
self.file_import([os.path.join(TEST_PATH, 'Amazing Grace2')], self.file_import([os.path.join(TEST_PATH, 'Amazing Grace2')],
self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json'))) self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))
self.file_import([os.path.join(TEST_PATH, 'Amazing Grace with bad CCLI')],
self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace without CCLI.json')))
class TestOpenSongImport(TestCase): class TestOpenSongImport(TestCase):

View File

@ -52,6 +52,8 @@ class TestSongShowPlusFileImport(SongImportTestHelper):
self.load_external_result_data(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.json'))) self.load_external_result_data(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.json')))
self.file_import([os.path.join(TEST_PATH, 'a mighty fortress is our god.sbsong')], self.file_import([os.path.join(TEST_PATH, 'a mighty fortress is our god.sbsong')],
self.load_external_result_data(os.path.join(TEST_PATH, 'a mighty fortress is our god.json'))) self.load_external_result_data(os.path.join(TEST_PATH, 'a mighty fortress is our god.json')))
self.file_import([os.path.join(TEST_PATH, 'cleanse-me.sbsong')],
self.load_external_result_data(os.path.join(TEST_PATH, 'cleanse-me.json')))
class TestSongShowPlusImport(TestCase): class TestSongShowPlusImport(TestCase):

View File

@ -46,3 +46,5 @@ class TestVideoPsalmFileImport(SongImportTestHelper):
""" """
self.file_import(os.path.join(TEST_PATH, 'videopsalm-as-safe-a-stronghold.json'), self.file_import(os.path.join(TEST_PATH, 'videopsalm-as-safe-a-stronghold.json'),
self.load_external_result_data(os.path.join(TEST_PATH, 'as-safe-a-stronghold.json'))) self.load_external_result_data(os.path.join(TEST_PATH, 'as-safe-a-stronghold.json')))
self.file_import(os.path.join(TEST_PATH, 'videopsalm-as-safe-a-stronghold2.json'),
self.load_external_result_data(os.path.join(TEST_PATH, 'as-safe-a-stronghold2.json')))

View File

@ -29,6 +29,7 @@ from unittest import TestCase
from openlp.plugins.songs.lib.importers.opensong import OpenSongImport from openlp.plugins.songs.lib.importers.opensong import OpenSongImport
from openlp.core.common import Registry from openlp.core.common import Registry
from tests.functional import patch, MagicMock, call from tests.functional import patch, MagicMock, call
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -36,7 +37,7 @@ log = logging.getLogger(__name__)
class SongImportTestHelper(TestCase): class SongImportTestHelper(TestCase):
""" """
This class is designed to be a helper class to reduce repition when testing the import of song files. This class is designed to be a helper class to reduce repetition when testing the import of song files.
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(SongImportTestHelper, self).__init__(*args, **kwargs) super(SongImportTestHelper, self).__init__(*args, **kwargs)

View File

@ -53,4 +53,4 @@
<key_line></key_line> <key_line></key_line>
<time_sig></time_sig> <time_sig></time_sig>
<style index="default_style"></style> <style index="default_style"></style>
</song> </song>

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<song>
<title>Amazing Grace (Demonstration)</title>
<author>John Newton, Edwin Excell &amp; John P. Rees</author>
<copyright>Public Domain </copyright>
<presentation>V1 V2 V3 V4 V5</presentation>
<capo print="false"></capo>
<tempo></tempo>
<ccli>GE</ccli>
<theme>God: Assurance/Grace/Salvation</theme>
<alttheme>Worship: Praise</alttheme>
<user1> </user1>
<user2> </user2>
<user3> </user3>
<lyrics>[V]
;Test the chords format
;Chords beging with .
;Verses begin with their verse number
;Link words with _
;Comments begin with ;
. D D7 G D
1A______ma________zing grace! How sweet the sound!
2'Twas grace that taught my heart to fear,
3The Lord has pro____mised good to me,
4Thro' ma________ny dan____gers, toils and snares
5When we've been there ten thou__sand years,
. Bm E A A7
1That saved a wretch like me!
2And grace my fears re___lieved.
3His Word my hope se___cures.
4I have al___rea____dy come.
5Bright shi___ning as the sun,
. D D7 G D
1I once was lost, but now am found;
2How pre___cious did that grace ap____pear,
3He will my shield and por___tion be
4'Tis grace that brought me safe thus far,
5We've no less days to sing God's praise,
. Bm A G D
1Was blind, but now I see.
2The hour I first be_lieved.
3As long as life en_dures.
4And grace will lead me home.
5Than when we first be_gun.
</lyrics>
<hymn_number>Demonstration Songs 0</hymn_number>
<key></key>
<aka></aka>
<key_line></key_line>
<time_sig></time_sig>
<style index="default_style"></style>
</song>

View File

@ -0,0 +1,42 @@
{
"authors": [
"John Newton",
"Edwin Excell",
"John P. Rees"
],
"ccli_number": null,
"comments": "\n\n\n",
"copyright": "Public Domain ",
"song_book_name": "Demonstration Songs",
"song_number": 0,
"title": "Amazing Grace (Demonstration)",
"topics": [
"Assurance",
"Grace",
"Praise",
"Salvation"
],
"verse_order_list": [],
"verses": [
[
"Amazing grace! How sweet the sound!\nThat saved a wretch like me!\nI once was lost, but now am found;\nWas blind, but now I see.",
"v1"
],
[
"'Twas grace that taught my heart to fear,\nAnd grace my fears relieved.\nHow precious did that grace appear,\nThe hour I first believed.",
"v2"
],
[
"The Lord has promised good to me,\nHis Word my hope secures.\nHe will my shield and portion be\nAs long as life endures.",
"v3"
],
[
"Thro' many dangers, toils and snares\nI have already come.\n'Tis grace that brought me safe thus far,\nAnd grace will lead me home.",
"v4"
],
[
"When we've been there ten thousand years,\nBright shining as the sun,\nWe've no less days to sing God's praise,\nThan when we first begun.",
"v5"
]
]
}

View File

@ -0,0 +1,38 @@
{
"authors": [
"J. Edwin Orr"
],
"ccli_number": 56307,
"comments": "",
"copyright": "Public Domain ",
"song_book_name": "",
"song_number": 438,
"title": "Cleanse Me [438]",
"topics": [
"Cleansing",
"Communion",
"Consecration",
"Holiness",
"Holy Spirit",
"Revival"
],
"verse_order_list": [],
"verses": [
[
"Search me, O God,\r\nAnd know my heart today;\r\nTry me, O Savior,\r\nKnow my thoughts, I pray.\r\nSee if there be\r\nSome wicked way in me;\r\nCleanse me from every sin\r\nAnd set me free.",
"v1"
],
[
"I praise Thee, Lord,\r\nFor cleansing me from sin;\r\nFulfill Thy Word,\r\nAnd make me pure within.\r\nFill me with fire\r\nWhere once I burned with shame;\r\nGrant my desire\r\nTo magnify Thy name.",
"v2"
],
[
"Lord, take my life,\r\nAnd make it wholly Thine;\r\nFill my poor heart\r\nWith Thy great love divine.\r\nTake all my will,\r\nMy passion, self and pride;\r\nI now surrender, Lord\r\nIn me abide.",
"v3"
],
[
"O Holy Ghost,\r\nRevival comes from Thee;\r\nSend a revival,\r\nStart the work in me.\r\nThy Word declares\r\nThou wilt supply our need;\r\nFor blessings now,\r\nO Lord, I humbly plead.",
"v4"
]
]
}

Binary file not shown.

View File

@ -0,0 +1,35 @@
{
"authors": [
["Martin Luther", "words"],
["Unknown", "music"]
],
"ccli_number": "12345",
"comments": "This is\nthe first comment\nThis is\nthe second comment\nThis is\nthe third comment\n",
"copyright": "Public Domain",
"song_book_name": "SongBook1",
"song_number": 0,
"title": "A Safe Stronghold Our God is Still",
"topics": [
"tema1",
"tema2"
],
"verse_order_list": [],
"verses": [
[
"As safe a stronghold our God is still,\nA trusty shield and weapon;\nHell help us clear from all the ill\nThat hath us now oertaken.\nThe ancient prince of hell\nHath risen with purpose fell;\nStrong mail of craft and power\nHe weareth in this hour;\nOn earth is not His fellow.",
"v"
],
[
"With \"force\" of arms we nothing can,\nFull soon were we down-ridden;\nBut for us fights \\ the proper Man,\nWhom God Himself hath bidden.\nAsk ye: Who is this same?\nChrist Jesus is His name,\nThe Lord Sabaoths Son;\nHe, and no other one,\nShall conquer in the battle.",
"v"
],
[
"And were this world all devils oer,\nAnd watching to devour us,\nWe lay it not to heart so sore;\nNot they can overpower us.\nAnd let the prince of ill\nLook grim as eer he will,\nHe harms us not a whit;\nFor why? his doom is writ;\nA word shall quickly slay him.",
"v"
],
[
"Gods word, for all their craft and force,\nOne moment will not linger,\nBut, spite of hell, shall have its course;\nTis written by His finger.\nAnd though they take our life,\nGoods, honour, children, wife,\nYet is their profit small:\nThese things shall vanish all;\nThe city of God remaineth.",
"v"
]
]
}

View File

@ -0,0 +1,47 @@
{Abbreviation:"SB1",Copyright:"Public domain",Songs:[{ID:3,Composer:"Unknown",Author:"Martin Luther",Copyright:"Public
Domain",Theme:"tema1
tema2",CCLI:"12345",Alias:"A safe stronghold",Memo1:"This is
the first comment
",Memo2:"This is
the second comment
",Memo3:"This is
the third comment
",Reference:"reference",Guid:"jtCkrJdPIUOmECjaQylg/g",Verses:[{
Text:"As safe a stronghold our God is still,
A trusty shield and weapon;
Hell help us clear from all the ill
That hath us now oertaken.
The ancient prince of hell
Hath risen with purpose fell;
Strong mail of craft and power
He weareth in this hour;
On earth is not His fellow."},{ID:2,
Text:"With \"force\" of arms we nothing can,
Full soon were we down-ridden;
But for us fights \\ the proper Man,
Whom God Himself hath bidden.
Ask ye: Who is this same?
Christ Jesus is His name,
The Lord Sabaoths Son;
He, and no other one,
Shall conquer in the battle."},{ID:3,
Text:"And were this world all devils oer,
And watching to devour us,
We lay it not to heart so sore;
Not they can overpower us.
And let the prince of ill
Look grim as eer he will,
He harms us not a whit;
For why? his doom is writ;
A word shall quickly slay him."},{ID:4,
Text:"Gods word, for all their craft and force,
One moment will not linger,
But, spite of hell, shall have its course;
Tis written by His finger.
And though they take our life,
Goods, honour, children, wife,
Yet is their profit small:
These things shall vanish all;
The city of God remaineth."}],AudioFile:"282.mp3",IsAudioFileEnabled:1,
Text:"A Safe Stronghold Our God is Still"}],Guid:"khiHU2blX0Kb41dGdbDLhA",VersionDate:"20121012000000",
Text:"SongBook1"}