Merge branch 'features_june_1' into 'master'

Fix up a number of bugs from 2.9.1

Closes #294, #587, #584, and #295

See merge request openlp/openlp!204
This commit is contained in:
Tim Bentley 2020-06-10 20:10:56 +00:00
commit af1dd70589
10 changed files with 75 additions and 87 deletions

View File

@ -57,7 +57,7 @@ def add(plugin_name, id):
@plugins.route('/<plugin>/search') @plugins.route('/<plugin>/search')
@login_required @login_required
def search_view(plugin): def search_view(plugin):
log.debug(f'{plugin}/search search called') log.debug(f'{plugin}/search called')
text = request.args.get('text', '') text = request.args.get('text', '')
result = search(plugin, text) result = search(plugin, text)
return jsonify(result) return jsonify(result)
@ -66,7 +66,7 @@ def search_view(plugin):
@plugins.route('/<plugin>/add', methods=['POST']) @plugins.route('/<plugin>/add', methods=['POST'])
@login_required @login_required
def add_view(plugin): def add_view(plugin):
log.debug(f'{plugin}/add search called') log.debug(f'{plugin}/add called')
data = request.json data = request.json
if not data: if not data:
abort(400) abort(400)
@ -78,7 +78,7 @@ def add_view(plugin):
@plugins.route('/<plugin>/live', methods=['POST']) @plugins.route('/<plugin>/live', methods=['POST'])
@login_required @login_required
def live_view(plugin): def live_view(plugin):
log.debug(f'{plugin}/live search called') log.debug(f'{plugin}/live called')
data = request.json data = request.json
if not data: if not data:
abort(400) abort(400)

View File

@ -120,11 +120,11 @@ class Registry(metaclass=Singleton):
:param args: Parameters to be passed to the function. :param args: Parameters to be passed to the function.
:param kwargs: Parameters to be passed to the function. :param kwargs: Parameters to be passed to the function.
""" """
log.debug(f'Running function {event}')
results = [] results = []
if event in self.functions_list: if event in self.functions_list:
for function in self.functions_list[event]: for function in self.functions_list[event]:
try: try:
log.debug('Running function {} for {}'.format(function, event))
result = function(*args, **kwargs) result = function(*args, **kwargs)
if result is not None: if result is not None:
results.append(result) results.append(result)

View File

@ -573,8 +573,6 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
""" """
process the bootstrap post setup request process the bootstrap post setup request
""" """
# self.preview_controller.panel.setVisible(self.settings.value('user interface/preview panel'))
# self.live_controller.panel.setVisible(self.settings.value('user interface/live panel'))
self.load_settings() self.load_settings()
self.restore_current_media_manager_item() self.restore_current_media_manager_item()
Registry().execute('theme_update_global') Registry().execute('theme_update_global')
@ -637,8 +635,6 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
Show the main form, as well as the display form Show the main form, as well as the display form
""" """
QtWidgets.QWidget.show(self) QtWidgets.QWidget.show(self)
# if self.live_controller.display.isVisible():
# self.live_controller.display.setFocus()
self.activateWindow() self.activateWindow()
# We have -disable-web-security added by our code. # We have -disable-web-security added by our code.
# If a file is passed in we will have that as well so count of 2 # If a file is passed in we will have that as well so count of 2

View File

@ -411,13 +411,13 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
:param msg: First element is the controller which should be used :param msg: First element is the controller which should be used
:param status: :param status:
""" """
self.media_play(msg[0], status) return self.media_play(msg[0], status)
def on_media_play(self): def on_media_play(self):
""" """
Responds to the request to play a loaded video from the web. Responds to the request to play a loaded video from the web.
""" """
self.media_play(Registry().get('live_controller'), False) return self.media_play(Registry().get('live_controller'), False)
def media_play(self, controller, first_time=True): def media_play(self, controller, first_time=True):
""" """
@ -495,13 +495,13 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
:param msg: First element is the controller which should be used :param msg: First element is the controller which should be used
""" """
self.media_pause(msg[0]) return self.media_pause(msg[0])
def on_media_pause(self): def on_media_pause(self):
""" """
Responds to the request to pause a loaded video from the web. Responds to the request to pause a loaded video from the web.
""" """
self.media_pause(Registry().get('live_controller')) return self.media_pause(Registry().get('live_controller'))
def media_pause(self, controller): def media_pause(self, controller):
""" """
@ -515,6 +515,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
controller.mediabar.actions['playbackStop'].setDisabled(False) controller.mediabar.actions['playbackStop'].setDisabled(False)
controller.mediabar.actions['playbackPause'].setVisible(False) controller.mediabar.actions['playbackPause'].setVisible(False)
controller.media_info.is_playing = False controller.media_info.is_playing = False
return True
return False
def media_loop_msg(self, msg): def media_loop_msg(self, msg):
""" """
@ -540,13 +542,13 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
:param msg: First element is the controller which should be used :param msg: First element is the controller which should be used
""" """
self.media_stop(msg[0]) return self.media_stop(msg[0])
def on_media_stop(self): def on_media_stop(self):
""" """
Responds to the request to stop a loaded video from the web. Responds to the request to stop a loaded video from the web.
""" """
self.media_stop(Registry().get('live_controller')) return self.media_stop(Registry().get('live_controller'))
def media_stop(self, controller): def media_stop(self, controller):
""" """
@ -570,6 +572,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
controller.media_timer = 0 controller.media_timer = 0
display = self._define_display(controller) display = self._define_display(controller)
display.show_display() display.show_display()
return True
return False
def media_volume_msg(self, msg): def media_volume_msg(self, msg):
""" """

View File

@ -1,5 +1,4 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
############################################################################### ###############################################################################
# OpenLP - Open Source Lyrics Projection # # OpenLP - Open Source Lyrics Projection #
@ -20,79 +19,58 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
The :mod:`~openlp.core.api.endpoint` module contains various API endpoints The :mod:`~openlp.core.ui.media` module contains various API endpoints
""" """
import logging import logging
from flask import abort, jsonify, Blueprint from flask import abort, Blueprint
from openlp.core.api import app from openlp.core.api import app
from openlp.core.api.lib import login_required, old_auth from openlp.core.api.lib import login_required
from openlp.core.common.registry import Registry from openlp.core.common.registry import Registry
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
v1_media = Blueprint('v1-media-controller', __name__)
v2_media = Blueprint('v2-media-controller', __name__) v2_media = Blueprint('v2-media-controller', __name__)
@v2_media.route('/play', methods=['POST']) @v2_media.route('/play', methods=['POST'])
@login_required @login_required
def media_play(): def media_play():
media = Registry().get('media_controller') log.debug('media_play')
live = Registry().get('live_controller') live = Registry().get('live_controller')
try: if live.service_item.name != 'media':
status = media.media_play(live, True)
except Exception:
# The current item probably isn't a media item
abort(400) abort(400)
status = live.mediacontroller_live_play.emit()
if status: if status:
return '', 204 return '', 202
abort(400) abort(400)
@v2_media.route('/pause', methods=['POST']) @v2_media.route('/pause', methods=['POST'])
@login_required @login_required
def media_pause(): def media_pause():
media = Registry().get('media_controller') log.debug('media_pause')
live = Registry().get('live_controller') live = Registry().get('live_controller')
media.media_pause(live) if live.service_item.name != 'media':
return '', 204 abort(400)
status = live.mediacontroller_live_pause.emit()
if status:
return '', 202
abort(400)
@v2_media.route('/stop', methods=['POST']) @v2_media.route('/stop', methods=['POST'])
@login_required @login_required
def media_stop(): def media_stop():
Registry().get('live_controller').mediacontroller_live_stop.emit() log.debug('media_stop')
return '', 204
# -------------- DEPRECATED ------------------------
@v1_media.route('/play')
@old_auth
def v1_media_play():
media = Registry().get('media_controller')
live = Registry().get('live_controller') live = Registry().get('live_controller')
status = media.media_play(live, False) if live.service_item.name != 'media':
return jsonify({'success': status}) abort(400)
status = live.mediacontroller_live_stop.emit()
if status:
@v1_media.route('/pause') return '', 202
@old_auth abort(400)
def v1_media_pause():
media = Registry().get('media_controller')
live = Registry().get('live_controller')
status = media.media_pause(live)
return jsonify({'success': status})
@v1_media.route('/stop')
@old_auth
def v1_media_stop():
Registry().get('live_controller').mediacontroller_live_stop.emit()
return ''
# -------------- END OF DEPRECATED ------------------------
def register_views(): def register_views():
app.register_blueprint(v2_media, url_prefix='/api/v2/media/') app.register_blueprint(v2_media, url_prefix='/api/v2/media/')
app.register_blueprint(v1_media, url_prefix='/api/media/')

View File

@ -287,42 +287,26 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
icon=UiIcons().live_presentation, icon=UiIcons().live_presentation,
checked=False, can_shortcuts=True, category=self.category, checked=False, can_shortcuts=True, category=self.category,
triggers=self.on_show_display) triggers=self.on_show_display)
self.theme_screen = create_action(self, 'setThemeScreen', self.theme_screen = create_action(self, 'themeScreen',
text=translate('OpenLP.SlideController', 'Show Theme'), text=translate('OpenLP.SlideController', 'Show Theme'),
icon=UiIcons().live_theme, icon=UiIcons().live_theme,
checked=False, can_shortcuts=False, category=self.category, checked=False, can_shortcuts=True, category=self.category,
triggers=self.on_theme_display) triggers=self.on_theme_display)
self.blank_screen = create_action(self, 'setBlankScreen', self.blank_screen = create_action(self, 'blankScreen',
text=translate('OpenLP.SlideController', 'Show Black'), text=translate('OpenLP.SlideController', 'Show Black'),
icon=UiIcons().live_black, icon=UiIcons().live_black,
checked=False, can_shortcuts=False, category=self.category, checked=False, can_shortcuts=True, category=self.category,
triggers=self.on_blank_display) triggers=self.on_blank_display)
self.desktop_screen = create_action(self, 'setDesktopScreen', self.desktop_screen = create_action(self, 'desktopScreen',
text=translate('OpenLP.SlideController', 'Show Desktop'), text=translate('OpenLP.SlideController', 'Show Desktop'),
icon=UiIcons().live_desktop, icon=UiIcons().live_desktop,
checked=False, can_shortcuts=False, category=self.category, checked=False, can_shortcuts=True, category=self.category,
triggers=self.on_hide_display) triggers=self.on_hide_display)
self.hide_menu.setDefaultAction(self.show_screen) self.hide_menu.setDefaultAction(self.show_screen)
self.hide_menu.menu().addAction(self.show_screen) self.hide_menu.menu().addAction(self.show_screen)
self.hide_menu.menu().addAction(self.theme_screen) self.hide_menu.menu().addAction(self.theme_screen)
self.hide_menu.menu().addAction(self.blank_screen) self.hide_menu.menu().addAction(self.blank_screen)
self.hide_menu.menu().addAction(self.desktop_screen) self.hide_menu.menu().addAction(self.desktop_screen)
# Add togglable actions for keyboard shortcuts
self.controller.addAction(create_action(self, 'desktopScreen',
can_shortcuts=True,
context=QtCore.Qt.WidgetWithChildrenShortcut,
category=self.category,
triggers=self.on_toggle_desktop))
self.controller.addAction(create_action(self, 'themeScreen',
can_shortcuts=True,
context=QtCore.Qt.WidgetWithChildrenShortcut,
category=self.category,
triggers=self.on_toggle_theme))
self.controller.addAction(create_action(self, 'blankScreen',
can_shortcuts=True,
context=QtCore.Qt.WidgetWithChildrenShortcut,
category=self.category,
triggers=self.on_toggle_blank))
# Wide menu of display control buttons. # Wide menu of display control buttons.
self.show_screen_button = QtWidgets.QToolButton(self.toolbar) self.show_screen_button = QtWidgets.QToolButton(self.toolbar)
self.show_screen_button.setObjectName('show_screen_button') self.show_screen_button.setObjectName('show_screen_button')
@ -922,7 +906,7 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
self.selected_row = 0 self.selected_row = 0
# take a copy not a link to the servicemanager copy. # take a copy not a link to the servicemanager copy.
self.service_item = copy.copy(service_item) self.service_item = copy.copy(service_item)
if self.service_item.is_command(): if self.service_item.is_command() and not self.service_item.is_media():
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.get_hide_mode(), slide_no]) [self.service_item, self.is_live, self.get_hide_mode(), slide_no])
@ -967,7 +951,7 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
self.preview_display.load_images(self.service_item.slides) self.preview_display.load_images(self.service_item.slides)
for display in self.displays: for display in self.displays:
display.load_images(self.service_item.slides) display.load_images(self.service_item.slides)
for slide_index, slide in enumerate(self.service_item.slides): for _, _ in enumerate(self.service_item.slides):
row += 1 row += 1
self.slide_list[str(row)] = row - 1 self.slide_list[str(row)] = row - 1
self.preview_widget.replace_service_item(self.service_item, width, slide_no) self.preview_widget.replace_service_item(self.service_item, width, slide_no)
@ -987,7 +971,10 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
# close the previous, so make sure we don't close the new one. # close the previous, so make sure we don't close the new one.
if old_item.is_command() and not self.service_item.is_command() or \ if old_item.is_command() and not self.service_item.is_command() or \
old_item.is_command() and not old_item.is_media() and self.service_item.is_media(): old_item.is_command() and not old_item.is_media() and self.service_item.is_media():
Registry().execute('{name}_stop'.format(name=old_item.name.lower()), [old_item, self.is_live]) if old_item.is_media():
self.on_media_close()
else:
Registry().execute('{name}_stop'.format(name=old_item.name.lower()), [old_item, self.is_live])
if old_item.is_media() and not self.service_item.is_media(): if old_item.is_media() and not self.service_item.is_media():
self.on_media_close() self.on_media_close()
if self.is_live: if self.is_live:

View File

@ -314,12 +314,21 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
:param show_error: Should the error be shown (True) :param show_error: Should the error be shown (True)
:return: The search result. :return: The search result.
""" """
from pathlib import Path
results = [] results = []
string = string.lower() string = string.lower()
for file_path in self.settings.value('media/media files'): for file_path in self.settings.value('media/media files'):
file_name = file_path.name if isinstance(file_path, Path):
if file_name.lower().find(string) > -1: file_name = file_path.name
results.append([str(file_path), file_name]) if file_name.lower().find(string) > -1:
results.append([str(file_path), file_name])
else:
if file_path.lower().find(string) > -1:
if file_path.startswith('device'):
(name, _, _) = parse_stream_path(file_path)
results.append([str(file_path), name])
else:
results.append([str(file_path), file_path])
return results return results
def on_load_optical(self): def on_load_optical(self):

View File

@ -160,6 +160,8 @@ class PresentationMediaItem(MediaManagerItem):
existing files, and when the user adds new files via the media manager. existing files, and when the user adds new files via the media manager.
:param list[pathlib.Path] file_paths: List of file paths to add to the media manager. :param list[pathlib.Path] file_paths: List of file paths to add to the media manager.
:param list[pathlib.Path] target_group: Group to load.
:param boolean initial_load: Is this the initial load of the list at start up
""" """
current_paths = self.get_file_list() current_paths = self.get_file_list()
titles = [file_path.name for file_path in current_paths] titles = [file_path.name for file_path in current_paths]

View File

@ -739,8 +739,8 @@ def test_process_item(mocked_execute, registry):
slide_controller._process_item(mocked_media_item, 0) slide_controller._process_item(mocked_media_item, 0)
# THEN: Registry.execute should have been called to stop the presentation # THEN: Registry.execute should have been called to stop the presentation
assert 2 == mocked_execute.call_count, 'Execute should have been called 2 times' assert 1 == mocked_execute.call_count, 'Execute should have been called 2 times'
assert 'mocked_presentation_item_stop' == mocked_execute.call_args_list[1][0][0], \ assert 'mocked_presentation_item_stop' == mocked_execute.call_args_list[0][0][0], \
'The presentation should have been stopped.' 'The presentation should have been stopped.'

View File

@ -55,6 +55,18 @@ def test_search_found(media_item):
assert result == [['test.mp4', 'test.mp4']], 'The result file contain the file name' assert result == [['test.mp4', 'test.mp4']], 'The result file contain the file name'
def test_search_found_mixed(media_item):
"""
Media Remote Search Successful find with Paths and Strings
"""
# GIVEN: The Mediaitem set up a list of media
media_item.settings.setValue('media/media files', [Path('test.mp3'), 'test.mp4'])
# WHEN: Retrieving the test file
result = media_item.search('test.mp4', False)
# THEN: a file should be found
assert result == [['test.mp4', 'test.mp4']], 'The result file contain the file name'
def test_search_not_found(media_item): def test_search_not_found(media_item):
""" """
Media Remote Search not find Media Remote Search not find