mirror of https://gitlab.com/openlp/openlp.git
Merge branch 'master' into list-view
This commit is contained in:
commit
fba1e54f05
|
@ -13,11 +13,13 @@ environment:
|
||||||
CHOCO_VLC_ARG:
|
CHOCO_VLC_ARG:
|
||||||
FORCE_PACKAGING: 0
|
FORCE_PACKAGING: 0
|
||||||
FORCE_PACKAGING_MANUAL: 0
|
FORCE_PACKAGING_MANUAL: 0
|
||||||
|
PYICU_PACK: PyICU-2.9-cp38-cp38-win_amd64.whl
|
||||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||||
PY_DIR: C:\\Python38
|
PY_DIR: C:\\Python38
|
||||||
CHOCO_VLC_ARG: --forcex86
|
CHOCO_VLC_ARG: --forcex86
|
||||||
FORCE_PACKAGING: 0
|
FORCE_PACKAGING: 0
|
||||||
FORCE_PACKAGING_MANUAL: 0
|
FORCE_PACKAGING_MANUAL: 0
|
||||||
|
PYICU_PACK: PyICU-2.9-cp38-cp38-win32.whl
|
||||||
- APPVEYOR_BUILD_WORKER_IMAGE: macos-catalina
|
- APPVEYOR_BUILD_WORKER_IMAGE: macos-catalina
|
||||||
QT_QPA_PLATFORM: offscreen
|
QT_QPA_PLATFORM: offscreen
|
||||||
FORCE_PACKAGING: 0
|
FORCE_PACKAGING: 0
|
||||||
|
@ -36,9 +38,13 @@ install:
|
||||||
# Install Windows only dependencies
|
# Install Windows only dependencies
|
||||||
- cmd: python -m pip install pyodbc pypiwin32
|
- cmd: python -m pip install pyodbc pypiwin32
|
||||||
- cmd: choco install vlc %CHOCO_VLC_ARG% --no-progress --limit-output
|
- cmd: choco install vlc %CHOCO_VLC_ARG% --no-progress --limit-output
|
||||||
|
# Download and install pyicu for windows (originally from http://www.lfd.uci.edu/~gohlke/pythonlibs/)
|
||||||
|
- cmd: python -m pip install https://get.openlp.org/win-sdk/%PYICU_PACK%
|
||||||
# Mac only dependencies
|
# Mac only dependencies
|
||||||
- sh: python -m pip install Pyro4 'pyobjc-core<8.2' 'pyobjc-framework-Cocoa<8.2' py-applescript
|
|
||||||
- sh: brew install --cask vlc
|
- sh: brew install --cask vlc
|
||||||
|
- sh: brew install pkg-config icu4c
|
||||||
|
- sh: PATH="/usr/local/opt/icu4c/bin:/usr/local/opt/icu4c/sbin:$PATH" PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/local/opt/icu4c/lib/pkgconfig" python -m pip install pyicu
|
||||||
|
- sh: python -m pip install Pyro4 'pyobjc-core<8.2' 'pyobjc-framework-Cocoa<8.2' py-applescript
|
||||||
|
|
||||||
build: off
|
build: off
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
3.0.0
|
3.0.0.dev6+dbea8bf56
|
|
@ -22,7 +22,7 @@
|
||||||
from openlp.core.api.versions.v2.controller import controller_views
|
from openlp.core.api.versions.v2.controller import controller_views
|
||||||
from openlp.core.api.versions.v2.core import core
|
from openlp.core.api.versions.v2.core import core
|
||||||
from openlp.core.api.versions.v2.service import service_views
|
from openlp.core.api.versions.v2.service import service_views
|
||||||
from openlp.core.api.versions.v2.plugins import plugins
|
from openlp.core.api.versions.v2.plugins import plugins, alert_1_views, alert_2_views
|
||||||
|
|
||||||
|
|
||||||
def register_blueprints(app):
|
def register_blueprints(app):
|
||||||
|
@ -30,3 +30,5 @@ def register_blueprints(app):
|
||||||
app.register_blueprint(core, url_prefix='/api/v2/core/')
|
app.register_blueprint(core, url_prefix='/api/v2/core/')
|
||||||
app.register_blueprint(service_views, url_prefix='/api/v2/service/')
|
app.register_blueprint(service_views, url_prefix='/api/v2/service/')
|
||||||
app.register_blueprint(plugins, url_prefix='/api/v2/plugins/')
|
app.register_blueprint(plugins, url_prefix='/api/v2/plugins/')
|
||||||
|
app.register_blueprint(alert_2_views, url_prefix='/api/v2/plugins/alerts')
|
||||||
|
app.register_blueprint(alert_1_views, url_prefix='/api/alert')
|
||||||
|
|
|
@ -24,7 +24,7 @@ import logging
|
||||||
import re
|
import re
|
||||||
from flask import abort, request, Blueprint, jsonify
|
from flask import abort, request, Blueprint, jsonify
|
||||||
|
|
||||||
from openlp.core.api.lib import login_required
|
from openlp.core.api.lib import login_required, extract_request, old_success_response, old_auth
|
||||||
from openlp.core.lib.plugin import PluginStatus
|
from openlp.core.lib.plugin import PluginStatus
|
||||||
from openlp.core.common.registry import Registry
|
from openlp.core.common.registry import Registry
|
||||||
from openlp.plugins.songs.lib import transpose_lyrics
|
from openlp.plugins.songs.lib import transpose_lyrics
|
||||||
|
@ -33,6 +33,8 @@ log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
plugins = Blueprint('v2-plugins', __name__)
|
plugins = Blueprint('v2-plugins', __name__)
|
||||||
|
alert_1_views = Blueprint('v1-alert-plugin', __name__)
|
||||||
|
alert_2_views = Blueprint('v2-alert-plugin', __name__)
|
||||||
|
|
||||||
|
|
||||||
def search(plugin_name, text):
|
def search(plugin_name, text):
|
||||||
|
@ -172,3 +174,27 @@ def transpose(transpose_value):
|
||||||
chord_slides.append({'chords': verse_list[i + 1].strip(), 'verse': verse_list[i]})
|
chord_slides.append({'chords': verse_list[i + 1].strip(), 'verse': verse_list[i]})
|
||||||
return jsonify(chord_slides), 200
|
return jsonify(chord_slides), 200
|
||||||
abort(400)
|
abort(400)
|
||||||
|
|
||||||
|
|
||||||
|
@alert_2_views.route('', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def alert():
|
||||||
|
data = request.json
|
||||||
|
if not data:
|
||||||
|
abort(400)
|
||||||
|
alert = data.get('text', '')
|
||||||
|
if alert:
|
||||||
|
if Registry().get('plugin_manager').get_plugin_by_name('alerts').status == PluginStatus.Active:
|
||||||
|
Registry().get('alerts_manager').alerts_text.emit([alert])
|
||||||
|
return '', 204
|
||||||
|
abort(400)
|
||||||
|
|
||||||
|
|
||||||
|
@alert_1_views.route('')
|
||||||
|
@old_auth
|
||||||
|
def old_alert():
|
||||||
|
alert = extract_request(request.args.get('data', ''), 'text')
|
||||||
|
if alert:
|
||||||
|
if Registry().get('plugin_manager').get_plugin_by_name('alerts').status == PluginStatus.Active:
|
||||||
|
Registry().get('alerts_manager').alerts_text.emit([alert])
|
||||||
|
return old_success_response()
|
||||||
|
|
|
@ -100,6 +100,25 @@ def trace_error_handler(logger):
|
||||||
logger.error(log_string)
|
logger.error(log_string)
|
||||||
|
|
||||||
|
|
||||||
|
def path_to_module(path):
|
||||||
|
"""
|
||||||
|
Convert a path to a module name (i.e openlp.core.common)
|
||||||
|
|
||||||
|
:param pathlib.Path path: The path to convert to a module name.
|
||||||
|
:return: The module name.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
module_path = path.with_suffix('')
|
||||||
|
return 'openlp.' + '.'.join(module_path.parts)
|
||||||
|
|
||||||
|
|
||||||
|
def import_openlp_module(module_name):
|
||||||
|
"""
|
||||||
|
Refactor module import out for testability. In Python 3.11, mock.patch and import_module do not play along nicely.
|
||||||
|
"""
|
||||||
|
importlib.import_module(module_name)
|
||||||
|
|
||||||
|
|
||||||
def extension_loader(glob_pattern, excluded_files=None):
|
def extension_loader(glob_pattern, excluded_files=None):
|
||||||
"""
|
"""
|
||||||
A utility function to find and load OpenLP extensions, such as plugins, presentation and media controllers and
|
A utility function to find and load OpenLP extensions, such as plugins, presentation and media controllers and
|
||||||
|
@ -119,25 +138,13 @@ def extension_loader(glob_pattern, excluded_files=None):
|
||||||
log.debug('Attempting to import %s', extension_path)
|
log.debug('Attempting to import %s', extension_path)
|
||||||
module_name = path_to_module(extension_path)
|
module_name = path_to_module(extension_path)
|
||||||
try:
|
try:
|
||||||
importlib.import_module(module_name)
|
import_openlp_module(module_name)
|
||||||
except (ImportError, OSError):
|
except (ImportError, OSError):
|
||||||
# On some platforms importing vlc.py might cause OSError exceptions. (e.g. Mac OS X)
|
# On some platforms importing vlc.py might cause OSError exceptions. (e.g. Mac OS X)
|
||||||
log.exception('Failed to import {module_name} on path {extension_path}'
|
log.exception('Failed to import {module_name} on path {extension_path}'
|
||||||
.format(module_name=module_name, extension_path=extension_path))
|
.format(module_name=module_name, extension_path=extension_path))
|
||||||
|
|
||||||
|
|
||||||
def path_to_module(path):
|
|
||||||
"""
|
|
||||||
Convert a path to a module name (i.e openlp.core.common)
|
|
||||||
|
|
||||||
:param pathlib.Path path: The path to convert to a module name.
|
|
||||||
:return: The module name.
|
|
||||||
:rtype: str
|
|
||||||
"""
|
|
||||||
module_path = path.with_suffix('')
|
|
||||||
return 'openlp.' + '.'.join(module_path.parts)
|
|
||||||
|
|
||||||
|
|
||||||
def get_frozen_path(frozen_option, non_frozen_option):
|
def get_frozen_path(frozen_option, non_frozen_option):
|
||||||
"""
|
"""
|
||||||
Return a path based on the system status.
|
Return a path based on the system status.
|
||||||
|
|
|
@ -112,6 +112,15 @@ class ServiceItemType(IntEnum):
|
||||||
Image = 2
|
Image = 2
|
||||||
Command = 3
|
Command = 3
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse(value):
|
||||||
|
if value in [1, '1', 'Text', 'ServiceItemType.Text']:
|
||||||
|
return ServiceItemType.Text
|
||||||
|
elif value in [2, '2', 'Image', 'ServiceItemType.Image']:
|
||||||
|
return ServiceItemType.Image
|
||||||
|
elif value in [3, '3', 'Command', 'ServiceItemType.Command']:
|
||||||
|
return ServiceItemType.Command
|
||||||
|
|
||||||
|
|
||||||
@unique
|
@unique
|
||||||
class PluginStatus(IntEnum):
|
class PluginStatus(IntEnum):
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
The :mod:`languages` module provides a list of language names with utility functions.
|
The :mod:`languages` module provides a list of language names with utility functions.
|
||||||
"""
|
"""
|
||||||
import itertools
|
import itertools
|
||||||
|
import locale
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
@ -51,7 +52,8 @@ def translate(context, text, comment=None, qt_translate=QtCore.QCoreApplication.
|
||||||
|
|
||||||
|
|
||||||
Language = namedtuple('Language', ['id', 'name', 'code'])
|
Language = namedtuple('Language', ['id', 'name', 'code'])
|
||||||
COLLATOR = None
|
ICU_COLLATOR = None
|
||||||
|
DIGITS_OR_NONDIGITS = re.compile(r'\d+|\D+')
|
||||||
LANGUAGES = sorted([
|
LANGUAGES = sorted([
|
||||||
Language(1, translate('common.languages', '(Afan) Oromo', 'Language code: om'), 'om'),
|
Language(1, translate('common.languages', '(Afan) Oromo', 'Language code: om'), 'om'),
|
||||||
Language(2, translate('common.languages', 'Abkhazian', 'Language code: ab'), 'ab'),
|
Language(2, translate('common.languages', 'Abkhazian', 'Language code: ab'), 'ab'),
|
||||||
|
@ -503,19 +505,25 @@ def format_time(text, local_time):
|
||||||
return re.sub(r'%[a-zA-Z]', match_formatting, text)
|
return re.sub(r'%[a-zA-Z]', match_formatting, text)
|
||||||
|
|
||||||
|
|
||||||
def get_locale_key(string, numeric=False):
|
def get_locale_key(string):
|
||||||
"""
|
"""
|
||||||
Creates a key for case insensitive, locale aware string sorting.
|
Creates a key for case insensitive, locale aware string sorting.
|
||||||
|
|
||||||
:param string: The corresponding string.
|
:param string: The corresponding string.
|
||||||
"""
|
"""
|
||||||
string = string.lower()
|
string = string.lower()
|
||||||
global COLLATOR
|
# ICU is the prefered way to handle locale sort key, we fallback to locale.strxfrm which will work in most cases.
|
||||||
if COLLATOR is None:
|
global ICU_COLLATOR
|
||||||
language = LanguageManager.get_language()
|
try:
|
||||||
COLLATOR = QtCore.QCollator(QtCore.QLocale(language))
|
if ICU_COLLATOR is None:
|
||||||
COLLATOR.setNumericMode(numeric)
|
import icu
|
||||||
return COLLATOR.sortKey(string)
|
language = LanguageManager.get_language()
|
||||||
|
icu_locale = icu.Locale(language)
|
||||||
|
ICU_COLLATOR = icu.Collator.createInstance(icu_locale)
|
||||||
|
return ICU_COLLATOR.getSortKey(string)
|
||||||
|
except Exception:
|
||||||
|
log.warning('ICU not found! Fallback to strxfrm')
|
||||||
|
return locale.strxfrm(string).encode()
|
||||||
|
|
||||||
|
|
||||||
def get_natural_key(string):
|
def get_natural_key(string):
|
||||||
|
@ -525,7 +533,13 @@ def get_natural_key(string):
|
||||||
:param string: string to be sorted by
|
:param string: string to be sorted by
|
||||||
Returns a list of string compare keys and integers.
|
Returns a list of string compare keys and integers.
|
||||||
"""
|
"""
|
||||||
return get_locale_key(string, True)
|
key = DIGITS_OR_NONDIGITS.findall(string)
|
||||||
|
key = [int(part) if part.isdigit() else get_locale_key(part) for part in key]
|
||||||
|
# Python 3 does not support comparison of different types anymore. So make sure, that we do not compare str
|
||||||
|
# and int.
|
||||||
|
if string and string[0].isdigit():
|
||||||
|
return [b''] + key
|
||||||
|
return key
|
||||||
|
|
||||||
|
|
||||||
def get_language(name):
|
def get_language(name):
|
||||||
|
|
|
@ -291,6 +291,8 @@ class Settings(QtCore.QSettings):
|
||||||
'media/vlc arguments': '',
|
'media/vlc arguments': '',
|
||||||
'media/live volume': 50,
|
'media/live volume': 50,
|
||||||
'media/preview volume': 0,
|
'media/preview volume': 0,
|
||||||
|
'media/live loop': False,
|
||||||
|
'media/preview loop': False,
|
||||||
'media/db type': 'sqlite',
|
'media/db type': 'sqlite',
|
||||||
'media/db username': '',
|
'media/db username': '',
|
||||||
'media/db password': '',
|
'media/db password': '',
|
||||||
|
|
|
@ -463,7 +463,7 @@ class ServiceItem(RegistryProperties):
|
||||||
header = service_item['serviceitem']['header']
|
header = service_item['serviceitem']['header']
|
||||||
self.title = header['title']
|
self.title = header['title']
|
||||||
self.name = header['name']
|
self.name = header['name']
|
||||||
self.service_item_type = header['type']
|
self.service_item_type = ServiceItemType.parse(header['type'])
|
||||||
self.theme = header['theme']
|
self.theme = header['theme']
|
||||||
self.add_icon()
|
self.add_icon()
|
||||||
self.raw_footer = header['footer']
|
self.raw_footer = header['footer']
|
||||||
|
@ -891,7 +891,7 @@ class ServiceItem(RegistryProperties):
|
||||||
data_dict = {
|
data_dict = {
|
||||||
'title': self.title,
|
'title': self.title,
|
||||||
'name': self.name,
|
'name': self.name,
|
||||||
'type': str(self.service_item_type),
|
'type': self.service_item_type,
|
||||||
'theme': self.theme,
|
'theme': self.theme,
|
||||||
'footer': self.raw_footer,
|
'footer': self.raw_footer,
|
||||||
'audit': self.audit,
|
'audit': self.audit,
|
||||||
|
|
|
@ -23,6 +23,8 @@ The :mod:`~openlp.core.ui.media` module contains classes and objects for media p
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from openlp.core.common.registry import Registry
|
||||||
|
|
||||||
log = logging.getLogger(__name__ + '.__init__')
|
log = logging.getLogger(__name__ + '.__init__')
|
||||||
|
|
||||||
# Audio and video extensions copied from 'include/vlc_interface.h' from vlc 2.2.0 source
|
# Audio and video extensions copied from 'include/vlc_interface.h' from vlc 2.2.0 source
|
||||||
|
@ -83,9 +85,7 @@ class ItemMediaInfo(object):
|
||||||
This class hold the media related info
|
This class hold the media related info
|
||||||
"""
|
"""
|
||||||
file_info = None
|
file_info = None
|
||||||
volume = 100
|
|
||||||
is_background = False
|
is_background = False
|
||||||
is_looping_playback = False
|
|
||||||
length = 0
|
length = 0
|
||||||
start_time = 0
|
start_time = 0
|
||||||
end_time = 0
|
end_time = 0
|
||||||
|
@ -97,6 +97,57 @@ class ItemMediaInfo(object):
|
||||||
media_type = MediaType()
|
media_type = MediaType()
|
||||||
|
|
||||||
|
|
||||||
|
def get_volume(controller) -> int:
|
||||||
|
"""
|
||||||
|
The volume needs to be retrieved
|
||||||
|
|
||||||
|
:param controller: the controller in use
|
||||||
|
:return: Are we looping
|
||||||
|
"""
|
||||||
|
if controller.is_live:
|
||||||
|
return Registry().get('settings').value('media/live volume')
|
||||||
|
else:
|
||||||
|
return Registry().get('settings').value('media/preview volume')
|
||||||
|
|
||||||
|
|
||||||
|
def save_volume(controller, volume: int) -> None:
|
||||||
|
"""
|
||||||
|
The volume needs to be saved
|
||||||
|
|
||||||
|
:param controller: the controller in use
|
||||||
|
:param volume: The volume to use and save
|
||||||
|
:return: Are we looping
|
||||||
|
"""
|
||||||
|
if controller.is_live:
|
||||||
|
return Registry().get('settings').setValue('media/live volume', volume)
|
||||||
|
else:
|
||||||
|
return Registry().get('settings').setValue('media/preview volume', volume)
|
||||||
|
|
||||||
|
|
||||||
|
def is_looping_playback(controller) -> bool:
|
||||||
|
"""
|
||||||
|
:param controller: the controller in use
|
||||||
|
:return: Are we looping
|
||||||
|
"""
|
||||||
|
if controller.is_live:
|
||||||
|
return Registry().get('settings').value('media/live loop')
|
||||||
|
else:
|
||||||
|
return Registry().get('settings').value('media/preview loop')
|
||||||
|
|
||||||
|
|
||||||
|
def toggle_looping_playback(controller) -> None:
|
||||||
|
"""
|
||||||
|
|
||||||
|
:param controller: the controller in use
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
if controller.is_live:
|
||||||
|
Registry().get('settings').setValue('media/live loop', not Registry().get('settings').value('media/live loop'))
|
||||||
|
else:
|
||||||
|
Registry().get('settings').setValue('media/preview loop',
|
||||||
|
not Registry().get('settings').value('media/preview loop'))
|
||||||
|
|
||||||
|
|
||||||
def parse_optical_path(input_string):
|
def parse_optical_path(input_string):
|
||||||
"""
|
"""
|
||||||
Split the optical path info.
|
Split the optical path info.
|
||||||
|
|
|
@ -42,7 +42,8 @@ from openlp.core.lib.serviceitem import ItemCapabilities
|
||||||
from openlp.core.lib.ui import critical_error_message_box
|
from openlp.core.lib.ui import critical_error_message_box
|
||||||
from openlp.core.state import State
|
from openlp.core.state import State
|
||||||
from openlp.core.ui import DisplayControllerType, HideMode
|
from openlp.core.ui import DisplayControllerType, HideMode
|
||||||
from openlp.core.ui.media import MediaState, ItemMediaInfo, MediaType, parse_optical_path, parse_stream_path
|
from openlp.core.ui.media import MediaState, ItemMediaInfo, MediaType, parse_optical_path, parse_stream_path, \
|
||||||
|
get_volume, toggle_looping_playback, is_looping_playback, save_volume
|
||||||
from openlp.core.ui.media.remote import register_views
|
from openlp.core.ui.media.remote import register_views
|
||||||
from openlp.core.ui.media.vlcplayer import VlcPlayer, get_vlc
|
from openlp.core.ui.media.vlcplayer import VlcPlayer, get_vlc
|
||||||
|
|
||||||
|
@ -236,10 +237,6 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
||||||
self.media_reset(controller)
|
self.media_reset(controller)
|
||||||
controller.media_info = ItemMediaInfo()
|
controller.media_info = ItemMediaInfo()
|
||||||
controller.media_info.media_type = MediaType.Video
|
controller.media_info.media_type = MediaType.Video
|
||||||
if controller.is_live:
|
|
||||||
controller.media_info.volume = self.settings.value('media/live volume')
|
|
||||||
else:
|
|
||||||
controller.media_info.volume = self.settings.value('media/preview volume')
|
|
||||||
# background will always loop video.
|
# background will always loop video.
|
||||||
if service_item.is_capable(ItemCapabilities.HasBackgroundAudio):
|
if service_item.is_capable(ItemCapabilities.HasBackgroundAudio):
|
||||||
controller.media_info.file_info = [file_path for (file_path, file_hash) in service_item.background_audio]
|
controller.media_info.file_info = [file_path for (file_path, file_hash) in service_item.background_audio]
|
||||||
|
@ -255,7 +252,6 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
||||||
elif service_item.is_capable(ItemCapabilities.HasBackgroundVideo):
|
elif service_item.is_capable(ItemCapabilities.HasBackgroundVideo):
|
||||||
controller.media_info.file_info = [service_item.video_file_name]
|
controller.media_info.file_info = [service_item.video_file_name]
|
||||||
service_item.media_length = self.media_length(service_item.video_file_name)
|
service_item.media_length = self.media_length(service_item.video_file_name)
|
||||||
controller.media_info.is_looping_playback = True
|
|
||||||
controller.media_info.is_background = True
|
controller.media_info.is_background = True
|
||||||
else:
|
else:
|
||||||
controller.media_info.file_info = [service_item.get_frame_path()]
|
controller.media_info.file_info = [service_item.get_frame_path()]
|
||||||
|
@ -457,13 +453,13 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
||||||
controller.seek_slider.blockSignals(False)
|
controller.seek_slider.blockSignals(False)
|
||||||
controller.volume_slider.blockSignals(False)
|
controller.volume_slider.blockSignals(False)
|
||||||
return False
|
return False
|
||||||
self.media_volume(controller, controller.media_info.volume)
|
self.media_volume(controller, get_volume(controller))
|
||||||
if not start_hidden:
|
if not start_hidden:
|
||||||
self._media_set_visibility(controller, True)
|
self._media_set_visibility(controller, True)
|
||||||
controller.mediabar.actions['playbackPlay'].setVisible(False)
|
controller.mediabar.actions['playbackPlay'].setVisible(False)
|
||||||
controller.mediabar.actions['playbackPause'].setVisible(True)
|
controller.mediabar.actions['playbackPause'].setVisible(True)
|
||||||
controller.mediabar.actions['playbackStop'].setDisabled(False)
|
controller.mediabar.actions['playbackStop'].setDisabled(False)
|
||||||
controller.mediabar.actions['playbackLoop'].setChecked(controller.media_info.is_looping_playback)
|
controller.mediabar.actions['playbackLoop'].setChecked(is_looping_playback(controller))
|
||||||
controller.mediabar.actions['playbackStop'].setVisible(not controller.media_info.is_background or
|
controller.mediabar.actions['playbackStop'].setVisible(not controller.media_info.is_background or
|
||||||
controller.media_info.media_type is MediaType.Audio)
|
controller.media_info.media_type is MediaType.Audio)
|
||||||
controller.mediabar.actions['playbackLoop'].setVisible((not controller.media_info.is_background and
|
controller.mediabar.actions['playbackLoop'].setVisible((not controller.media_info.is_background and
|
||||||
|
@ -501,7 +497,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
||||||
if controller.media_info.is_playing and controller.media_info.length > 0:
|
if controller.media_info.is_playing and controller.media_info.length > 0:
|
||||||
controller.media_info.timer += TICK_TIME
|
controller.media_info.timer += TICK_TIME
|
||||||
if controller.media_info.timer >= controller.media_info.start_time + controller.media_info.length:
|
if controller.media_info.timer >= controller.media_info.start_time + controller.media_info.length:
|
||||||
if controller.media_info.is_looping_playback:
|
if is_looping_playback(controller):
|
||||||
start_again = True
|
start_again = True
|
||||||
else:
|
else:
|
||||||
self.media_stop(controller)
|
self.media_stop(controller)
|
||||||
|
@ -522,8 +518,11 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
||||||
end_seconds = controller.media_info.end_time // 1000
|
end_seconds = controller.media_info.end_time // 1000
|
||||||
end_minutes = end_seconds // 60
|
end_minutes = end_seconds // 60
|
||||||
end_seconds %= 60
|
end_seconds %= 60
|
||||||
controller.position_label.setText(' %02d:%02d / %02d:%02d' %
|
if end_minutes == 0 and end_seconds == 0:
|
||||||
(minutes, seconds, end_minutes, end_seconds))
|
controller.position_label.setText('')
|
||||||
|
else:
|
||||||
|
controller.position_label.setText(' %02d:%02d / %02d:%02d' %
|
||||||
|
(minutes, seconds, end_minutes, end_seconds))
|
||||||
|
|
||||||
def media_pause_msg(self, msg):
|
def media_pause_msg(self, msg):
|
||||||
"""
|
"""
|
||||||
|
@ -573,8 +572,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
||||||
|
|
||||||
:param controller: The controller that needs to be stopped
|
:param controller: The controller that needs to be stopped
|
||||||
"""
|
"""
|
||||||
controller.media_info.is_looping_playback = not controller.media_info.is_looping_playback
|
toggle_looping_playback(controller)
|
||||||
controller.mediabar.actions['playbackLoop'].setChecked(controller.media_info.is_looping_playback)
|
controller.mediabar.actions['playbackLoop'].setChecked(is_looping_playback(controller))
|
||||||
|
|
||||||
def media_stop_msg(self, msg):
|
def media_stop_msg(self, msg):
|
||||||
"""
|
"""
|
||||||
|
@ -638,11 +637,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
||||||
:param volume: The volume to be set
|
:param volume: The volume to be set
|
||||||
"""
|
"""
|
||||||
self.log_debug(f'media_volume {volume}')
|
self.log_debug(f'media_volume {volume}')
|
||||||
if controller.is_live:
|
save_volume(controller, volume)
|
||||||
self.settings.setValue('media/live volume', volume)
|
|
||||||
else:
|
|
||||||
self.settings.setValue('media/preview volume', volume)
|
|
||||||
controller.media_info.volume = volume
|
|
||||||
self.current_media_players[controller.controller_type].volume(controller, volume)
|
self.current_media_players[controller.controller_type].volume(controller, volume)
|
||||||
controller.volume_slider.setValue(volume)
|
controller.volume_slider.setValue(volume)
|
||||||
|
|
||||||
|
|
|
@ -35,13 +35,11 @@ from openlp.core.common.i18n import translate
|
||||||
from openlp.core.common.platform import is_linux, is_macosx, is_win
|
from openlp.core.common.platform import is_linux, is_macosx, is_win
|
||||||
from openlp.core.display.screens import ScreenList
|
from openlp.core.display.screens import ScreenList
|
||||||
from openlp.core.lib.ui import critical_error_message_box
|
from openlp.core.lib.ui import critical_error_message_box
|
||||||
from openlp.core.ui.media import MediaState, MediaType, VlCState
|
from openlp.core.ui.media import MediaState, MediaType, VlCState, get_volume
|
||||||
from openlp.core.ui.media.mediaplayer import MediaPlayer
|
from openlp.core.ui.media.mediaplayer import MediaPlayer
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Audio and video extensions copied from 'include/vlc_interface.h' from vlc 2.2.0 source
|
|
||||||
|
|
||||||
|
|
||||||
STATE_WAIT_TIME = 60
|
STATE_WAIT_TIME = 60
|
||||||
|
|
||||||
|
@ -229,7 +227,7 @@ class VlcPlayer(MediaPlayer):
|
||||||
controller.vlc_media.parse()
|
controller.vlc_media.parse()
|
||||||
controller.seek_slider.setMinimum(controller.media_info.start_time)
|
controller.seek_slider.setMinimum(controller.media_info.start_time)
|
||||||
controller.seek_slider.setMaximum(controller.media_info.end_time)
|
controller.seek_slider.setMaximum(controller.media_info.end_time)
|
||||||
self.volume(controller, controller.media_info.volume)
|
self.volume(controller, get_volume(controller))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def media_state_wait(self, controller, media_state):
|
def media_state_wait(self, controller, media_state):
|
||||||
|
@ -275,7 +273,7 @@ class VlcPlayer(MediaPlayer):
|
||||||
threading.Thread(target=controller.vlc_media_player.play).start()
|
threading.Thread(target=controller.vlc_media_player.play).start()
|
||||||
if not self.media_state_wait(controller, VlCState.Playing):
|
if not self.media_state_wait(controller, VlCState.Playing):
|
||||||
return False
|
return False
|
||||||
self.volume(controller, controller.media_info.volume)
|
self.volume(controller, get_volume(controller))
|
||||||
self.set_state(MediaState.Playing, controller)
|
self.set_state(MediaState.Playing, controller)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
@ -971,6 +971,8 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
|
||||||
Registry().execute(
|
Registry().execute(
|
||||||
'{text}_start'.format(text=self.service_item.name.lower()),
|
'{text}_start'.format(text=self.service_item.name.lower()),
|
||||||
[self.service_item, self.is_live, self._current_hide_mode, slide_no])
|
[self.service_item, self.is_live, self._current_hide_mode, slide_no])
|
||||||
|
if self.service_item.is_capable(ItemCapabilities.ProvidesOwnTheme):
|
||||||
|
self._set_theme(self.service_item)
|
||||||
else:
|
else:
|
||||||
self._set_theme(self.service_item)
|
self._set_theme(self.service_item)
|
||||||
self.info_label.setText(self.service_item.title)
|
self.info_label.setText(self.service_item.title)
|
||||||
|
|
|
@ -29,7 +29,6 @@ from openlp.core.lib.plugin import Plugin, StringContent
|
||||||
from openlp.core.lib.theme import VerticalType
|
from openlp.core.lib.theme import VerticalType
|
||||||
from openlp.core.lib.ui import create_action
|
from openlp.core.lib.ui import create_action
|
||||||
from openlp.core.ui.icons import UiIcons
|
from openlp.core.ui.icons import UiIcons
|
||||||
from openlp.plugins.alerts.remote import register_views
|
|
||||||
from openlp.plugins.alerts.forms.alertform import AlertForm
|
from openlp.plugins.alerts.forms.alertform import AlertForm
|
||||||
from openlp.plugins.alerts.lib.alertsmanager import AlertsManager
|
from openlp.plugins.alerts.lib.alertsmanager import AlertsManager
|
||||||
from openlp.plugins.alerts.lib.alertstab import AlertsTab
|
from openlp.plugins.alerts.lib.alertstab import AlertsTab
|
||||||
|
@ -139,7 +138,6 @@ class AlertsPlugin(Plugin):
|
||||||
self.tools_alert_item.setVisible(True)
|
self.tools_alert_item.setVisible(True)
|
||||||
action_list = ActionList.get_instance()
|
action_list = ActionList.get_instance()
|
||||||
action_list.add_action(self.tools_alert_item, UiStrings().Tools)
|
action_list.add_action(self.tools_alert_item, UiStrings().Tools)
|
||||||
register_views()
|
|
||||||
|
|
||||||
def finalise(self):
|
def finalise(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
##########################################################################
|
|
||||||
# OpenLP - Open Source Lyrics Projection #
|
|
||||||
# ---------------------------------------------------------------------- #
|
|
||||||
# Copyright (c) 2008-2023 OpenLP Developers #
|
|
||||||
# ---------------------------------------------------------------------- #
|
|
||||||
# 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, either version 3 of the License, or #
|
|
||||||
# (at your option) any later version. #
|
|
||||||
# #
|
|
||||||
# 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, see <https://www.gnu.org/licenses/>. #
|
|
||||||
##########################################################################
|
|
||||||
from flask import Blueprint, request, abort
|
|
||||||
|
|
||||||
from openlp.core.api import app
|
|
||||||
from openlp.core.api.lib import login_required, extract_request, old_success_response, old_auth
|
|
||||||
from openlp.core.common.registry import Registry
|
|
||||||
from openlp.core.lib.plugin import PluginStatus
|
|
||||||
|
|
||||||
|
|
||||||
v1_views = Blueprint('v1-alert-plugin', __name__)
|
|
||||||
v2_views = Blueprint('v2-alert-plugin', __name__)
|
|
||||||
|
|
||||||
|
|
||||||
@v2_views.route('', methods=['POST'])
|
|
||||||
@login_required
|
|
||||||
def alert():
|
|
||||||
data = request.json
|
|
||||||
if not data:
|
|
||||||
abort(400)
|
|
||||||
alert = data.get('text', '')
|
|
||||||
if alert:
|
|
||||||
if Registry().get('plugin_manager').get_plugin_by_name('alerts').status == PluginStatus.Active:
|
|
||||||
Registry().get('alerts_manager').alerts_text.emit([alert])
|
|
||||||
return '', 204
|
|
||||||
abort(400)
|
|
||||||
|
|
||||||
|
|
||||||
@v1_views.route('')
|
|
||||||
@old_auth
|
|
||||||
def old_alert():
|
|
||||||
alert = extract_request(request.args.get('data', ''), 'text')
|
|
||||||
if alert:
|
|
||||||
if Registry().get('plugin_manager').get_plugin_by_name('alerts').status == PluginStatus.Active:
|
|
||||||
Registry().get('alerts_manager').alerts_text.emit([alert])
|
|
||||||
return old_success_response()
|
|
||||||
|
|
||||||
|
|
||||||
def register_views():
|
|
||||||
app.register_blueprint(v2_views, url_prefix='/api/v2/plugins/alerts')
|
|
||||||
app.register_blueprint(v1_views, url_prefix='/api/alert')
|
|
|
@ -1,116 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
|
||||||
|
|
||||||
##########################################################################
|
|
||||||
# OpenLP - Open Source Lyrics Projection #
|
|
||||||
# ---------------------------------------------------------------------- #
|
|
||||||
# Copyright (c) 2008-2023 OpenLP Developers #
|
|
||||||
# ---------------------------------------------------------------------- #
|
|
||||||
# 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, either version 3 of the License, or #
|
|
||||||
# (at your option) any later version. #
|
|
||||||
# #
|
|
||||||
# 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, see <https://www.gnu.org/licenses/>. #
|
|
||||||
##########################################################################
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from flask import abort, request, Blueprint, jsonify
|
|
||||||
|
|
||||||
from openlp.core.api import app
|
|
||||||
from openlp.core.api.lib import login_required, extract_request, old_auth
|
|
||||||
from openlp.core.lib.plugin import PluginStatus
|
|
||||||
from openlp.core.common.registry import Registry
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
v1_media = Blueprint('v1-media-plugin', __name__)
|
|
||||||
v2_media = Blueprint('v2-media-plugin', __name__)
|
|
||||||
|
|
||||||
|
|
||||||
def search(text):
|
|
||||||
plugin = Registry().get('plugin_manager').get_plugin_by_name('media')
|
|
||||||
if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search:
|
|
||||||
results = plugin.media_item.search(text, False)
|
|
||||||
return results
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def live(id):
|
|
||||||
plugin = Registry().get('plugin_manager').get_plugin_by_name('media')
|
|
||||||
if plugin.status == PluginStatus.Active and plugin.media_item:
|
|
||||||
plugin.media_item.media_go_live.emit([id, True])
|
|
||||||
|
|
||||||
|
|
||||||
def add(id):
|
|
||||||
plugin = Registry().get('plugin_manager').get_plugin_by_name('media')
|
|
||||||
if plugin.status == PluginStatus.Active and plugin.media_item:
|
|
||||||
item_id = plugin.media_item.create_item_from_id(id)
|
|
||||||
plugin.media_item.media_add_to_service.emit([item_id, True])
|
|
||||||
|
|
||||||
|
|
||||||
@v2_media.route('/search')
|
|
||||||
@login_required
|
|
||||||
def search_view():
|
|
||||||
text = request.args.get('text', '')
|
|
||||||
result = search(text)
|
|
||||||
return jsonify(result)
|
|
||||||
|
|
||||||
|
|
||||||
@v2_media.route('/add', methods=['POST'])
|
|
||||||
@login_required
|
|
||||||
def add_view():
|
|
||||||
data = request.json
|
|
||||||
if not data:
|
|
||||||
abort(400)
|
|
||||||
id = data.get('id', -1)
|
|
||||||
add(id)
|
|
||||||
return '', 204
|
|
||||||
|
|
||||||
|
|
||||||
@v2_media.route('/live', methods=['POST'])
|
|
||||||
@login_required
|
|
||||||
def live_view():
|
|
||||||
data = request.json
|
|
||||||
if not data:
|
|
||||||
abort(400)
|
|
||||||
id = data.get('id', -1)
|
|
||||||
live(id)
|
|
||||||
return '', 204
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------- DEPRECATED --------------
|
|
||||||
@v1_media.route('/search')
|
|
||||||
@old_auth
|
|
||||||
def old_search():
|
|
||||||
text = extract_request(request.args.get('data', ''), 'text')
|
|
||||||
return jsonify({'results': {'items': search(text)}})
|
|
||||||
|
|
||||||
|
|
||||||
@v1_media.route('/add')
|
|
||||||
@old_auth
|
|
||||||
def old_add():
|
|
||||||
id = extract_request(request.args.get('data', ''), 'id')
|
|
||||||
add(id)
|
|
||||||
return '', 204
|
|
||||||
|
|
||||||
|
|
||||||
@v1_media.route('/live')
|
|
||||||
@old_auth
|
|
||||||
def old_live():
|
|
||||||
id = extract_request(request.args.get('data', ''), 'id')
|
|
||||||
live(id)
|
|
||||||
return '', 204
|
|
||||||
# ---------------- END DEPRECATED ----------------
|
|
||||||
|
|
||||||
|
|
||||||
def register_views():
|
|
||||||
app.register_blueprint(v2_media, url_prefix='/api/v2/plugins/media/')
|
|
||||||
app.register_blueprint(v1_media, url_prefix='/api/media/')
|
|
|
@ -24,7 +24,7 @@ presentations from a variety of document formats.
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from openlp.core.common import extension_loader, sha256_file_hash
|
from openlp.core.common import extension_loader, sha256_file_hash
|
||||||
from openlp.core.common.i18n import translate
|
from openlp.core.common.i18n import translate
|
||||||
|
@ -84,7 +84,9 @@ class PresentationPlugin(Plugin):
|
||||||
has_old_scheme = True
|
has_old_scheme = True
|
||||||
# Migrate each file
|
# Migrate each file
|
||||||
presentation_paths = self.settings.value('presentations/presentations files') or []
|
presentation_paths = self.settings.value('presentations/presentations files') or []
|
||||||
for path in presentation_paths:
|
for presentation_path in presentation_paths:
|
||||||
|
# Typecast to Path object
|
||||||
|
path = Path(presentation_path)
|
||||||
# check to see if the file exists before trying to process it.
|
# check to see if the file exists before trying to process it.
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -52,7 +52,7 @@ def report_song_list():
|
||||||
report_file_path.with_suffix('.csv')
|
report_file_path.with_suffix('.csv')
|
||||||
Registry().get('application').set_busy_cursor()
|
Registry().get('application').set_busy_cursor()
|
||||||
try:
|
try:
|
||||||
with report_file_path.open('wt') as export_file:
|
with report_file_path.open('wt', encoding='utf8') as export_file:
|
||||||
fieldnames = ('Title', 'Alternative Title', 'Copyright', 'Author(s)', 'Song Book', 'Topic')
|
fieldnames = ('Title', 'Alternative Title', 'Copyright', 'Author(s)', 'Song Book', 'Topic')
|
||||||
writer = csv.DictWriter(export_file, fieldnames=fieldnames, quoting=csv.QUOTE_ALL)
|
writer = csv.DictWriter(export_file, fieldnames=fieldnames, quoting=csv.QUOTE_ALL)
|
||||||
headers = dict((n, n) for n in fieldnames)
|
headers = dict((n, n) for n in fieldnames)
|
||||||
|
|
|
@ -52,6 +52,7 @@ WIN32_MODULES = [
|
||||||
'win32com',
|
'win32com',
|
||||||
'win32ui',
|
'win32ui',
|
||||||
'pywintypes',
|
'pywintypes',
|
||||||
|
'icu',
|
||||||
]
|
]
|
||||||
|
|
||||||
LINUX_MODULES = [
|
LINUX_MODULES = [
|
||||||
|
|
1
setup.py
1
setup.py
|
@ -107,6 +107,7 @@ using a computer and a display/projector.""",
|
||||||
'lxml',
|
'lxml',
|
||||||
'Mako',
|
'Mako',
|
||||||
"pillow",
|
"pillow",
|
||||||
|
'PyICU',
|
||||||
'pymediainfo >= 2.2',
|
'pymediainfo >= 2.2',
|
||||||
'pyobjc; platform_system=="Darwin"',
|
'pyobjc; platform_system=="Darwin"',
|
||||||
'pyobjc-framework-Cocoa; platform_system=="Darwin"',
|
'pyobjc-framework-Cocoa; platform_system=="Darwin"',
|
||||||
|
|
|
@ -35,15 +35,38 @@ from openlp.core.common.registry import Registry
|
||||||
ZERO_URL = '0.0.0.0'
|
ZERO_URL = '0.0.0.0'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def mocked_get_installed_version():
|
||||||
|
setup_patcher = patch('openlp.core.api.tab.get_installed_version')
|
||||||
|
mocked_setup_patcher = setup_patcher.start()
|
||||||
|
mocked_setup_patcher.return_value = None
|
||||||
|
yield mocked_setup_patcher
|
||||||
|
setup_patcher.stop()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def api_tab(settings):
|
def api_tab_instantiate(mocked_get_installed_version, settings):
|
||||||
Registry().set_flag('website_version', '00-00-0000')
|
forms = []
|
||||||
Registry().set_flag('no_web_server', False)
|
|
||||||
parent = QtWidgets.QMainWindow()
|
parent = QtWidgets.QMainWindow()
|
||||||
form = ApiTab(parent)
|
|
||||||
yield form
|
def _create_api_tab():
|
||||||
|
nonlocal forms, parent
|
||||||
|
Registry().set_flag('website_version', '00-00-0000')
|
||||||
|
Registry().set_flag('no_web_server', False)
|
||||||
|
form = ApiTab(parent)
|
||||||
|
forms.append(form)
|
||||||
|
return form
|
||||||
|
|
||||||
|
yield _create_api_tab
|
||||||
del parent
|
del parent
|
||||||
del form
|
for form in forms:
|
||||||
|
del form
|
||||||
|
mocked_get_installed_version.return_value = None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def api_tab(api_tab_instantiate):
|
||||||
|
yield api_tab_instantiate()
|
||||||
|
|
||||||
|
|
||||||
def test_get_ip_address_default(api_tab):
|
def test_get_ip_address_default(api_tab):
|
||||||
|
@ -183,9 +206,12 @@ def test_available_version_property_set_none(api_tab):
|
||||||
assert api_tab.available_version_value.text() == '(unknown)'
|
assert api_tab.available_version_value.text() == '(unknown)'
|
||||||
|
|
||||||
|
|
||||||
def test_installed_version_property_get_none(api_tab):
|
def test_installed_version_property_get_none(mocked_get_installed_version, api_tab_instantiate, settings):
|
||||||
"""Test that the installed version property is None on init"""
|
"""Test that the installed version property is None on init"""
|
||||||
# GIVEN: An uninitialised API tab
|
# GIVEN: An uninitialised API tab
|
||||||
|
mocked_get_installed_version.return_value = None
|
||||||
|
settings.setValue('api/download_version', None)
|
||||||
|
api_tab = api_tab_instantiate()
|
||||||
|
|
||||||
# WHEN: the installed version is GET'ed
|
# WHEN: the installed version is GET'ed
|
||||||
result = api_tab.installed_version
|
result = api_tab.installed_version
|
||||||
|
|
|
@ -77,7 +77,7 @@ def test_extension_loader_files_found():
|
||||||
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file2.py'),
|
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file2.py'),
|
||||||
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file3.py'),
|
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file3.py'),
|
||||||
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file4.py')]), \
|
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file4.py')]), \
|
||||||
patch('openlp.core.common.importlib.import_module') as mocked_import_module:
|
patch('openlp.core.common.import_openlp_module') as mocked_import_module:
|
||||||
|
|
||||||
# WHEN: Calling `extension_loader` with a list of files to exclude
|
# WHEN: Calling `extension_loader` with a list of files to exclude
|
||||||
extension_loader('glob', ['file2.py', 'file3.py'])
|
extension_loader('glob', ['file2.py', 'file3.py'])
|
||||||
|
@ -97,7 +97,7 @@ def test_extension_loader_import_error():
|
||||||
return_value=Path('/', 'app', 'dir', 'openlp')), \
|
return_value=Path('/', 'app', 'dir', 'openlp')), \
|
||||||
patch.object(Path, 'glob', return_value=[
|
patch.object(Path, 'glob', return_value=[
|
||||||
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file1.py')]), \
|
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file1.py')]), \
|
||||||
patch('openlp.core.common.importlib.import_module', side_effect=ImportError()), \
|
patch('openlp.core.common.import_openlp_module', side_effect=ImportError()), \
|
||||||
patch('openlp.core.common.log') as mocked_logger:
|
patch('openlp.core.common.log') as mocked_logger:
|
||||||
|
|
||||||
# WHEN: Calling `extension_loader`
|
# WHEN: Calling `extension_loader`
|
||||||
|
@ -116,7 +116,7 @@ def test_extension_loader_os_error():
|
||||||
return_value=Path('/', 'app', 'dir', 'openlp')), \
|
return_value=Path('/', 'app', 'dir', 'openlp')), \
|
||||||
patch.object(Path, 'glob', return_value=[
|
patch.object(Path, 'glob', return_value=[
|
||||||
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file1.py')]), \
|
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file1.py')]), \
|
||||||
patch('openlp.core.common.importlib.import_module', side_effect=OSError()), \
|
patch('openlp.core.common.import_openlp_module', side_effect=OSError()), \
|
||||||
patch('openlp.core.common.log') as mocked_logger:
|
patch('openlp.core.common.log') as mocked_logger:
|
||||||
|
|
||||||
# WHEN: Calling `extension_loader`
|
# WHEN: Calling `extension_loader`
|
||||||
|
|
|
@ -273,7 +273,7 @@ def test_image_to_byte_base_64():
|
||||||
assert 'byte_array base64ified' == result, 'The result should be the return value of the mocked base64 method'
|
assert 'byte_array base64ified' == result, 'The result should be the return value of the mocked base64 method'
|
||||||
|
|
||||||
|
|
||||||
def test_create_thumb_with_size():
|
def test_create_thumb_with_size(registry):
|
||||||
"""
|
"""
|
||||||
Test the create_thumb() function with a given size.
|
Test the create_thumb() function with a given size.
|
||||||
"""
|
"""
|
||||||
|
@ -308,7 +308,7 @@ def test_create_thumb_with_size():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def test_create_thumb_no_size():
|
def test_create_thumb_no_size(registry):
|
||||||
"""
|
"""
|
||||||
Test the create_thumb() function with no size specified.
|
Test the create_thumb() function with no size specified.
|
||||||
"""
|
"""
|
||||||
|
@ -343,7 +343,7 @@ def test_create_thumb_no_size():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def test_create_thumb_invalid_size():
|
def test_create_thumb_invalid_size(registry):
|
||||||
"""
|
"""
|
||||||
Test the create_thumb() function with invalid size specified.
|
Test the create_thumb() function with invalid size specified.
|
||||||
"""
|
"""
|
||||||
|
@ -379,7 +379,7 @@ def test_create_thumb_invalid_size():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def test_create_thumb_width_only():
|
def test_create_thumb_width_only(registry):
|
||||||
"""
|
"""
|
||||||
Test the create_thumb() function with a size of only width specified.
|
Test the create_thumb() function with a size of only width specified.
|
||||||
"""
|
"""
|
||||||
|
@ -415,7 +415,7 @@ def test_create_thumb_width_only():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def test_create_thumb_height_only():
|
def test_create_thumb_height_only(registry):
|
||||||
"""
|
"""
|
||||||
Test the create_thumb() function with a size of only height specified.
|
Test the create_thumb() function with a size of only height specified.
|
||||||
"""
|
"""
|
||||||
|
@ -451,7 +451,7 @@ def test_create_thumb_height_only():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def test_create_thumb_empty_img():
|
def test_create_thumb_empty_img(registry):
|
||||||
"""
|
"""
|
||||||
Test the create_thumb() function with a size of only height specified.
|
Test the create_thumb() function with a size of only height specified.
|
||||||
"""
|
"""
|
||||||
|
@ -502,7 +502,7 @@ def test_create_thumb_empty_img():
|
||||||
|
|
||||||
@patch('openlp.core.lib.QtGui.QImageReader')
|
@patch('openlp.core.lib.QtGui.QImageReader')
|
||||||
@patch('openlp.core.lib.build_icon')
|
@patch('openlp.core.lib.build_icon')
|
||||||
def test_create_thumb_path_fails(mocked_build_icon, MockQImageReader):
|
def test_create_thumb_path_fails(mocked_build_icon, MockQImageReader, registry):
|
||||||
"""
|
"""
|
||||||
Test that build_icon() is run against the image_path when the thumbnail fails to be created
|
Test that build_icon() is run against the image_path when the thumbnail fails to be created
|
||||||
"""
|
"""
|
||||||
|
@ -537,7 +537,7 @@ def test_check_item_selected_true():
|
||||||
assert result is True, 'The result should be True'
|
assert result is True, 'The result should be True'
|
||||||
|
|
||||||
|
|
||||||
def test_check_item_selected_false():
|
def test_check_item_selected_false(registry):
|
||||||
"""
|
"""
|
||||||
Test that the check_item_selected() function returns False when there are no selected indexes.
|
Test that the check_item_selected() function returns False when there are no selected indexes.
|
||||||
"""
|
"""
|
||||||
|
@ -608,7 +608,7 @@ def test_validate_thumb_file_exists_and_older():
|
||||||
assert result is False, 'The result should be False'
|
assert result is False, 'The result should be False'
|
||||||
|
|
||||||
|
|
||||||
def test_resize_thumb():
|
def test_resize_thumb(registry):
|
||||||
"""
|
"""
|
||||||
Test the resize_thumb() function
|
Test the resize_thumb() function
|
||||||
"""
|
"""
|
||||||
|
@ -630,7 +630,7 @@ def test_resize_thumb():
|
||||||
assert image.pixel(0, 0) == wanted_background_rgb, 'The background should be white.'
|
assert image.pixel(0, 0) == wanted_background_rgb, 'The background should be white.'
|
||||||
|
|
||||||
|
|
||||||
def test_resize_thumb_ignoring_aspect_ratio():
|
def test_resize_thumb_ignoring_aspect_ratio(registry):
|
||||||
"""
|
"""
|
||||||
Test the resize_thumb() function ignoring aspect ratio
|
Test the resize_thumb() function ignoring aspect ratio
|
||||||
"""
|
"""
|
||||||
|
@ -652,7 +652,7 @@ def test_resize_thumb_ignoring_aspect_ratio():
|
||||||
assert image.pixel(0, 0) == wanted_background_rgb, 'The background should be white.'
|
assert image.pixel(0, 0) == wanted_background_rgb, 'The background should be white.'
|
||||||
|
|
||||||
|
|
||||||
def test_resize_thumb_width_aspect_ratio():
|
def test_resize_thumb_width_aspect_ratio(registry):
|
||||||
"""
|
"""
|
||||||
Test the resize_thumb() function using the image's width as the reference
|
Test the resize_thumb() function using the image's width as the reference
|
||||||
"""
|
"""
|
||||||
|
@ -670,7 +670,7 @@ def test_resize_thumb_width_aspect_ratio():
|
||||||
assert wanted_width == result_size.width(), 'The image should have the requested width.'
|
assert wanted_width == result_size.width(), 'The image should have the requested width.'
|
||||||
|
|
||||||
|
|
||||||
def test_resize_thumb_same_aspect_ratio():
|
def test_resize_thumb_same_aspect_ratio(registry):
|
||||||
"""
|
"""
|
||||||
Test the resize_thumb() function when the image and the wanted aspect ratio are the same
|
Test the resize_thumb() function when the image and the wanted aspect ratio are the same
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -862,7 +862,7 @@ def test_to_dict_text_item(mocked_sha256_file_hash, state_media, settings, servi
|
||||||
],
|
],
|
||||||
'theme': None,
|
'theme': None,
|
||||||
'title': 'Amazing Grace',
|
'title': 'Amazing Grace',
|
||||||
'type': 'ServiceItemType.Text',
|
'type': ServiceItemType.Text,
|
||||||
'data': {'authors': 'John Newton', 'title': 'amazing grace@'}
|
'data': {'authors': 'John Newton', 'title': 'amazing grace@'}
|
||||||
}
|
}
|
||||||
assert result == expected_dict
|
assert result == expected_dict
|
||||||
|
@ -905,7 +905,7 @@ def test_to_dict_image_item(state_media, settings, service_item_env):
|
||||||
],
|
],
|
||||||
'theme': -1,
|
'theme': -1,
|
||||||
'title': 'Images',
|
'title': 'Images',
|
||||||
'type': 'ServiceItemType.Image',
|
'type': ServiceItemType.Image,
|
||||||
'data': {}
|
'data': {}
|
||||||
}
|
}
|
||||||
assert result == expected_dict
|
assert result == expected_dict
|
||||||
|
@ -961,7 +961,7 @@ def test_to_dict_presentation_item(mocked_image_uri, mocked_get_data_path, state
|
||||||
],
|
],
|
||||||
'theme': None,
|
'theme': None,
|
||||||
'title': '',
|
'title': '',
|
||||||
'type': 'ServiceItemType.Command',
|
'type': ServiceItemType.Command,
|
||||||
'data': {}
|
'data': {}
|
||||||
}
|
}
|
||||||
assert result == expected_dict
|
assert result == expected_dict
|
||||||
|
|
|
@ -22,14 +22,18 @@
|
||||||
Package to test the openlp.core.ui.media package.
|
Package to test the openlp.core.ui.media package.
|
||||||
"""
|
"""
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from unittest import skipUnless
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from openlp.core.state import State
|
||||||
|
from openlp.core.common.platform import is_linux, is_macosx
|
||||||
from openlp.core.common.registry import Registry
|
from openlp.core.common.registry import Registry
|
||||||
from openlp.core.ui import DisplayControllerType, HideMode
|
from openlp.core.ui import DisplayControllerType, HideMode
|
||||||
from openlp.core.ui.media.mediacontroller import MediaController
|
from openlp.core.ui.media.mediacontroller import MediaController
|
||||||
from openlp.core.ui.media import ItemMediaInfo, MediaState
|
from openlp.core.ui.media import ItemMediaInfo, MediaState, MediaType
|
||||||
|
|
||||||
from tests.utils.constants import RESOURCE_PATH
|
from tests.utils.constants import RESOURCE_PATH
|
||||||
|
|
||||||
|
@ -46,6 +50,216 @@ def media_env(registry):
|
||||||
yield media_controller
|
yield media_controller
|
||||||
|
|
||||||
|
|
||||||
|
@patch('openlp.core.ui.media.mediacontroller.register_views')
|
||||||
|
def test_setup(mocked_register_views, media_env):
|
||||||
|
"""
|
||||||
|
Test that the setup method is called correctly
|
||||||
|
"""
|
||||||
|
# GIVEN: A media controller, and function list
|
||||||
|
expected_functions_list = ['bootstrap_initialise', 'bootstrap_post_set_up', 'bootstrap_completion',
|
||||||
|
'playbackPlay', 'playbackPause', 'playbackStop', 'playbackLoop', 'seek_slider',
|
||||||
|
'volume_slider', 'media_hide', 'media_blank', 'media_unblank', 'songs_hide',
|
||||||
|
'songs_blank', 'songs_unblank']
|
||||||
|
# WHEN: Setup is called
|
||||||
|
media_env.setup()
|
||||||
|
# THEN: the functions should be defined along with the api blueprint
|
||||||
|
assert list(Registry().functions_list.keys()) == expected_functions_list, \
|
||||||
|
f'The function list should have been {(Registry().functions_list.keys())}'
|
||||||
|
mocked_register_views.called_once(), 'The media blueprint has not been registered'
|
||||||
|
|
||||||
|
|
||||||
|
def test_initialise_good(media_env, state_media):
|
||||||
|
"""
|
||||||
|
Test that the bootstrap initialise method is called correctly
|
||||||
|
"""
|
||||||
|
# GIVEN: a mocked setup
|
||||||
|
with patch.object(media_env.media_controller, u'setup') as mocked_setup:
|
||||||
|
# THEN: The underlying method is called
|
||||||
|
media_env.media_controller.bootstrap_initialise()
|
||||||
|
# THEN: The following should have happened
|
||||||
|
mocked_setup.called_once(), 'The setup function has been called'
|
||||||
|
|
||||||
|
|
||||||
|
def test_initialise_missing_vlc(media_env, state_media):
|
||||||
|
"""
|
||||||
|
Test that the bootstrap initialise method is called correctly with no VLC
|
||||||
|
"""
|
||||||
|
# GIVEN: a mocked setup and no VLC
|
||||||
|
with patch.object(media_env.media_controller, u'setup') as mocked_setup, \
|
||||||
|
patch('openlp.core.ui.media.mediacontroller.get_vlc', return_value=False):
|
||||||
|
# THEN: The underlying method is called
|
||||||
|
media_env.media_controller.bootstrap_initialise()
|
||||||
|
# THEN: The following should have happened
|
||||||
|
mocked_setup.called_once(), 'The setup function has been called'
|
||||||
|
text = State().get_text()
|
||||||
|
if not is_macosx():
|
||||||
|
assert text.find("python3-vlc") > 0, "VLC should not be missing"
|
||||||
|
|
||||||
|
|
||||||
|
@patch('openlp.core.ui.media.mediacontroller.pymediainfo_available', False)
|
||||||
|
def test_initialise_missing_pymedia(media_env, state_media):
|
||||||
|
"""
|
||||||
|
Test that the bootstrap initialise method is called correctly with no pymediainfo
|
||||||
|
"""
|
||||||
|
# GIVEN: a mocked setup and no pymedia
|
||||||
|
with patch.object(media_env.media_controller, u'setup') as mocked_setup:
|
||||||
|
# THEN: The underlying method is called
|
||||||
|
media_env.media_controller.bootstrap_initialise()
|
||||||
|
# THEN: The following should have happened
|
||||||
|
mocked_setup.called_once(), 'The setup function has been called'
|
||||||
|
text = State().get_text()
|
||||||
|
if not is_macosx():
|
||||||
|
assert text.find("python3-pymediainfo") > 0, "PyMedia should not be missing"
|
||||||
|
|
||||||
|
|
||||||
|
@skipUnless(is_linux(), "Linux only")
|
||||||
|
def test_initialise_missing_pymedia_fedora(media_env, state_media):
|
||||||
|
"""
|
||||||
|
Test that the bootstrap initialise method is called correctly with no VLC
|
||||||
|
"""
|
||||||
|
# GIVEN: a mocked setup and no VLC
|
||||||
|
with patch.object(media_env.media_controller, u'setup') as mocked_setup, \
|
||||||
|
patch('openlp.core.ui.media.mediacontroller.get_vlc', return_value=False), \
|
||||||
|
patch('openlp.core.ui.media.mediacontroller.is_linux', return_value=True):
|
||||||
|
# WHEN: The underlying method is called
|
||||||
|
media_env.media_controller.bootstrap_initialise()
|
||||||
|
# THEN: The following should have happened
|
||||||
|
mocked_setup.called_once(), 'The setup function has been called'
|
||||||
|
text = State().get_text()
|
||||||
|
assert text.find("python3-pymediainfo") == -1, "PyMedia should be missing"
|
||||||
|
assert text.find("python3-vlc") > 0, "VLC should not be missing"
|
||||||
|
assert text.find("rpmfusion") > 0, "RPMFusion should provide the modules"
|
||||||
|
|
||||||
|
|
||||||
|
@skipUnless(is_linux(), "Linux only")
|
||||||
|
def test_initialise_missing_pymedia_not_fedora(media_env, state_media):
|
||||||
|
"""
|
||||||
|
Test that the bootstrap initialise method is called correctly with no VLC
|
||||||
|
"""
|
||||||
|
# GIVEN: a mocked setup and no VLC
|
||||||
|
with patch.object(media_env.media_controller, u'setup') as mocked_setup, \
|
||||||
|
patch('openlp.core.ui.media.mediacontroller.get_vlc', return_value=False), \
|
||||||
|
patch('openlp.core.ui.media.mediacontroller.is_linux', return_value=False):
|
||||||
|
# WHEN: The underlying method is called
|
||||||
|
media_env.media_controller.bootstrap_initialise()
|
||||||
|
# THEN: The following should have happened
|
||||||
|
mocked_setup.called_once(), 'The setup function has been called'
|
||||||
|
text = State().get_text()
|
||||||
|
assert text.find("python3-pymediainfo") == -1, "PyMedia should be missing"
|
||||||
|
assert text.find("python3-vlc") > 0, "VLC should not be missing"
|
||||||
|
assert text.find("rpmfusion") == -1, "RPMFusion should not provide the modules"
|
||||||
|
|
||||||
|
|
||||||
|
def test_initialise_missing_pymedia_mac_os(media_env, state_media):
|
||||||
|
"""
|
||||||
|
Test that the bootstrap initialise method is called correctly with no VLC
|
||||||
|
"""
|
||||||
|
# GIVEN: a mocked setup and no VLC
|
||||||
|
with patch.object(media_env.media_controller, u'setup') as mocked_setup, \
|
||||||
|
patch('openlp.core.ui.media.mediacontroller.get_vlc', return_value=False), \
|
||||||
|
patch('openlp.core.ui.media.mediacontroller.is_macosx', return_value=True):
|
||||||
|
# WHEN: The underlying method is called
|
||||||
|
media_env.media_controller.bootstrap_initialise()
|
||||||
|
# THEN: The following should have happened
|
||||||
|
mocked_setup.called_once(), 'The setup function has been called'
|
||||||
|
text = State().get_text()
|
||||||
|
assert text.find("python3-pymediainfo") == -1, "PyMedia should be missing"
|
||||||
|
assert text.find("python3-vlc") == -1, "PyMedia should be missing"
|
||||||
|
assert text.find("videolan") > 0, "VideoLAN should provide the modules"
|
||||||
|
|
||||||
|
|
||||||
|
def test_post_set_up_good(media_env, state_media):
|
||||||
|
"""
|
||||||
|
Test the Bootstrap post set up assuming all functions are good
|
||||||
|
"""
|
||||||
|
# GIVEN: A working environment
|
||||||
|
media_env.vlc_live_media_stop = MagicMock()
|
||||||
|
media_env.vlc_preview_media_stop = MagicMock()
|
||||||
|
media_env.vlc_live_media_tick = MagicMock()
|
||||||
|
media_env.vlc_preview_media_tick = MagicMock()
|
||||||
|
State().add_service("mediacontroller", 0)
|
||||||
|
State().update_pre_conditions("mediacontroller", True)
|
||||||
|
# WHEN: I call the function
|
||||||
|
with patch.object(media_env.media_controller, u'setup_display') as mocked_display:
|
||||||
|
media_env.bootstrap_post_set_up()
|
||||||
|
# THEN: the environment is set up correctly
|
||||||
|
assert mocked_display.call_count == 2, "Should have been called twice"
|
||||||
|
text = State().get_text()
|
||||||
|
assert text.find("No Displays") == -1, "No Displays have been disable"
|
||||||
|
assert mocked_display.has_calls(None, False) # Live Controller
|
||||||
|
assert mocked_display.has_calls(None, True) # Preview Controller
|
||||||
|
|
||||||
|
|
||||||
|
def test_media_state_live(media_env, state_media):
|
||||||
|
"""
|
||||||
|
Test the Bootstrap post set up assuming all functions are good
|
||||||
|
"""
|
||||||
|
# GIVEN: A working environment
|
||||||
|
media_env.vlc_live_media_stop = MagicMock()
|
||||||
|
media_env.vlc_preview_media_stop = MagicMock()
|
||||||
|
media_env.vlc_preview_media_tick = MagicMock()
|
||||||
|
mocked_live_controller = MagicMock()
|
||||||
|
mocked_live_controller.is_live = True
|
||||||
|
mocked_live_controller.media_info.media_type = MediaType.Audio
|
||||||
|
Registry().register('live_controller', mocked_live_controller)
|
||||||
|
media_env.media_controller.vlc_player = MagicMock()
|
||||||
|
media_env.media_controller._display_controllers = MagicMock(return_value=mocked_live_controller)
|
||||||
|
# WHEN: I call the function
|
||||||
|
with patch.object(media_env.media_controller, u'setup_display') as mocked_display:
|
||||||
|
media_env.bootstrap_post_set_up()
|
||||||
|
# THEN: the environment is set up correctly
|
||||||
|
text = State().get_text()
|
||||||
|
assert text.find("No Displays") == -1, "No Displays have been disable"
|
||||||
|
assert mocked_display.has_calls(None, False) # Live Controller
|
||||||
|
assert mocked_display.has_calls(None, True) # Preview Controller
|
||||||
|
|
||||||
|
|
||||||
|
def test_post_set_up_no_controller(media_env, state_media):
|
||||||
|
"""
|
||||||
|
Test the Bootstrap post set up assuming all functions are good
|
||||||
|
"""
|
||||||
|
# GIVEN: A working environment
|
||||||
|
media_env.vlc_live_media_stop = MagicMock()
|
||||||
|
media_env.vlc_preview_media_stop = MagicMock()
|
||||||
|
media_env.vlc_live_media_tick = MagicMock()
|
||||||
|
media_env.vlc_preview_media_tick = MagicMock()
|
||||||
|
State().add_service("mediacontroller", 0)
|
||||||
|
State().update_pre_conditions("mediacontroller", False)
|
||||||
|
# WHEN: I call the function
|
||||||
|
with patch.object(media_env.media_controller, u'setup_display') as mocked_display:
|
||||||
|
media_env.bootstrap_post_set_up()
|
||||||
|
# THEN: the environment is set up correctly
|
||||||
|
assert mocked_display.call_count == 0, "Should have not have been called twice"
|
||||||
|
text = State().get_text()
|
||||||
|
assert text.find("No Displays") == -1, "No Displays have been disable"
|
||||||
|
|
||||||
|
|
||||||
|
def test_post_set_up_controller_exception(media_env, state_media):
|
||||||
|
"""
|
||||||
|
Test the Bootstrap post set up assuming all functions are good
|
||||||
|
"""
|
||||||
|
# GIVEN: A working environment
|
||||||
|
media_env.vlc_live_media_stop = MagicMock()
|
||||||
|
media_env.vlc_preview_media_stop = MagicMock()
|
||||||
|
media_env.vlc_live_media_tick = MagicMock()
|
||||||
|
media_env.vlc_preview_media_tick = MagicMock()
|
||||||
|
State().add_service("mediacontroller", 0)
|
||||||
|
State().update_pre_conditions("mediacontroller", True)
|
||||||
|
State().add_service("media_live", 0)
|
||||||
|
State().update_pre_conditions("media_live", True)
|
||||||
|
# WHEN: I call the function
|
||||||
|
with patch.object(media_env.media_controller, u'setup_display') as mocked_display:
|
||||||
|
mocked_display.side_effect = AttributeError()
|
||||||
|
try:
|
||||||
|
media_env.bootstrap_post_set_up()
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
# THEN: the environment is set up correctly
|
||||||
|
text = State().get_text()
|
||||||
|
assert text.find("Displays") > 0, "Displays have been disable"
|
||||||
|
assert mocked_display.call_count == 2, "Should have been called twice"
|
||||||
|
|
||||||
|
|
||||||
def test_resize(media_env):
|
def test_resize(media_env):
|
||||||
"""
|
"""
|
||||||
Test that the resize method is called correctly
|
Test that the resize method is called correctly
|
||||||
|
@ -86,7 +300,6 @@ def test_load_video(media_env, settings):
|
||||||
# The video should have autoplayed
|
# The video should have autoplayed
|
||||||
# The controls should have been made visible
|
# The controls should have been made visible
|
||||||
media_env.media_controller.media_reset.assert_called_once_with(mocked_slide_controller)
|
media_env.media_controller.media_reset.assert_called_once_with(mocked_slide_controller)
|
||||||
assert mocked_slide_controller.media_info.volume == 1
|
|
||||||
media_env.media_controller.media_play.assert_called_once_with(mocked_slide_controller, False)
|
media_env.media_controller.media_play.assert_called_once_with(mocked_slide_controller, False)
|
||||||
media_env.media_controller.set_controls_visible.assert_called_once_with(mocked_slide_controller, True)
|
media_env.media_controller.set_controls_visible.assert_called_once_with(mocked_slide_controller, True)
|
||||||
|
|
||||||
|
|
|
@ -283,7 +283,7 @@ def test_check_not_available(mocked_get_vlc):
|
||||||
|
|
||||||
@patch('openlp.core.ui.media.vlcplayer.get_vlc')
|
@patch('openlp.core.ui.media.vlcplayer.get_vlc')
|
||||||
@patch('openlp.core.ui.media.vlcplayer.os.path.normcase')
|
@patch('openlp.core.ui.media.vlcplayer.os.path.normcase')
|
||||||
def test_load(mocked_normcase, mocked_get_vlc):
|
def test_load(mocked_normcase, mocked_get_vlc, settings):
|
||||||
"""
|
"""
|
||||||
Test loading a video into VLC
|
Test loading a video into VLC
|
||||||
"""
|
"""
|
||||||
|
@ -294,7 +294,6 @@ def test_load(mocked_normcase, mocked_get_vlc):
|
||||||
mocked_get_vlc.return_value = mocked_vlc
|
mocked_get_vlc.return_value = mocked_vlc
|
||||||
mocked_display = MagicMock()
|
mocked_display = MagicMock()
|
||||||
mocked_controller = MagicMock()
|
mocked_controller = MagicMock()
|
||||||
mocked_controller.media_info.volume = 100
|
|
||||||
mocked_controller.media_info.media_type = MediaType.Video
|
mocked_controller.media_info.media_type = MediaType.Video
|
||||||
mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path
|
mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path
|
||||||
mocked_vlc_media = MagicMock()
|
mocked_vlc_media = MagicMock()
|
||||||
|
@ -305,8 +304,7 @@ def test_load(mocked_normcase, mocked_get_vlc):
|
||||||
vlc_player = VlcPlayer(None)
|
vlc_player = VlcPlayer(None)
|
||||||
|
|
||||||
# WHEN: A video is loaded into VLC
|
# WHEN: A video is loaded into VLC
|
||||||
with patch.object(vlc_player, 'volume') as mocked_volume:
|
result = vlc_player.load(mocked_controller, mocked_display, media_path)
|
||||||
result = vlc_player.load(mocked_controller, mocked_display, media_path)
|
|
||||||
|
|
||||||
# THEN: The video should be loaded
|
# THEN: The video should be loaded
|
||||||
mocked_normcase.assert_called_with(media_path)
|
mocked_normcase.assert_called_with(media_path)
|
||||||
|
@ -314,14 +312,13 @@ def test_load(mocked_normcase, mocked_get_vlc):
|
||||||
assert mocked_vlc_media == mocked_controller.vlc_media
|
assert mocked_vlc_media == mocked_controller.vlc_media
|
||||||
mocked_controller.vlc_media_player.set_media.assert_called_with(mocked_vlc_media)
|
mocked_controller.vlc_media_player.set_media.assert_called_with(mocked_vlc_media)
|
||||||
mocked_vlc_media.parse.assert_called_with()
|
mocked_vlc_media.parse.assert_called_with()
|
||||||
mocked_volume.assert_called_with(mocked_controller, 100)
|
|
||||||
assert result is True
|
assert result is True
|
||||||
|
|
||||||
|
|
||||||
@patch('openlp.core.ui.media.vlcplayer.is_win')
|
@patch('openlp.core.ui.media.vlcplayer.is_win')
|
||||||
@patch('openlp.core.ui.media.vlcplayer.get_vlc')
|
@patch('openlp.core.ui.media.vlcplayer.get_vlc')
|
||||||
@patch('openlp.core.ui.media.vlcplayer.os.path.normcase')
|
@patch('openlp.core.ui.media.vlcplayer.os.path.normcase')
|
||||||
def test_load_audio_cd(mocked_normcase, mocked_get_vlc, mocked_is_win):
|
def test_load_audio_cd(mocked_normcase, mocked_get_vlc, mocked_is_win, settings):
|
||||||
"""
|
"""
|
||||||
Test loading an audio CD into VLC
|
Test loading an audio CD into VLC
|
||||||
"""
|
"""
|
||||||
|
@ -333,7 +330,6 @@ def test_load_audio_cd(mocked_normcase, mocked_get_vlc, mocked_is_win):
|
||||||
mocked_get_vlc.return_value = mocked_vlc
|
mocked_get_vlc.return_value = mocked_vlc
|
||||||
mocked_display = MagicMock()
|
mocked_display = MagicMock()
|
||||||
mocked_controller = MagicMock()
|
mocked_controller = MagicMock()
|
||||||
mocked_controller.media_info.volume = 100
|
|
||||||
mocked_controller.media_info.media_type = MediaType.CD
|
mocked_controller.media_info.media_type = MediaType.CD
|
||||||
mocked_controller.media_info.title_track = 1
|
mocked_controller.media_info.title_track = 1
|
||||||
mocked_vlc_media = MagicMock()
|
mocked_vlc_media = MagicMock()
|
||||||
|
@ -351,8 +347,7 @@ def test_load_audio_cd(mocked_normcase, mocked_get_vlc, mocked_is_win):
|
||||||
vlc_player = VlcPlayer(None)
|
vlc_player = VlcPlayer(None)
|
||||||
|
|
||||||
# WHEN: An audio CD is loaded into VLC
|
# WHEN: An audio CD is loaded into VLC
|
||||||
with patch.object(vlc_player, 'volume') as mocked_volume, \
|
with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait:
|
||||||
patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait:
|
|
||||||
result = vlc_player.load(mocked_controller, mocked_display, media_path)
|
result = vlc_player.load(mocked_controller, mocked_display, media_path)
|
||||||
|
|
||||||
# THEN: The video should be loaded
|
# THEN: The video should be loaded
|
||||||
|
@ -361,7 +356,6 @@ def test_load_audio_cd(mocked_normcase, mocked_get_vlc, mocked_is_win):
|
||||||
assert mocked_vlc_media == mocked_controller.vlc_media
|
assert mocked_vlc_media == mocked_controller.vlc_media
|
||||||
mocked_controller.vlc_media_player.set_media.assert_called_with(mocked_vlc_media)
|
mocked_controller.vlc_media_player.set_media.assert_called_with(mocked_vlc_media)
|
||||||
mocked_vlc_media.parse.assert_called_with()
|
mocked_vlc_media.parse.assert_called_with()
|
||||||
mocked_volume.assert_called_with(mocked_controller, 100)
|
|
||||||
mocked_media_state_wait.assert_called_with(mocked_controller, ANY)
|
mocked_media_state_wait.assert_called_with(mocked_controller, ANY)
|
||||||
mocked_controller.seek_slider.setMinimum.assert_called_with(20000)
|
mocked_controller.seek_slider.setMinimum.assert_called_with(20000)
|
||||||
mocked_controller.seek_slider.setMaximum.assert_called_with(30000)
|
mocked_controller.seek_slider.setMaximum.assert_called_with(30000)
|
||||||
|
@ -371,7 +365,7 @@ def test_load_audio_cd(mocked_normcase, mocked_get_vlc, mocked_is_win):
|
||||||
@patch('openlp.core.ui.media.vlcplayer.is_win')
|
@patch('openlp.core.ui.media.vlcplayer.is_win')
|
||||||
@patch('openlp.core.ui.media.vlcplayer.get_vlc')
|
@patch('openlp.core.ui.media.vlcplayer.get_vlc')
|
||||||
@patch('openlp.core.ui.media.vlcplayer.os.path.normcase')
|
@patch('openlp.core.ui.media.vlcplayer.os.path.normcase')
|
||||||
def test_load_audio_cd_on_windows(mocked_normcase, mocked_get_vlc, mocked_is_win):
|
def test_load_audio_cd_on_windows(mocked_normcase, mocked_get_vlc, mocked_is_win, settings):
|
||||||
"""
|
"""
|
||||||
Test loading an audio CD into VLC on Windows
|
Test loading an audio CD into VLC on Windows
|
||||||
"""
|
"""
|
||||||
|
@ -383,7 +377,6 @@ def test_load_audio_cd_on_windows(mocked_normcase, mocked_get_vlc, mocked_is_win
|
||||||
mocked_get_vlc.return_value = mocked_vlc
|
mocked_get_vlc.return_value = mocked_vlc
|
||||||
mocked_display = MagicMock()
|
mocked_display = MagicMock()
|
||||||
mocked_controller = MagicMock()
|
mocked_controller = MagicMock()
|
||||||
mocked_controller.media_info.volume = 100
|
|
||||||
mocked_controller.media_info.media_type = MediaType.CD
|
mocked_controller.media_info.media_type = MediaType.CD
|
||||||
mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path
|
mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path
|
||||||
mocked_controller.media_info.title_track = 1
|
mocked_controller.media_info.title_track = 1
|
||||||
|
@ -400,8 +393,7 @@ def test_load_audio_cd_on_windows(mocked_normcase, mocked_get_vlc, mocked_is_win
|
||||||
vlc_player = VlcPlayer(None)
|
vlc_player = VlcPlayer(None)
|
||||||
|
|
||||||
# WHEN: An audio CD is loaded into VLC
|
# WHEN: An audio CD is loaded into VLC
|
||||||
with patch.object(vlc_player, 'volume') as mocked_volume, \
|
with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait:
|
||||||
patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait:
|
|
||||||
result = vlc_player.load(mocked_controller, mocked_display, media_path)
|
result = vlc_player.load(mocked_controller, mocked_display, media_path)
|
||||||
|
|
||||||
# THEN: The video should be loaded
|
# THEN: The video should be loaded
|
||||||
|
@ -410,7 +402,6 @@ def test_load_audio_cd_on_windows(mocked_normcase, mocked_get_vlc, mocked_is_win
|
||||||
assert mocked_vlc_media == mocked_controller.vlc_media
|
assert mocked_vlc_media == mocked_controller.vlc_media
|
||||||
mocked_controller.vlc_media_player.set_media.assert_called_with(mocked_vlc_media)
|
mocked_controller.vlc_media_player.set_media.assert_called_with(mocked_vlc_media)
|
||||||
mocked_vlc_media.parse.assert_called_with()
|
mocked_vlc_media.parse.assert_called_with()
|
||||||
mocked_volume.assert_called_with(mocked_controller, 100)
|
|
||||||
mocked_media_state_wait.assert_called_with(mocked_controller, ANY)
|
mocked_media_state_wait.assert_called_with(mocked_controller, ANY)
|
||||||
assert result is True
|
assert result is True
|
||||||
|
|
||||||
|
@ -449,7 +440,7 @@ def test_load_audio_cd_no_tracks(mocked_normcase, mocked_get_vlc, mocked_is_win)
|
||||||
vlc_player = VlcPlayer(None)
|
vlc_player = VlcPlayer(None)
|
||||||
|
|
||||||
# WHEN: An audio CD is loaded into VLC
|
# WHEN: An audio CD is loaded into VLC
|
||||||
with patch.object(vlc_player, 'volume'), patch.object(vlc_player, 'media_state_wait'):
|
with patch.object(vlc_player, 'media_state_wait'):
|
||||||
result = vlc_player.load(mocked_controller, mocked_display, media_path)
|
result = vlc_player.load(mocked_controller, mocked_display, media_path)
|
||||||
|
|
||||||
# THEN: The video should be loaded
|
# THEN: The video should be loaded
|
||||||
|
@ -465,7 +456,7 @@ def test_load_audio_cd_no_tracks(mocked_normcase, mocked_get_vlc, mocked_is_win)
|
||||||
@patch('openlp.core.ui.media.vlcplayer.is_win')
|
@patch('openlp.core.ui.media.vlcplayer.is_win')
|
||||||
@patch('openlp.core.ui.media.vlcplayer.get_vlc')
|
@patch('openlp.core.ui.media.vlcplayer.get_vlc')
|
||||||
@patch('openlp.core.ui.media.vlcplayer.os.path.normcase')
|
@patch('openlp.core.ui.media.vlcplayer.os.path.normcase')
|
||||||
def test_load_dvd(mocked_normcase, mocked_get_vlc, mocked_is_win):
|
def test_load_dvd(mocked_normcase, mocked_get_vlc, mocked_is_win, settings):
|
||||||
"""
|
"""
|
||||||
Test loading a DVD into VLC
|
Test loading a DVD into VLC
|
||||||
"""
|
"""
|
||||||
|
@ -477,14 +468,12 @@ def test_load_dvd(mocked_normcase, mocked_get_vlc, mocked_is_win):
|
||||||
mocked_get_vlc.return_value = mocked_vlc
|
mocked_get_vlc.return_value = mocked_vlc
|
||||||
mocked_display = MagicMock()
|
mocked_display = MagicMock()
|
||||||
mocked_controller = MagicMock()
|
mocked_controller = MagicMock()
|
||||||
mocked_controller.media_info.volume = 100
|
|
||||||
mocked_controller.media_info.media_type = MediaType.DVD
|
mocked_controller.media_info.media_type = MediaType.DVD
|
||||||
mocked_controller.media_info.title_track = '2'
|
mocked_controller.media_info.title_track = '2'
|
||||||
mocked_controller.media_info.audio_track = 2
|
mocked_controller.media_info.audio_track = 2
|
||||||
mocked_controller.media_info.subtitle_track = 4
|
mocked_controller.media_info.subtitle_track = 4
|
||||||
mocked_vlc_media = MagicMock()
|
mocked_vlc_media = MagicMock()
|
||||||
mocked_media = MagicMock()
|
mocked_media = MagicMock()
|
||||||
mocked_controller.media_info.volume = 100
|
|
||||||
mocked_controller.media_info.start_time = 20000
|
mocked_controller.media_info.start_time = 20000
|
||||||
mocked_controller.media_info.end_time = 30000
|
mocked_controller.media_info.end_time = 30000
|
||||||
mocked_controller.media_info.length = 10000
|
mocked_controller.media_info.length = 10000
|
||||||
|
@ -493,8 +482,7 @@ def test_load_dvd(mocked_normcase, mocked_get_vlc, mocked_is_win):
|
||||||
vlc_player = VlcPlayer(None)
|
vlc_player = VlcPlayer(None)
|
||||||
|
|
||||||
# WHEN: A DVD clip is loaded into VLC
|
# WHEN: A DVD clip is loaded into VLC
|
||||||
with patch.object(vlc_player, 'volume') as mocked_volume, \
|
with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait:
|
||||||
patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait:
|
|
||||||
result = vlc_player.load(mocked_controller, mocked_display, media_path)
|
result = vlc_player.load(mocked_controller, mocked_display, media_path)
|
||||||
|
|
||||||
# THEN: The video should be loaded
|
# THEN: The video should be loaded
|
||||||
|
@ -505,7 +493,6 @@ def test_load_dvd(mocked_normcase, mocked_get_vlc, mocked_is_win):
|
||||||
mocked_controller.vlc_media_player.audio_set_track.assert_called_with(2)
|
mocked_controller.vlc_media_player.audio_set_track.assert_called_with(2)
|
||||||
mocked_controller.vlc_media_player.video_set_spu.assert_called_with(4)
|
mocked_controller.vlc_media_player.video_set_spu.assert_called_with(4)
|
||||||
mocked_vlc_media.parse.assert_called_with()
|
mocked_vlc_media.parse.assert_called_with()
|
||||||
mocked_volume.assert_called_with(mocked_controller, 100)
|
|
||||||
mocked_media_state_wait.assert_called_with(mocked_controller, ANY)
|
mocked_media_state_wait.assert_called_with(mocked_controller, ANY)
|
||||||
mocked_controller.seek_slider.setMinimum.assert_called_with(20000)
|
mocked_controller.seek_slider.setMinimum.assert_called_with(20000)
|
||||||
mocked_controller.seek_slider.setMaximum.assert_called_with(30000)
|
mocked_controller.seek_slider.setMaximum.assert_called_with(30000)
|
||||||
|
@ -606,7 +593,7 @@ def test_resize():
|
||||||
|
|
||||||
@patch('openlp.core.ui.media.vlcplayer.threading')
|
@patch('openlp.core.ui.media.vlcplayer.threading')
|
||||||
@patch('openlp.core.ui.media.vlcplayer.get_vlc')
|
@patch('openlp.core.ui.media.vlcplayer.get_vlc')
|
||||||
def test_play(mocked_get_vlc, mocked_threading):
|
def test_play(mocked_get_vlc, mocked_threading, settings):
|
||||||
"""
|
"""
|
||||||
Test the play() method
|
Test the play() method
|
||||||
"""
|
"""
|
||||||
|
@ -618,21 +605,17 @@ def test_play(mocked_get_vlc, mocked_threading):
|
||||||
mocked_display = MagicMock()
|
mocked_display = MagicMock()
|
||||||
mocked_controller = MagicMock()
|
mocked_controller = MagicMock()
|
||||||
mocked_media = MagicMock()
|
mocked_media = MagicMock()
|
||||||
mocked_controller.media_info.volume = 100
|
|
||||||
mocked_controller.vlc_media_player.get_media.return_value = mocked_media
|
mocked_controller.vlc_media_player.get_media.return_value = mocked_media
|
||||||
vlc_player = VlcPlayer(None)
|
vlc_player = VlcPlayer(None)
|
||||||
vlc_player.set_state(MediaState.Paused, mocked_controller)
|
vlc_player.set_state(MediaState.Paused, mocked_controller)
|
||||||
|
|
||||||
# WHEN: play() is called
|
# WHEN: play() is called
|
||||||
with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \
|
with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait:
|
||||||
patch.object(vlc_player, 'volume') as mocked_volume:
|
|
||||||
mocked_media_state_wait.return_value = True
|
mocked_media_state_wait.return_value = True
|
||||||
result = vlc_player.play(mocked_controller, mocked_display)
|
result = vlc_player.play(mocked_controller, mocked_display)
|
||||||
|
|
||||||
# THEN: A bunch of things should happen to play the media
|
# THEN: A bunch of things should happen to play the media
|
||||||
mocked_thread.start.assert_called_with()
|
mocked_thread.start.assert_called_with()
|
||||||
mocked_volume.assert_called_with(mocked_controller, 100)
|
|
||||||
|
|
||||||
assert MediaState.Playing == vlc_player.get_live_state()
|
assert MediaState.Playing == vlc_player.get_live_state()
|
||||||
assert result is True, 'The value returned from play() should be True'
|
assert result is True, 'The value returned from play() should be True'
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ exceptionform.MIGRATE_VERSION = 'Migrate Test'
|
||||||
exceptionform.CHARDET_VERSION = 'CHARDET Test'
|
exceptionform.CHARDET_VERSION = 'CHARDET Test'
|
||||||
exceptionform.ENCHANT_VERSION = 'Enchant Test'
|
exceptionform.ENCHANT_VERSION = 'Enchant Test'
|
||||||
exceptionform.MAKO_VERSION = 'Mako Test'
|
exceptionform.MAKO_VERSION = 'Mako Test'
|
||||||
|
exceptionform.ICU_VERSION = 'ICU Test'
|
||||||
exceptionform.VLC_VERSION = 'VLC Test'
|
exceptionform.VLC_VERSION = 'VLC Test'
|
||||||
|
|
||||||
MAIL_ITEM_TEXT = ('**OpenLP Bug Report**\nVersion: Trunk Test\n\n--- Details of the Exception. ---\n\n'
|
MAIL_ITEM_TEXT = ('**OpenLP Bug Report**\nVersion: Trunk Test\n\n--- Details of the Exception. ---\n\n'
|
||||||
|
|
|
@ -26,10 +26,11 @@ import datetime
|
||||||
from unittest.mock import MagicMock, patch, sentinel
|
from unittest.mock import MagicMock, patch, sentinel
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtGui
|
from PyQt5 import QtCore, QtGui
|
||||||
|
from openlp.core.lib.serviceitem import ServiceItem
|
||||||
|
|
||||||
from openlp.core.state import State
|
from openlp.core.state import State
|
||||||
from openlp.core.common.registry import Registry
|
from openlp.core.common.registry import Registry
|
||||||
from openlp.core.lib import ServiceItemAction
|
from openlp.core.lib import ItemCapabilities, ServiceItemAction
|
||||||
from openlp.core.ui import HideMode
|
from openlp.core.ui import HideMode
|
||||||
from openlp.core.ui.slidecontroller import NON_TEXT_MENU, WIDE_MENU, NARROW_MENU, InfoLabel, LiveController, \
|
from openlp.core.ui.slidecontroller import NON_TEXT_MENU, WIDE_MENU, NARROW_MENU, InfoLabel, LiveController, \
|
||||||
PreviewController, SlideController
|
PreviewController, SlideController
|
||||||
|
@ -1141,6 +1142,54 @@ def test_process_item_is_reloading_wont_change_display_hide_mode(mocked_execute,
|
||||||
slide_controller.set_hide_mode.assert_not_called()
|
slide_controller.set_hide_mode.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
@patch.object(Registry, 'execute')
|
||||||
|
def test_process_item_provides_own_theme(mocked_execute, registry, state_media):
|
||||||
|
"""
|
||||||
|
Test that media theme is set when media item is flagged with ProvidesOwnTheme
|
||||||
|
"""
|
||||||
|
# GIVEN: A mocked presentation service item that provides it's own theme, a mocked Registry.execute
|
||||||
|
# and a slide controller with many mocks.
|
||||||
|
mocked_pres_item = MagicMock()
|
||||||
|
mocked_pres_item.name = 'mocked_presentation_item'
|
||||||
|
mocked_pres_item.is_command.return_value = True
|
||||||
|
mocked_pres_item.is_media.return_value = False
|
||||||
|
mocked_pres_item.requires_media.return_value = False
|
||||||
|
mocked_pres_item.is_image.return_value = False
|
||||||
|
mocked_pres_item.is_text.return_value = False
|
||||||
|
# Needed to perform the capability checks
|
||||||
|
mocked_pres_item.is_capable = lambda param: ServiceItem.is_capable(mocked_pres_item, param)
|
||||||
|
mocked_pres_item.from_service = False
|
||||||
|
mocked_pres_item.capabilities = [ItemCapabilities.ProvidesOwnTheme]
|
||||||
|
mocked_pres_item.get_frames.return_value = []
|
||||||
|
mocked_settings = MagicMock()
|
||||||
|
mocked_settings.value.return_value = True
|
||||||
|
mocked_main_window = MagicMock()
|
||||||
|
Registry().register('settings', mocked_settings)
|
||||||
|
Registry().register('main_window', mocked_main_window)
|
||||||
|
Registry().register('media_controller', MagicMock())
|
||||||
|
Registry().register('application', MagicMock())
|
||||||
|
slide_controller = SlideController(None)
|
||||||
|
slide_controller.service_item = mocked_pres_item
|
||||||
|
slide_controller.is_live = False
|
||||||
|
slide_controller.preview_widget = MagicMock()
|
||||||
|
slide_controller.preview_display = MagicMock()
|
||||||
|
slide_controller.enable_tool_bar = MagicMock()
|
||||||
|
slide_controller.slide_selected = MagicMock()
|
||||||
|
slide_controller.on_stop_loop = MagicMock()
|
||||||
|
slide_controller.info_label = MagicMock()
|
||||||
|
slide_controller._set_theme = MagicMock()
|
||||||
|
slide_controller.displays = [MagicMock()]
|
||||||
|
slide_controller.split = 0
|
||||||
|
slide_controller.type_prefix = 'test'
|
||||||
|
slide_controller._current_hide_mode = None
|
||||||
|
|
||||||
|
# WHEN: _process_item is called
|
||||||
|
slide_controller._process_item(mocked_pres_item, 0)
|
||||||
|
|
||||||
|
# THEN: _set_theme should be called once
|
||||||
|
slide_controller._set_theme.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
def test_live_stolen_focus_shortcuts(settings):
|
def test_live_stolen_focus_shortcuts(settings):
|
||||||
"""
|
"""
|
||||||
Test that all the needed shortcuts are available in scenarios where Live has stolen focus.
|
Test that all the needed shortcuts are available in scenarios where Live has stolen focus.
|
||||||
|
|
|
@ -32,8 +32,7 @@ from openlp.plugins.alerts.alertsplugin import AlertsPlugin
|
||||||
def plugin_env(mocked_manager, settings, state, registry):
|
def plugin_env(mocked_manager, settings, state, registry):
|
||||||
"""An instance of the AlertsPlugin"""
|
"""An instance of the AlertsPlugin"""
|
||||||
mocked_manager.return_value = MagicMock()
|
mocked_manager.return_value = MagicMock()
|
||||||
with patch('openlp.plugins.alerts.alertsplugin.register_views'):
|
return AlertsPlugin(), settings
|
||||||
return AlertsPlugin(), settings
|
|
||||||
|
|
||||||
|
|
||||||
def test_plugin_about():
|
def test_plugin_about():
|
||||||
|
@ -77,12 +76,10 @@ def test_alerts_initialise(plugin_env):
|
||||||
plugin = plugin_env[0]
|
plugin = plugin_env[0]
|
||||||
plugin.tools_alert_item = MagicMock()
|
plugin.tools_alert_item = MagicMock()
|
||||||
# WHEN: I request the form
|
# WHEN: I request the form
|
||||||
with patch('openlp.core.common.actions.ActionList') as mocked_actionlist, \
|
with patch('openlp.core.common.actions.ActionList') as mocked_actionlist:
|
||||||
patch('openlp.plugins.alerts.alertsplugin.register_views') as mocked_register_views:
|
|
||||||
plugin.initialise()
|
plugin.initialise()
|
||||||
# THEN: the form is loaded
|
# THEN: the form is loaded
|
||||||
mocked_actionlist.instance.add_action.assert_called_once()
|
mocked_actionlist.instance.add_action.assert_called_once()
|
||||||
mocked_register_views.assert_called_once_with()
|
|
||||||
plugin.tools_alert_item.setVisible.assert_called_once_with(True)
|
plugin.tools_alert_item.setVisible.assert_called_once_with(True)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,14 @@
|
||||||
"""
|
"""
|
||||||
This module contains tests for the plugin class Presentation plugin.
|
This module contains tests for the plugin class Presentation plugin.
|
||||||
"""
|
"""
|
||||||
|
from pathlib import Path
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from openlp.plugins.presentations.presentationplugin import PresentationPlugin
|
from openlp.plugins.presentations.presentationplugin import PresentationPlugin
|
||||||
|
from openlp.plugins.presentations.lib.presentationtab import PresentationTab
|
||||||
|
|
||||||
|
|
||||||
|
TEST_RESOURCES_PATH = Path(__file__) / '..' / '..' / '..' / 'resources'
|
||||||
|
|
||||||
|
|
||||||
def test_plugin_about():
|
def test_plugin_about():
|
||||||
|
@ -34,3 +41,33 @@ def test_plugin_about():
|
||||||
'programs. The choice of available presentation programs is '
|
'programs. The choice of available presentation programs is '
|
||||||
'available to the user in a drop down box.'
|
'available to the user in a drop down box.'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_creaste_settings_tab(qapp, state, registry, settings):
|
||||||
|
"""Test creating the settings tab"""
|
||||||
|
# GIVEN: A Presentations plugin
|
||||||
|
presentations_plugin = PresentationPlugin()
|
||||||
|
|
||||||
|
# WHEN: create_settings_tab is run
|
||||||
|
presentations_plugin.create_settings_tab(None)
|
||||||
|
|
||||||
|
# THEN: A settings tab should have been created
|
||||||
|
assert isinstance(presentations_plugin.settings_tab, PresentationTab)
|
||||||
|
|
||||||
|
|
||||||
|
@patch('openlp.plugins.presentations.presentationplugin.Manager')
|
||||||
|
def test_initialise(MockedManager, state, registry, mock_settings):
|
||||||
|
"""Test that initialising the plugin works correctly"""
|
||||||
|
# GIVEN: Some initial values needed for intialisation and a presentations plugin
|
||||||
|
mock_settings.setValue.side_effect = [None, [str(TEST_RESOURCES_PATH / 'presentations' / 'test.ppt')]]
|
||||||
|
mocked_main_window = MagicMock()
|
||||||
|
registry.register('main_window', mocked_main_window)
|
||||||
|
presentations_plugin = PresentationPlugin()
|
||||||
|
presentations_plugin.media_item = MagicMock()
|
||||||
|
|
||||||
|
# WHEN: initialise() is called
|
||||||
|
presentations_plugin.initialise()
|
||||||
|
|
||||||
|
# THEN: Nothing should break, and everything should be called
|
||||||
|
mock_settings.setValue.assert_called_with('presentations/thumbnail_scheme', 'sha256file')
|
||||||
|
mock_settings.remove.assert_called_once_with('presentations/presentations files')
|
||||||
|
|
|
@ -95,7 +95,9 @@ def test_report_song_list_error_reading(mock_file_dialog, mock_log, registry):
|
||||||
Test that report song list sends an exception if the selected file location is not writable
|
Test that report song list sends an exception if the selected file location is not writable
|
||||||
"""
|
"""
|
||||||
# GIVEN: A mocked file that returns a os error on open
|
# GIVEN: A mocked file that returns a os error on open
|
||||||
def raise_os_error(x):
|
def raise_os_error(mode, encoding):
|
||||||
|
assert mode == 'wt'
|
||||||
|
assert encoding == 'utf8'
|
||||||
raise OSError
|
raise OSError
|
||||||
mock_file = MagicMock()
|
mock_file = MagicMock()
|
||||||
mock_file.open.side_effect = raise_os_error
|
mock_file.open.side_effect = raise_os_error
|
||||||
|
|
Loading…
Reference in New Issue