2009-09-08 19:58:05 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
2012-12-29 09:35:24 +00:00
|
|
|
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
2008-10-23 19:49:13 +00:00
|
|
|
|
2009-09-08 19:58:05 +00:00
|
|
|
###############################################################################
|
|
|
|
# OpenLP - Open Source Lyrics Projection #
|
|
|
|
# --------------------------------------------------------------------------- #
|
2017-12-29 09:15:48 +00:00
|
|
|
# Copyright (c) 2008-2018 OpenLP Developers #
|
2009-09-08 19:58:05 +00:00
|
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
# This program is free software; you can redistribute it and/or modify it #
|
|
|
|
# under the terms of the GNU General Public License as published by the Free #
|
|
|
|
# Software Foundation; version 2 of the License. #
|
|
|
|
# #
|
|
|
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
|
|
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
|
|
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
|
|
|
# more details. #
|
|
|
|
# #
|
|
|
|
# You should have received a copy of the GNU General Public License along #
|
|
|
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
|
|
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
|
|
|
###############################################################################
|
2013-01-16 21:03:01 +00:00
|
|
|
"""
|
2013-10-13 13:51:13 +00:00
|
|
|
The :mod:`common` module contains most of the components and libraries that make
|
|
|
|
OpenLP work.
|
2013-01-16 21:03:01 +00:00
|
|
|
"""
|
2014-10-06 19:10:03 +00:00
|
|
|
import hashlib
|
2017-05-14 10:11:10 +00:00
|
|
|
import importlib
|
2013-10-13 13:51:13 +00:00
|
|
|
import logging
|
2016-04-01 16:56:54 +00:00
|
|
|
import os
|
|
|
|
import re
|
2013-10-13 17:02:12 +00:00
|
|
|
import sys
|
2013-12-13 19:44:17 +00:00
|
|
|
import traceback
|
2014-10-06 19:10:03 +00:00
|
|
|
from ipaddress import IPv4Address, IPv6Address, AddressValueError
|
2016-04-04 20:14:04 +00:00
|
|
|
from shutil import which
|
2016-04-16 13:51:42 +00:00
|
|
|
from subprocess import check_output, CalledProcessError, STDOUT
|
2008-10-23 19:49:13 +00:00
|
|
|
|
2017-10-07 07:05:07 +00:00
|
|
|
from PyQt5 import QtGui
|
2015-11-07 00:49:40 +00:00
|
|
|
from PyQt5.QtCore import QCryptographicHash as QHash
|
2018-02-11 11:42:13 +00:00
|
|
|
from PyQt5.QtNetwork import QAbstractSocket, QHostAddress, QNetworkInterface
|
2017-12-28 08:27:44 +00:00
|
|
|
from chardet.universaldetector import UniversalDetector
|
2013-10-13 20:36:42 +00:00
|
|
|
|
2014-04-12 20:19:22 +00:00
|
|
|
log = logging.getLogger(__name__ + '.__init__')
|
2013-02-01 19:58:18 +00:00
|
|
|
|
2013-10-13 13:51:13 +00:00
|
|
|
|
2013-12-13 19:44:17 +00:00
|
|
|
FIRST_CAMEL_REGEX = re.compile('(.)([A-Z][a-z]+)')
|
|
|
|
SECOND_CAMEL_REGEX = re.compile('([a-z0-9])([A-Z])')
|
2017-10-28 10:04:09 +00:00
|
|
|
CONTROL_CHARS = re.compile(r'[\x00-\x1F\x7F-\x9F]')
|
|
|
|
INVALID_FILE_CHARS = re.compile(r'[\\/:\*\?"<>\|\+\[\]%]')
|
2016-04-05 17:30:20 +00:00
|
|
|
IMAGES_FILTER = None
|
2017-10-28 10:04:09 +00:00
|
|
|
REPLACMENT_CHARS_MAP = str.maketrans({'\u2018': '\'', '\u2019': '\'', '\u201c': '"', '\u201d': '"', '\u2026': '...',
|
|
|
|
'\u2013': '-', '\u2014': '-', '\v': '\n\n', '\f': '\n\n'})
|
|
|
|
NEW_LINE_REGEX = re.compile(r' ?(\r\n?|\n) ?')
|
|
|
|
WHITESPACE_REGEX = re.compile(r'[ \t]+')
|
2013-12-13 19:44:17 +00:00
|
|
|
|
2018-02-11 11:42:13 +00:00
|
|
|
|
2018-02-11 21:52:04 +00:00
|
|
|
def get_local_ip4():
|
|
|
|
"""
|
|
|
|
Creates a dictionary of local IPv4 interfaces on local machine.
|
|
|
|
If no active interfaces available, returns a dict of localhost IPv4 information
|
|
|
|
|
|
|
|
:returns: Dict of interfaces
|
|
|
|
"""
|
|
|
|
# Get the local IPv4 active address(es) that are NOT localhost (lo or '127.0.0.1')
|
|
|
|
log.debug('Getting local IPv4 interface(es) information')
|
|
|
|
MY_IP4 = {}
|
|
|
|
for iface in QNetworkInterface.allInterfaces():
|
|
|
|
if not iface.isValid() or not (iface.flags() & (QNetworkInterface.IsUp | QNetworkInterface.IsRunning)):
|
|
|
|
continue
|
|
|
|
for address in iface.addressEntries():
|
|
|
|
ip = address.ip()
|
|
|
|
# NOTE: Next line will skip if interface is localhost - keep for now until we decide about it later
|
|
|
|
# if (ip.protocol() == QAbstractSocket.IPv4Protocol) and (ip != QHostAddress.LocalHost):
|
|
|
|
if (ip.protocol() == QAbstractSocket.IPv4Protocol):
|
|
|
|
MY_IP4[iface.name()] = {'ip': ip.toString(),
|
|
|
|
'broadcast': address.broadcast().toString(),
|
|
|
|
'netmask': address.netmask().toString(),
|
|
|
|
'prefix': address.prefixLength(),
|
|
|
|
'localnet': QHostAddress(address.netmask().toIPv4Address() &
|
|
|
|
ip.toIPv4Address()).toString()
|
|
|
|
}
|
|
|
|
log.debug('Adding {iface} to active list'.format(iface=iface.name()))
|
|
|
|
if len(MY_IP4) == 1:
|
|
|
|
if 'lo' in MY_IP4:
|
|
|
|
# No active interfaces - so leave localhost in there
|
|
|
|
log.warning('No active IPv4 interfaces found except localhost')
|
|
|
|
else:
|
|
|
|
# Since we have a valid IP4 interface, remove localhost
|
|
|
|
log.debug('Found at least one IPv4 interface, removing localhost')
|
|
|
|
MY_IP4.pop('lo')
|
|
|
|
|
|
|
|
return MY_IP4
|
2018-02-11 11:42:13 +00:00
|
|
|
|
2013-12-13 19:44:17 +00:00
|
|
|
|
|
|
|
def trace_error_handler(logger):
|
|
|
|
"""
|
|
|
|
Log the calling path of an exception
|
|
|
|
|
2014-03-17 19:05:55 +00:00
|
|
|
:param logger: logger to use so traceback is logged to correct class
|
2013-12-13 19:44:17 +00:00
|
|
|
"""
|
2014-04-01 17:32:19 +00:00
|
|
|
log_string = "OpenLP Error trace"
|
2013-12-13 19:44:17 +00:00
|
|
|
for tb in traceback.extract_stack():
|
2017-10-07 07:05:07 +00:00
|
|
|
log_string += '\n File {file} at line {line} \n\t called {data}'.format(file=tb[0], line=tb[1], data=tb[3])
|
2014-04-01 17:32:19 +00:00
|
|
|
logger.error(log_string)
|
2013-12-13 19:44:17 +00:00
|
|
|
|
2013-12-15 16:50:09 +00:00
|
|
|
|
2017-05-14 10:11:10 +00:00
|
|
|
def extension_loader(glob_pattern, excluded_files=[]):
|
|
|
|
"""
|
|
|
|
A utility function to find and load OpenLP extensions, such as plugins, presentation and media controllers and
|
|
|
|
importers.
|
|
|
|
|
2017-08-12 17:45:56 +00:00
|
|
|
:param str glob_pattern: A glob pattern used to find the extension(s) to be imported. Should be relative to the
|
|
|
|
application directory. i.e. plugins/*/*plugin.py
|
|
|
|
:param list[str] excluded_files: A list of file names to exclude that the glob pattern may find.
|
2017-05-14 10:11:10 +00:00
|
|
|
:rtype: None
|
|
|
|
"""
|
2017-10-07 07:05:07 +00:00
|
|
|
from openlp.core.common.applocation import AppLocation
|
2017-08-12 17:45:56 +00:00
|
|
|
app_dir = AppLocation.get_directory(AppLocation.AppDir)
|
|
|
|
for extension_path in app_dir.glob(glob_pattern):
|
|
|
|
extension_path = extension_path.relative_to(app_dir)
|
2017-05-15 10:09:59 +00:00
|
|
|
if extension_path.name in excluded_files:
|
2017-05-14 10:11:10 +00:00
|
|
|
continue
|
2017-12-17 15:25:54 +00:00
|
|
|
log.debug('Attempting to import %s', extension_path)
|
2017-05-15 10:09:59 +00:00
|
|
|
module_name = path_to_module(extension_path)
|
2017-05-14 10:11:10 +00:00
|
|
|
try:
|
2017-05-15 10:09:59 +00:00
|
|
|
importlib.import_module(module_name)
|
2017-05-14 10:11:10 +00:00
|
|
|
except (ImportError, OSError):
|
|
|
|
# On some platforms importing vlc.py might cause OSError exceptions. (e.g. Mac OS X)
|
|
|
|
log.warning('Failed to import {module_name} on path {extension_path}'
|
2017-08-12 17:45:56 +00:00
|
|
|
.format(module_name=module_name, extension_path=extension_path))
|
2017-05-15 10:09:59 +00:00
|
|
|
|
2017-05-15 10:24:28 +00:00
|
|
|
|
2017-05-15 10:09:59 +00:00
|
|
|
def path_to_module(path):
|
|
|
|
"""
|
|
|
|
Convert a path to a module name (i.e openlp.core.common)
|
2017-05-15 10:24:28 +00:00
|
|
|
|
2017-08-25 20:03:25 +00:00
|
|
|
:param openlp.core.common.path.Path path: The path to convert to a module name.
|
2017-05-15 10:09:59 +00:00
|
|
|
:return: The module name.
|
|
|
|
:rtype: str
|
|
|
|
"""
|
|
|
|
module_path = path.with_suffix('')
|
2017-08-12 17:45:56 +00:00
|
|
|
return 'openlp.' + '.'.join(module_path.parts)
|
2017-05-14 10:11:10 +00:00
|
|
|
|
|
|
|
|
2013-10-13 17:02:12 +00:00
|
|
|
def get_frozen_path(frozen_option, non_frozen_option):
|
|
|
|
"""
|
|
|
|
Return a path based on the system status.
|
2014-04-01 23:49:58 +00:00
|
|
|
|
|
|
|
:param frozen_option:
|
|
|
|
:param non_frozen_option:
|
2013-10-13 17:02:12 +00:00
|
|
|
"""
|
|
|
|
if hasattr(sys, 'frozen') and sys.frozen == 1:
|
|
|
|
return frozen_option
|
|
|
|
return non_frozen_option
|
|
|
|
|
2013-10-13 20:36:42 +00:00
|
|
|
|
|
|
|
class ThemeLevel(object):
|
|
|
|
"""
|
|
|
|
Provides an enumeration for the level a theme applies to
|
|
|
|
"""
|
|
|
|
Global = 1
|
|
|
|
Service = 2
|
|
|
|
Song = 3
|
|
|
|
|
|
|
|
|
|
|
|
class SlideLimits(object):
|
|
|
|
"""
|
|
|
|
Provides an enumeration for behaviour of OpenLP at the end limits of each service item when pressing the up/down
|
|
|
|
arrow keys
|
|
|
|
"""
|
|
|
|
End = 1
|
|
|
|
Wrap = 2
|
|
|
|
Next = 3
|
|
|
|
|
2013-12-13 19:44:17 +00:00
|
|
|
|
|
|
|
def de_hump(name):
|
|
|
|
"""
|
|
|
|
Change any Camel Case string to python string
|
|
|
|
"""
|
|
|
|
sub_name = FIRST_CAMEL_REGEX.sub(r'\1_\2', name)
|
|
|
|
return SECOND_CAMEL_REGEX.sub(r'\1_\2', sub_name).lower()
|
|
|
|
|
2014-08-27 23:18:06 +00:00
|
|
|
|
|
|
|
def is_win():
|
|
|
|
"""
|
|
|
|
Returns true if running on a system with a nt kernel e.g. Windows, Wine
|
|
|
|
|
|
|
|
:return: True if system is running a nt kernel false otherwise
|
|
|
|
"""
|
|
|
|
return os.name.startswith('nt')
|
|
|
|
|
|
|
|
|
|
|
|
def is_macosx():
|
|
|
|
"""
|
|
|
|
Returns true if running on a system with a darwin kernel e.g. Mac OS X
|
|
|
|
|
|
|
|
:return: True if system is running a darwin kernel false otherwise
|
|
|
|
"""
|
|
|
|
return sys.platform.startswith('darwin')
|
|
|
|
|
|
|
|
|
|
|
|
def is_linux():
|
|
|
|
"""
|
|
|
|
Returns true if running on a system with a linux kernel e.g. Ubuntu, Debian, etc
|
|
|
|
|
|
|
|
:return: True if system is running a linux kernel false otherwise
|
|
|
|
"""
|
|
|
|
return sys.platform.startswith('linux')
|
|
|
|
|
2014-10-06 19:10:03 +00:00
|
|
|
|
|
|
|
def verify_ipv4(addr):
|
|
|
|
"""
|
|
|
|
Validate an IPv4 address
|
|
|
|
|
|
|
|
:param addr: Address to validate
|
|
|
|
:returns: bool
|
|
|
|
"""
|
|
|
|
try:
|
2017-10-07 07:05:07 +00:00
|
|
|
IPv4Address(addr)
|
2014-10-06 19:10:03 +00:00
|
|
|
return True
|
|
|
|
except AddressValueError:
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def verify_ipv6(addr):
|
|
|
|
"""
|
|
|
|
Validate an IPv6 address
|
|
|
|
|
|
|
|
:param addr: Address to validate
|
|
|
|
:returns: bool
|
|
|
|
"""
|
|
|
|
try:
|
2017-10-07 07:05:07 +00:00
|
|
|
IPv6Address(addr)
|
2014-10-06 19:10:03 +00:00
|
|
|
return True
|
|
|
|
except AddressValueError:
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def verify_ip_address(addr):
|
|
|
|
"""
|
|
|
|
Validate an IP address as either IPv4 or IPv6
|
|
|
|
|
|
|
|
:param addr: Address to validate
|
|
|
|
:returns: bool
|
|
|
|
"""
|
|
|
|
return True if verify_ipv4(addr) else verify_ipv6(addr)
|
|
|
|
|
|
|
|
|
2016-07-08 19:19:08 +00:00
|
|
|
def md5_hash(salt=None, data=None):
|
2014-10-06 19:10:03 +00:00
|
|
|
"""
|
|
|
|
Returns the hashed output of md5sum on salt,data
|
|
|
|
using Python3 hashlib
|
|
|
|
|
|
|
|
:param salt: Initial salt
|
2015-02-02 18:00:39 +00:00
|
|
|
:param data: OPTIONAL Data to hash
|
2014-10-06 19:10:03 +00:00
|
|
|
:returns: str
|
|
|
|
"""
|
2016-04-23 00:40:59 +00:00
|
|
|
log.debug('md5_hash(salt="{text}")'.format(text=salt))
|
2016-07-08 19:19:08 +00:00
|
|
|
if not salt and not data:
|
|
|
|
return None
|
2014-10-06 19:10:03 +00:00
|
|
|
hash_obj = hashlib.new('md5')
|
2016-07-08 19:19:08 +00:00
|
|
|
if salt:
|
|
|
|
hash_obj.update(salt)
|
2015-02-02 18:00:39 +00:00
|
|
|
if data:
|
2016-06-18 02:45:02 +00:00
|
|
|
hash_obj.update(data)
|
2014-10-06 19:10:03 +00:00
|
|
|
hash_value = hash_obj.hexdigest()
|
2016-04-23 00:40:59 +00:00
|
|
|
log.debug('md5_hash() returning "{text}"'.format(text=hash_value))
|
2014-10-06 19:10:03 +00:00
|
|
|
return hash_value
|
|
|
|
|
|
|
|
|
2016-07-08 19:19:08 +00:00
|
|
|
def qmd5_hash(salt=None, data=None):
|
2014-10-06 19:10:03 +00:00
|
|
|
"""
|
2014-10-09 20:30:07 +00:00
|
|
|
Returns the hashed output of MD5Sum on salt, data
|
2016-07-08 19:19:08 +00:00
|
|
|
using PyQt5.QCryptographicHash. Function returns a
|
|
|
|
QByteArray instead of a text string.
|
|
|
|
If you need a string instead, call with
|
|
|
|
|
|
|
|
result = str(qmd5_hash(salt=..., data=...), encoding='ascii')
|
2014-10-06 19:10:03 +00:00
|
|
|
|
|
|
|
:param salt: Initial salt
|
2015-02-02 18:00:39 +00:00
|
|
|
:param data: OPTIONAL Data to hash
|
2016-07-08 19:19:08 +00:00
|
|
|
:returns: QByteArray
|
2014-10-06 19:10:03 +00:00
|
|
|
"""
|
2016-04-23 00:40:59 +00:00
|
|
|
log.debug('qmd5_hash(salt="{text}"'.format(text=salt))
|
2016-07-08 19:19:08 +00:00
|
|
|
if salt is None and data is None:
|
|
|
|
return None
|
2014-10-06 19:10:03 +00:00
|
|
|
hash_obj = QHash(QHash.Md5)
|
2016-07-08 19:19:08 +00:00
|
|
|
if salt:
|
|
|
|
hash_obj.addData(salt)
|
2016-06-18 02:02:53 +00:00
|
|
|
if data:
|
2016-06-18 02:45:02 +00:00
|
|
|
hash_obj.addData(data)
|
2014-10-06 19:10:03 +00:00
|
|
|
hash_value = hash_obj.result().toHex()
|
2016-06-17 23:48:32 +00:00
|
|
|
log.debug('qmd5_hash() returning "{hash}"'.format(hash=hash_value))
|
|
|
|
return hash_value
|
2014-10-06 19:10:03 +00:00
|
|
|
|
2014-10-31 20:12:06 +00:00
|
|
|
|
2014-10-31 19:47:36 +00:00
|
|
|
def clean_button_text(button_text):
|
|
|
|
"""
|
|
|
|
Clean the & and other characters out of button text
|
2014-10-31 20:12:06 +00:00
|
|
|
|
2014-10-31 19:47:36 +00:00
|
|
|
:param button_text: The text to clean
|
|
|
|
"""
|
|
|
|
return button_text.replace('&', '').replace('< ', '').replace(' >', '')
|
2014-10-06 19:10:03 +00:00
|
|
|
|
2014-10-31 20:12:06 +00:00
|
|
|
|
2016-04-01 17:28:40 +00:00
|
|
|
def add_actions(target, actions):
|
|
|
|
"""
|
|
|
|
Adds multiple actions to a menu or toolbar in one command.
|
|
|
|
|
|
|
|
:param target: The menu or toolbar to add actions to
|
|
|
|
:param actions: The actions to be added. An action consisting of the keyword ``None``
|
|
|
|
will result in a separator being inserted into the target.
|
|
|
|
"""
|
|
|
|
for action in actions:
|
|
|
|
if action is None:
|
|
|
|
target.addSeparator()
|
|
|
|
else:
|
|
|
|
target.addAction(action)
|
2016-04-04 20:14:04 +00:00
|
|
|
|
|
|
|
|
|
|
|
def get_uno_command(connection_type='pipe'):
|
|
|
|
"""
|
|
|
|
Returns the UNO command to launch an libreoffice.org instance.
|
|
|
|
"""
|
|
|
|
for command in ['libreoffice', 'soffice']:
|
|
|
|
if which(command):
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
raise FileNotFoundError('Command not found')
|
|
|
|
|
|
|
|
OPTIONS = '--nologo --norestore --minimized --nodefault --nofirststartwizard'
|
|
|
|
if connection_type == 'pipe':
|
|
|
|
CONNECTION = '"--accept=pipe,name=openlp_pipe;urp;"'
|
|
|
|
else:
|
|
|
|
CONNECTION = '"--accept=socket,host=localhost,port=2002;urp;"'
|
2016-04-23 21:38:43 +00:00
|
|
|
return '{cmd} {opt} {conn}'.format(cmd=command, opt=OPTIONS, conn=CONNECTION)
|
2016-04-04 20:14:04 +00:00
|
|
|
|
|
|
|
|
|
|
|
def get_uno_instance(resolver, connection_type='pipe'):
|
|
|
|
"""
|
|
|
|
Returns a running libreoffice.org instance.
|
|
|
|
|
|
|
|
:param resolver: The UNO resolver to use to find a running instance.
|
|
|
|
"""
|
|
|
|
log.debug('get UNO Desktop Openoffice - resolve')
|
|
|
|
if connection_type == 'pipe':
|
|
|
|
return resolver.resolve('uno:pipe,name=openlp_pipe;urp;StarOffice.ComponentContext')
|
|
|
|
else:
|
2016-04-04 20:27:33 +00:00
|
|
|
return resolver.resolve('uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext')
|
2016-04-05 16:58:29 +00:00
|
|
|
|
|
|
|
|
|
|
|
def get_filesystem_encoding():
|
|
|
|
"""
|
|
|
|
Returns the name of the encoding used to convert Unicode filenames into system file names.
|
|
|
|
"""
|
|
|
|
encoding = sys.getfilesystemencoding()
|
|
|
|
if encoding is None:
|
|
|
|
encoding = sys.getdefaultencoding()
|
2016-04-05 17:10:51 +00:00
|
|
|
return encoding
|
|
|
|
|
|
|
|
|
2017-08-12 17:45:56 +00:00
|
|
|
def delete_file(file_path):
|
2016-04-05 17:10:51 +00:00
|
|
|
"""
|
|
|
|
Deletes a file from the system.
|
|
|
|
|
2017-08-25 20:03:25 +00:00
|
|
|
:param openlp.core.common.path.Path file_path: The file, including path, to delete.
|
2017-08-12 17:45:56 +00:00
|
|
|
:return: True if the deletion was successful, or the file never existed. False otherwise.
|
|
|
|
:rtype: bool
|
2016-04-05 17:10:51 +00:00
|
|
|
"""
|
2017-08-12 17:45:56 +00:00
|
|
|
if not file_path:
|
2016-04-05 17:10:51 +00:00
|
|
|
return False
|
|
|
|
try:
|
2017-08-12 17:45:56 +00:00
|
|
|
if file_path.exists():
|
|
|
|
file_path.unlink()
|
2016-04-05 17:10:51 +00:00
|
|
|
return True
|
2017-11-03 20:55:41 +00:00
|
|
|
except OSError:
|
2017-08-12 17:45:56 +00:00
|
|
|
log.exception('Unable to delete file {file_path}'.format(file_path=file_path))
|
2016-04-05 17:30:20 +00:00
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def get_images_filter():
|
|
|
|
"""
|
|
|
|
Returns a filter string for a file dialog containing all the supported image formats.
|
|
|
|
"""
|
2017-10-07 07:05:07 +00:00
|
|
|
from openlp.core.common.i18n import translate
|
2016-04-05 17:30:20 +00:00
|
|
|
global IMAGES_FILTER
|
|
|
|
if not IMAGES_FILTER:
|
|
|
|
log.debug('Generating images filter.')
|
|
|
|
formats = list(map(bytes.decode, list(map(bytes, QtGui.QImageReader.supportedImageFormats()))))
|
2016-04-23 00:40:59 +00:00
|
|
|
visible_formats = '(*.{text})'.format(text='; *.'.join(formats))
|
|
|
|
actual_formats = '(*.{text})'.format(text=' *.'.join(formats))
|
|
|
|
IMAGES_FILTER = '{text} {visible} {actual}'.format(text=translate('OpenLP', 'Image Files'),
|
|
|
|
visible=visible_formats,
|
|
|
|
actual=actual_formats)
|
2016-04-05 17:30:20 +00:00
|
|
|
return IMAGES_FILTER
|
|
|
|
|
|
|
|
|
2017-08-12 17:45:56 +00:00
|
|
|
def is_not_image_file(file_path):
|
2016-04-05 17:30:20 +00:00
|
|
|
"""
|
|
|
|
Validate that the file is not an image file.
|
|
|
|
|
2017-08-25 20:03:25 +00:00
|
|
|
:param openlp.core.common.path.Path file_path: The file to be checked.
|
2017-08-12 17:45:56 +00:00
|
|
|
:return: If the file is not an image
|
|
|
|
:rtype: bool
|
2016-04-05 17:30:20 +00:00
|
|
|
"""
|
2017-08-12 17:45:56 +00:00
|
|
|
if not (file_path and file_path.exists()):
|
2016-04-05 17:30:20 +00:00
|
|
|
return True
|
|
|
|
else:
|
|
|
|
formats = [bytes(fmt).decode().lower() for fmt in QtGui.QImageReader.supportedImageFormats()]
|
2017-08-12 17:45:56 +00:00
|
|
|
if file_path.suffix[1:].lower() in formats:
|
2016-04-05 17:30:20 +00:00
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
def clean_filename(filename):
|
|
|
|
"""
|
|
|
|
Removes invalid characters from the given ``filename``.
|
|
|
|
|
2017-08-12 17:45:56 +00:00
|
|
|
:param str filename: The "dirty" file name to clean.
|
|
|
|
:return: The cleaned string
|
|
|
|
:rtype: str
|
2016-04-05 17:30:20 +00:00
|
|
|
"""
|
2016-04-05 20:07:57 +00:00
|
|
|
return INVALID_FILE_CHARS.sub('_', CONTROL_CHARS.sub('', filename))
|
2016-04-16 13:51:42 +00:00
|
|
|
|
|
|
|
|
2016-04-21 16:26:34 +00:00
|
|
|
def check_binary_exists(program_path):
|
2016-04-16 13:51:42 +00:00
|
|
|
"""
|
|
|
|
Function that checks whether a binary exists.
|
|
|
|
|
2017-08-25 20:03:25 +00:00
|
|
|
:param openlp.core.common.path.Path program_path: The full path to the binary to check.
|
2016-04-16 13:51:42 +00:00
|
|
|
:return: program output to be parsed
|
2017-08-12 17:45:56 +00:00
|
|
|
:rtype: bytes
|
2016-04-16 13:51:42 +00:00
|
|
|
"""
|
2016-04-23 21:38:43 +00:00
|
|
|
log.debug('testing program_path: {text}'.format(text=program_path))
|
2016-04-16 13:51:42 +00:00
|
|
|
try:
|
|
|
|
# Setup startupinfo options for check_output to avoid console popping up on windows
|
|
|
|
if is_win():
|
2017-10-07 07:05:07 +00:00
|
|
|
from subprocess import STARTUPINFO, STARTF_USESHOWWINDOW
|
2016-04-16 13:51:42 +00:00
|
|
|
startupinfo = STARTUPINFO()
|
|
|
|
startupinfo.dwFlags |= STARTF_USESHOWWINDOW
|
|
|
|
else:
|
|
|
|
startupinfo = None
|
2017-08-12 17:45:56 +00:00
|
|
|
run_log = check_output([str(program_path), '--help'], stderr=STDOUT, startupinfo=startupinfo)
|
2016-04-16 13:51:42 +00:00
|
|
|
except CalledProcessError as e:
|
2017-08-12 17:45:56 +00:00
|
|
|
run_log = e.output
|
2016-04-16 13:51:42 +00:00
|
|
|
except Exception:
|
|
|
|
trace_error_handler(log)
|
2017-08-12 17:45:56 +00:00
|
|
|
run_log = ''
|
|
|
|
log.debug('check_output returned: {text}'.format(text=run_log))
|
|
|
|
return run_log
|
2016-08-09 06:24:04 +00:00
|
|
|
|
|
|
|
|
2017-08-12 17:45:56 +00:00
|
|
|
def get_file_encoding(file_path):
|
2016-08-09 06:24:04 +00:00
|
|
|
"""
|
|
|
|
Utility function to incrementally detect the file encoding.
|
|
|
|
|
2017-08-25 20:03:25 +00:00
|
|
|
:param openlp.core.common.path.Path file_path: Filename for the file to determine the encoding for.
|
2016-08-09 06:24:04 +00:00
|
|
|
:return: A dict with the keys 'encoding' and 'confidence'
|
2017-08-12 17:45:56 +00:00
|
|
|
:rtype: dict[str, float]
|
2016-08-09 06:24:04 +00:00
|
|
|
"""
|
|
|
|
detector = UniversalDetector()
|
|
|
|
try:
|
2017-08-12 17:45:56 +00:00
|
|
|
with file_path.open('rb') as detect_file:
|
2016-08-09 06:24:04 +00:00
|
|
|
while not detector.done:
|
|
|
|
chunk = detect_file.read(1024)
|
|
|
|
if not chunk:
|
|
|
|
break
|
|
|
|
detector.feed(chunk)
|
|
|
|
detector.close()
|
|
|
|
return detector.result
|
|
|
|
except OSError:
|
|
|
|
log.exception('Error detecting file encoding')
|
2017-10-28 10:04:09 +00:00
|
|
|
|
2017-10-28 18:58:34 +00:00
|
|
|
|
2017-10-28 10:04:09 +00:00
|
|
|
def normalize_str(irreg_str):
|
|
|
|
"""
|
2017-10-28 18:58:34 +00:00
|
|
|
Normalize the supplied string. Remove unicode control chars and tidy up white space.
|
2017-10-28 10:04:09 +00:00
|
|
|
|
2017-10-28 18:58:34 +00:00
|
|
|
:param str irreg_str: The string to normalize.
|
|
|
|
:return: The normalized string
|
|
|
|
:rtype: str
|
2017-10-28 10:04:09 +00:00
|
|
|
"""
|
|
|
|
irreg_str = irreg_str.translate(REPLACMENT_CHARS_MAP)
|
2017-10-28 18:58:34 +00:00
|
|
|
irreg_str = CONTROL_CHARS.sub('', irreg_str)
|
2017-10-28 10:04:09 +00:00
|
|
|
irreg_str = NEW_LINE_REGEX.sub('\n', irreg_str)
|
|
|
|
return WHITESPACE_REGEX.sub(' ', irreg_str)
|