Merge branch 'beta_fixes_2' into 'master'

Video/VLC fixes

Closes #679

See merge request openlp/openlp!259
This commit is contained in:
Raoul Snyman 2020-11-17 03:59:39 +00:00
commit 83e0d728e4
9 changed files with 473 additions and 168 deletions

View File

@ -1016,9 +1016,6 @@ var Display = {
targetElement.style.cssText = ""; targetElement.style.cssText = "";
targetElement.setAttribute("data-background", backgroundContent); targetElement.setAttribute("data-background", backgroundContent);
targetElement.setAttribute("data-background-size", "cover"); targetElement.setAttribute("data-background-size", "cover");
if (!!backgroundHtml) {
background.innerHTML = backgroundHtml;
}
// set up the main area // set up the main area
if (!is_text) { if (!is_text) {

View File

@ -41,8 +41,7 @@ from openlp.core.common.registry import Registry, RegistryBase
from openlp.core.lib.serviceitem import ItemCapabilities 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.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
VIDEO_EXT, AUDIO_EXT
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
@ -50,6 +49,7 @@ from openlp.core.ui.media.vlcplayer import VlcPlayer, get_vlc
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
TICK_TIME = 200 TICK_TIME = 200
HIDE_DELAY_TIME = 2500
class MediaController(RegistryBase, LogMixin, RegistryProperties): class MediaController(RegistryBase, LogMixin, RegistryProperties):
@ -68,10 +68,16 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
# Timer for video state # Timer for video state
self.live_timer = QtCore.QTimer() self.live_timer = QtCore.QTimer()
self.live_timer.setInterval(TICK_TIME) self.live_timer.setInterval(TICK_TIME)
self.live_hide_timer = QtCore.QTimer()
self.live_hide_timer.setSingleShot(True)
self.live_kill_timer = QtCore.QTimer()
self.live_kill_timer.setSingleShot(True)
self.preview_timer = QtCore.QTimer() self.preview_timer = QtCore.QTimer()
self.preview_timer.setInterval(TICK_TIME) self.preview_timer.setInterval(TICK_TIME)
# Signals # Signals
self.live_timer.timeout.connect(self._media_state_live) self.live_timer.timeout.connect(self._media_state_live)
self.live_hide_timer.timeout.connect(self._on_media_hide_live)
self.live_kill_timer.timeout.connect(self._on_media_kill_live)
self.preview_timer.timeout.connect(self._media_state_preview) self.preview_timer.timeout.connect(self._media_state_preview)
Registry().register_function('playbackPlay', self.media_play_msg) Registry().register_function('playbackPlay', self.media_play_msg)
Registry().register_function('playbackPause', self.media_pause_msg) Registry().register_function('playbackPause', self.media_pause_msg)
@ -154,31 +160,29 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
""" """
Check if there is a running Live media Player and do updating stuff (e.g. update the UI) Check if there is a running Live media Player and do updating stuff (e.g. update the UI)
""" """
display = self._define_display(self._display_controllers(DisplayControllerType.Live))
if DisplayControllerType.Live in self.current_media_players: if DisplayControllerType.Live in self.current_media_players:
self.current_media_players[DisplayControllerType.Live].resize(self.live_controller) media_player = self.current_media_players[DisplayControllerType.Live]
self.current_media_players[DisplayControllerType.Live].update_ui(self.live_controller, display) media_player.resize(self.live_controller)
self.tick(self._display_controllers(DisplayControllerType.Live)) media_player.update_ui(self.live_controller, self._define_display(self.live_controller))
if self.current_media_players[DisplayControllerType.Live].get_live_state() is not MediaState.Playing: if not self.tick(self.live_controller):
self.live_timer.stop() self.live_timer.stop()
else: else:
self.live_timer.stop() self.live_timer.stop()
self.media_stop(self._display_controllers(DisplayControllerType.Live)) self.media_stop(self.live_controller)
def _media_state_preview(self): def _media_state_preview(self):
""" """
Check if there is a running Preview media Player and do updating stuff (e.g. update the UI) Check if there is a running Preview media Player and do updating stuff (e.g. update the UI)
""" """
display = self._define_display(self._display_controllers(DisplayControllerType.Preview))
if DisplayControllerType.Preview in self.current_media_players: if DisplayControllerType.Preview in self.current_media_players:
self.current_media_players[DisplayControllerType.Preview].resize(self.live_controller) media_player = self.current_media_players[DisplayControllerType.Preview]
self.current_media_players[DisplayControllerType.Preview].update_ui(self.preview_controller, display) media_player.resize(self.preview_controller)
self.tick(self._display_controllers(DisplayControllerType.Preview)) media_player.update_ui(self.preview_controller, self._define_display(self.preview_controller))
if self.current_media_players[DisplayControllerType.Preview].get_preview_state() is not MediaState.Playing: if not self.tick(self.preview_controller):
self.preview_timer.stop() self.preview_timer.stop()
else: else:
self.preview_timer.stop() self.preview_timer.stop()
self.media_stop(self._display_controllers(DisplayControllerType.Preview)) self.media_stop(self.preview_controller)
def setup_display(self, controller, preview): def setup_display(self, controller, preview):
""" """
@ -224,9 +228,11 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
""" """
is_valid = True is_valid = True
controller = self._display_controllers(source) controller = self._display_controllers(source)
log.debug(f'load_video is_live:{controller.is_live}')
# stop running videos # stop running videos
self.media_reset(controller) self.media_reset(controller)
controller.media_info = ItemMediaInfo() controller.media_info = ItemMediaInfo()
controller.media_info.media_type = MediaType.Video
if controller.is_live: if controller.is_live:
controller.media_info.volume = self.settings.value('media/live volume') controller.media_info.volume = self.settings.value('media/live volume')
else: else:
@ -234,6 +240,9 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
# 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 = service_item.background_audio controller.media_info.file_info = service_item.background_audio
controller.media_info.media_type = MediaType.Audio
# is_background indicates we shouldn't override the normal display
controller.media_info.is_background = True
else: else:
if service_item.is_capable(ItemCapabilities.HasBackgroundStream): if service_item.is_capable(ItemCapabilities.HasBackgroundStream):
(name, mrl, options) = parse_stream_path(service_item.stream_mrl) (name, mrl, options) = parse_stream_path(service_item.stream_mrl)
@ -268,6 +277,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
controller.media_info.length = service_item.media_length controller.media_info.length = service_item.media_length
is_valid = self._check_file_type(controller, display) is_valid = self._check_file_type(controller, display)
controller.media_info.start_time = service_item.start_time controller.media_info.start_time = service_item.start_time
controller.media_info.timer = service_item.start_time
controller.media_info.end_time = service_item.end_time controller.media_info.end_time = service_item.end_time
elif controller.preview_display: elif controller.preview_display:
if service_item.is_capable(ItemCapabilities.IsOptical): if service_item.is_capable(ItemCapabilities.IsOptical):
@ -292,11 +302,21 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
translate('MediaPlugin.MediaItem', 'Unsupported File')) translate('MediaPlugin.MediaItem', 'Unsupported File'))
return False return False
self.log_debug('video media type: {tpe} '.format(tpe=str(controller.media_info.media_type))) self.log_debug('video media type: {tpe} '.format(tpe=str(controller.media_info.media_type)))
# If both the preview and live view have a stream, make sure only the live view continues streaming
if controller.media_info.media_type == MediaType.Stream:
if controller.is_live:
if self.preview_controller.media_info.media_type == MediaType.Stream:
self.log_warning('stream can only be displayed in one instance, killing preview stream')
self.preview_controller.on_media_close()
else:
if self.live_controller.media_info.media_type == MediaType.Stream:
self.log_warning('stream cannot be previewed while also streaming live')
return
autoplay = False autoplay = False
if service_item.requires_media(): if service_item.requires_media() and hidden == HideMode.Theme:
autoplay = True autoplay = True
# Preview requested # Preview requested
if not controller.is_live: elif not controller.is_live:
autoplay = True autoplay = True
# Visible or background requested or Service Item wants to autostart # Visible or background requested or Service Item wants to autostart
elif not hidden or service_item.will_auto_start: elif not hidden or service_item.will_auto_start:
@ -355,6 +375,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
else: else:
controller.media_info.media_type = MediaType.DVD controller.media_info.media_type = MediaType.DVD
controller.media_info.start_time = start controller.media_info.start_time = start
controller.media_info.timer = start
controller.media_info.end_time = end controller.media_info.end_time = end
controller.media_info.length = (end - start) controller.media_info.length = (end - start)
controller.media_info.title_track = title controller.media_info.title_track = title
@ -386,38 +407,23 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
return True return True
return False return False
for file in controller.media_info.file_info: for file in controller.media_info.file_info:
if file.is_file: if not file.is_file and not self.vlc_player.can_folder:
suffix = '*%s' % file.suffix.lower() return False
file = str(file) file = str(file)
if suffix in VIDEO_EXT: self.resize(controller, self.vlc_player)
self.resize(controller, self.vlc_player) if self.vlc_player.load(controller, display, file):
if self.vlc_player.load(controller, display, file): self.current_media_players[controller.controller_type] = self.vlc_player
self.current_media_players[controller.controller_type] = self.vlc_player return True
controller.media_info.media_type = MediaType.Video
return True
if suffix in AUDIO_EXT:
if self.vlc_player.load(controller, display, file):
self.current_media_players[controller.controller_type] = self.vlc_player
controller.media_info.media_type = MediaType.Audio
return True
else:
file = str(file)
if self.vlc_player.can_folder:
self.resize(controller, self.vlc_player)
if self.vlc_player.load(controller, display, file):
self.current_media_players[controller.controller_type] = self.vlc_player
controller.media_info.media_type = MediaType.Video
return True
return False return False
def media_play_msg(self, msg, status=True): def media_play_msg(self, msg):
""" """
Responds to the request to play a loaded video Responds to the request to play a loaded video
: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:
""" """
return self.media_play(msg[0], status) return self.media_play(msg[0])
def on_media_play(self): def on_media_play(self):
""" """
@ -425,14 +431,13 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
""" """
return self.media_play(self.live_controller) return self.media_play(self.live_controller)
def media_play(self, controller, first_time=True): def media_play(self, controller):
""" """
Responds to the request to play a loaded video Responds to the request to play a loaded video
:param controller: The controller to be played :param controller: The controller to be played
:param first_time:
""" """
self.log_debug(f"media_play with {first_time}") self.log_debug(f'media_play is_live:{controller.is_live}')
controller.seek_slider.blockSignals(True) controller.seek_slider.blockSignals(True)
controller.volume_slider.blockSignals(True) controller.volume_slider.blockSignals(True)
display = self._define_display(controller) display = self._define_display(controller)
@ -441,25 +446,29 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
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, controller.media_info.volume)
if first_time: self._media_set_visibility(controller, True)
self.current_media_players[controller.controller_type].set_visible(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['playbackStop'].setVisible(not controller.media_info.is_background or
controller.media_info.media_type is MediaType.Audio)
controller.mediabar.actions['playbackLoop'].setVisible((not controller.media_info.is_background and
controller.media_info.media_type is not MediaType.Stream)
or controller.media_info.media_type is MediaType.Audio)
# Start Timer for ui updates
if controller.is_live: if controller.is_live:
if controller.hide_menu.defaultAction().isChecked():
controller.hide_menu.defaultAction().trigger()
# Start Timer for ui updates
if not self.live_timer.isActive(): if not self.live_timer.isActive():
self.live_timer.start() self.live_timer.start()
else: else:
# Start Timer for ui updates
if not self.preview_timer.isActive(): if not self.preview_timer.isActive():
self.preview_timer.start() self.preview_timer.start()
controller.seek_slider.blockSignals(False) controller.seek_slider.blockSignals(False)
controller.volume_slider.blockSignals(False) controller.volume_slider.blockSignals(False)
controller.media_info.is_playing = True controller.media_info.is_playing = True
if not controller.media_info.is_background: if not controller.media_info.is_background:
if controller.is_live:
controller.set_hide_mode(None)
display = self._define_display(controller) display = self._define_display(controller)
display.hide_display(HideMode.Screen) display.hide_display(HideMode.Screen)
controller._set_theme(controller.service_item) controller._set_theme(controller.service_item)
@ -472,30 +481,35 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
Add a tick while the media is playing but only count if not paused Add a tick while the media is playing but only count if not paused
:param controller: The Controller to be processed :param controller: The Controller to be processed
:return: Is the video still running?
""" """
start_again = False start_again = False
stopped = False
if controller.media_info.is_playing and controller.media_info.length > 0: if controller.media_info.is_playing and controller.media_info.length > 0:
if controller.media_info.timer > controller.media_info.length: if controller.media_info.timer + TICK_TIME >= controller.media_info.length:
if controller.media_info.is_looping_playback: if controller.media_info.is_looping_playback:
start_again = True start_again = True
else: else:
self.media_stop(controller) self.media_stop(controller)
elif controller.media_info.timer > controller.media_info.length - TICK_TIME * 4: stopped = True
if not controller.media_info.is_looping_playback:
display = self._define_display(controller)
display.show_display()
controller.media_info.timer += TICK_TIME controller.media_info.timer += TICK_TIME
seconds = controller.media_info.timer // 1000 self._update_seek_ui(controller)
minutes = seconds // 60 else:
seconds %= 60 stopped = True
total_seconds = controller.media_info.length // 1000
total_minutes = total_seconds // 60
total_seconds %= 60
controller.position_label.setText(' %02d:%02d / %02d:%02d' %
(minutes, seconds, total_minutes, total_seconds))
if start_again: if start_again:
controller.seek_slider.setSliderPosition(0) controller.seek_slider.setSliderPosition(0)
self.media_play(controller, False) return not stopped
def _update_seek_ui(self, controller):
seconds = controller.media_info.timer // 1000
minutes = seconds // 60
seconds %= 60
total_seconds = controller.media_info.length // 1000
total_minutes = total_seconds // 60
total_seconds %= 60
controller.position_label.setText(' %02d:%02d / %02d:%02d' %
(minutes, seconds, total_minutes, total_seconds))
def media_pause_msg(self, msg): def media_pause_msg(self, msg):
""" """
@ -517,12 +531,16 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
:param controller: The Controller to be paused :param controller: The Controller to be paused
""" """
self.log_debug(f'media_stop is_live:{controller.is_live}')
if controller.controller_type in self.current_media_players: if controller.controller_type in self.current_media_players:
self.current_media_players[controller.controller_type].pause(controller) self.current_media_players[controller.controller_type].pause(controller)
controller.mediabar.actions['playbackPlay'].setVisible(True) controller.mediabar.actions['playbackPlay'].setVisible(True)
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
# Add a tick to the timer to prevent it finishing the video before it can loop back or stop
# If the clip finishes, we hit a bug where we cannot start the video
controller.media_info.timer += TICK_TIME
controller.output_has_changed() controller.output_has_changed()
return True return True
return False return False
@ -565,9 +583,19 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
:param controller: The controller that needs to be stopped :param controller: The controller that needs to be stopped
""" """
self.log_debug(f'media_stop is_live:{controller.is_live}')
if controller.controller_type in self.current_media_players: if controller.controller_type in self.current_media_players:
self.current_media_players[controller.controller_type].stop(controller) self.current_media_players[controller.controller_type].stop(controller)
self.current_media_players[controller.controller_type].set_visible(controller, False) if controller.is_live:
self.live_hide_timer.start(HIDE_DELAY_TIME)
if not controller.media_info.is_background:
display = self._define_display(controller)
if display.hide_mode == HideMode.Screen:
controller.set_hide_mode(HideMode.Blank)
else:
controller.set_hide_mode(display.hide_mode or HideMode.Blank)
else:
self._media_set_visibility(controller, False)
controller.seek_slider.setSliderPosition(0) controller.seek_slider.setSliderPosition(0)
total_seconds = controller.media_info.length // 1000 total_seconds = controller.media_info.length // 1000
total_minutes = total_seconds // 60 total_minutes = total_seconds // 60
@ -577,10 +605,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
controller.mediabar.actions['playbackStop'].setDisabled(True) controller.mediabar.actions['playbackStop'].setDisabled(True)
controller.mediabar.actions['playbackPause'].setVisible(False) controller.mediabar.actions['playbackPause'].setVisible(False)
controller.media_info.is_playing = False controller.media_info.is_playing = False
controller.media_info.timer = 1000 controller.media_info.timer = 0
controller.media_timer = 0
display = self._define_display(controller)
display.show_display()
controller.output_has_changed() controller.output_has_changed()
return True return True
return False return False
@ -631,33 +656,76 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
""" """
self.current_media_players[controller.controller_type].seek(controller, seek_value) self.current_media_players[controller.controller_type].seek(controller, seek_value)
controller.media_info.timer = seek_value controller.media_info.timer = seek_value
self._update_seek_ui(controller)
def media_reset(self, controller): def media_reset(self, controller, delayed=False):
""" """
Responds to the request to reset a loaded video Responds to the request to reset a loaded video
:param controller: The controller to use. :param controller: The controller to use.
:param delayed: Should the controller briefly remain visible.
""" """
self.log_debug('media_reset')
self.set_controls_visible(controller, False) self.set_controls_visible(controller, False)
if controller.controller_type in self.current_media_players: if controller.controller_type in self.current_media_players:
display = self._define_display(controller) display = self._define_display(controller)
display.show_display() hide_mode = controller.get_hide_mode()
if hide_mode is None:
display.show_display()
else:
display.hide_display(hide_mode)
self.current_media_players[controller.controller_type].reset(controller) self.current_media_players[controller.controller_type].reset(controller)
self.current_media_players[controller.controller_type].set_visible(controller, False) if controller.is_live and delayed:
del self.current_media_players[controller.controller_type] self.live_kill_timer.start(HIDE_DELAY_TIME)
else:
if controller.is_live:
self.live_kill_timer.stop()
else:
self._media_set_visibility(controller, False)
del self.current_media_players[controller.controller_type]
def media_hide(self, msg): def media_hide_msg(self, msg):
""" """
Hide the related video Widget Hide the related video Widget
:param msg: First element is the boolean for Live indication :param msg: First element is the boolean for Live indication
""" """
is_live = msg[1] is_live = msg[1]
if not is_live: self.media_hide(is_live)
def media_hide(self, is_live, delayed=False):
"""
Pause and hide the related video Widget if is_live
:param is_live: Live indication
:param delayed: Should the controller briefly remain visible.
"""
self.log_debug(f'media_hide is_live:{is_live}')
if not is_live or self.live_kill_timer.isActive():
return return
if self.live_controller.controller_type in self.current_media_players and \ if self.live_controller.controller_type in self.current_media_players and \
self.current_media_players[self.live_controller.controller_type].get_live_state() == MediaState.Playing: self.current_media_players[self.live_controller.controller_type].get_live_state() == MediaState.Playing:
self.media_pause(self.live_controller) if delayed:
self.current_media_players[self.live_controller.controller_type].set_visible(self.live_controller, False) self.live_hide_timer.start(HIDE_DELAY_TIME)
else:
self.media_pause(self.live_controller)
self._media_set_visibility(self.live_controller, False)
def _on_media_hide_live(self):
self.media_pause(self.live_controller)
self._media_set_visibility(self.live_controller, False)
def _on_media_kill_live(self):
self._media_set_visibility(self.live_controller, False)
del self.current_media_players[self.live_controller.controller_type]
def _media_set_visibility(self, controller, visible):
"""
Set the live video Widget visibility
"""
if controller.is_live:
self.live_hide_timer.stop()
visible = visible and controller.media_info.media_type is not MediaType.Audio
self.current_media_players[controller.controller_type].set_visible(controller, visible)
def media_blank(self, msg): def media_blank(self, msg):
""" """
@ -668,13 +736,30 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
""" """
is_live = msg[1] is_live = msg[1]
hide_mode = msg[2] hide_mode = msg[2]
if not is_live: self.log_debug(f'media_blank is_live:{is_live}')
if not is_live or self.live_controller.controller_type not in self.current_media_players:
return return
Registry().execute('live_display_hide', hide_mode) if self.live_kill_timer.isActive():
if self.live_controller.controller_type in self.current_media_players and \ # If pressing blank when the video is being removed, remove it instantly
self.current_media_players[self.live_controller.controller_type].get_live_state() == MediaState.Playing: self._media_set_visibility(self.live_controller, False)
self.media_pause(self.live_controller) self.media_reset(self.live_controller)
self.current_media_players[self.live_controller.controller_type].set_visible(self.live_controller, False) return
if not self.live_controller.media_info.is_background:
Registry().execute('live_display_hide', hide_mode)
controller_type = self.live_controller.controller_type
playing = self.current_media_players[controller_type].get_live_state() == MediaState.Playing
if hide_mode == HideMode.Theme:
if not playing:
self.media_play(self.live_controller)
else:
self.live_hide_timer.stop()
else:
if hide_mode == HideMode.Screen:
if playing:
self.media_pause(self.live_controller)
self._media_set_visibility(self.live_controller, False)
else:
self.live_hide_timer.start(HIDE_DELAY_TIME)
def media_unblank(self, msg): def media_unblank(self, msg):
""" """
@ -683,24 +768,28 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
:param msg: First element is not relevant in this context :param msg: First element is not relevant in this context
Second element is the boolean for Live indication Second element is the boolean for Live indication
""" """
Registry().execute('live_display_show')
is_live = msg[1] is_live = msg[1]
if not is_live: self.log_debug(f'media_blank is_live:{is_live}')
if not is_live or self.live_kill_timer.isActive():
return return
if self.live_controller.controller_type in self.current_media_players and \ Registry().execute('live_display_show')
self.current_media_players[self.live_controller.controller_type].get_live_state() != \ if self.live_controller.controller_type in self.current_media_players:
MediaState.Playing: if self.current_media_players[self.live_controller.controller_type].get_live_state() != \
if self.media_play(self.live_controller): MediaState.Playing:
self.current_media_players[self.live_controller.controller_type].set_visible(self.live_controller, True) self.media_play(self.live_controller)
# Start Timer for ui updates else:
if not self.live_timer.isActive(): self._media_set_visibility(self.live_controller, True)
self.live_timer.start() if not self.live_controller.media_info.is_background:
display = self._define_display(self.live_controller)
display.hide_display(HideMode.Screen)
def finalise(self): def finalise(self):
""" """
Reset all the media controllers when OpenLP shuts down Reset all the media controllers when OpenLP shuts down
""" """
self.live_timer.stop() self.live_timer.stop()
self.live_hide_timer.stop()
self.live_kill_timer.stop()
self.preview_timer.stop() self.preview_timer.stop()
self.media_reset(self._display_controllers(DisplayControllerType.Live)) self.media_reset(self._display_controllers(DisplayControllerType.Live))
self.media_reset(self._display_controllers(DisplayControllerType.Preview)) self.media_reset(self._display_controllers(DisplayControllerType.Preview))

View File

@ -134,7 +134,6 @@ class VlcPlayer(MediaPlayer):
# creating an empty vlc media player # creating an empty vlc media player
controller.vlc_media_player = controller.vlc_instance.media_player_new() controller.vlc_media_player = controller.vlc_instance.media_player_new()
controller.vlc_widget.resize(controller.size()) controller.vlc_widget.resize(controller.size())
controller.vlc_widget.raise_()
controller.vlc_widget.hide() controller.vlc_widget.hide()
# The media player has to be 'connected' to the QFrame. # The media player has to be 'connected' to the QFrame.
# (otherwise a video would be displayed in it's own window) # (otherwise a video would be displayed in it's own window)
@ -245,22 +244,22 @@ class VlcPlayer(MediaPlayer):
start_time = 0 start_time = 0
log.debug('vlc play') log.debug('vlc play')
if controller.is_live: if controller.is_live:
if self.get_live_state() != MediaState.Paused and controller.media_info.start_time > 0: if self.get_live_state() != MediaState.Paused and controller.media_info.timer > 0:
start_time = controller.media_info.start_time start_time = controller.media_info.timer
else: else:
if self.get_preview_state() != MediaState.Paused and controller.media_info.start_time > 0: if self.get_preview_state() != MediaState.Paused and controller.media_info.timer > 0:
start_time = controller.media_info.start_time start_time = controller.media_info.timer
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, vlc.State.Playing): if not self.media_state_wait(controller, vlc.State.Playing):
return False return False
if controller.is_live: if controller.is_live:
if self.get_live_state() != MediaState.Paused and controller.media_info.start_time > 0: if self.get_live_state() != MediaState.Paused and controller.media_info.timer > 0:
log.debug('vlc play, start time set') log.debug('vlc play, start time set')
start_time = controller.media_info.start_time start_time = controller.media_info.timer
else: else:
if self.get_preview_state() != MediaState.Paused and controller.media_info.start_time > 0: if self.get_preview_state() != MediaState.Paused and controller.media_info.timer > 0:
log.debug('vlc play, start time set') log.debug('vlc play, start time set')
start_time = controller.media_info.start_time start_time = controller.media_info.timer
log.debug('mediatype: ' + str(controller.media_info.media_type)) log.debug('mediatype: ' + str(controller.media_info.media_type))
# Set tracks for the optical device # Set tracks for the optical device
if controller.media_info.media_type == MediaType.DVD and \ if controller.media_info.media_type == MediaType.DVD and \
@ -278,10 +277,10 @@ class VlcPlayer(MediaPlayer):
if controller.media_info.subtitle_track > 0: if controller.media_info.subtitle_track > 0:
controller.vlc_media_player.video_set_spu(controller.media_info.subtitle_track) controller.vlc_media_player.video_set_spu(controller.media_info.subtitle_track)
log.debug('vlc play, subtitle_track set: ' + str(controller.media_info.subtitle_track)) log.debug('vlc play, subtitle_track set: ' + str(controller.media_info.subtitle_track))
if controller.media_info.start_time > 0: if controller.media_info.timer > 0:
log.debug('vlc play, starttime set: ' + str(controller.media_info.start_time)) log.debug('vlc play, starttime set: ' + str(controller.media_info.timer))
start_time = controller.media_info.start_time start_time = controller.media_info.timer
controller.media_info.length = controller.media_info.end_time - controller.media_info.start_time controller.media_info.length = controller.media_info.end_time - controller.media_info.timer
self.volume(controller, controller.media_info.volume) self.volume(controller, controller.media_info.volume)
if start_time > 0 and controller.vlc_media_player.is_seekable(): if start_time > 0 and controller.vlc_media_player.is_seekable():
controller.vlc_media_player.set_time(int(start_time)) controller.vlc_media_player.set_time(int(start_time))
@ -343,7 +342,6 @@ class VlcPlayer(MediaPlayer):
:param controller: The controller where the media is :param controller: The controller where the media is
""" """
controller.vlc_media_player.stop() controller.vlc_media_player.stop()
controller.vlc_widget.setVisible(False)
self.set_state(MediaState.Off, controller) self.set_state(MediaState.Off, controller)
def set_visible(self, controller, status): def set_visible(self, controller, status):
@ -362,14 +360,6 @@ class VlcPlayer(MediaPlayer):
:param controller: Which Controller is running the show. :param controller: Which Controller is running the show.
:param output_display: The display where the media is :param output_display: The display where the media is
""" """
vlc = get_vlc()
# Stop video if playback is finished.
if controller.vlc_media.get_state() == vlc.State.Ended:
self.stop(controller)
if controller.media_info.end_time > 0:
if controller.vlc_media_player.get_time() > controller.media_info.end_time:
self.stop(controller)
self.set_visible(controller, False)
if not controller.seek_slider.isSliderDown(): if not controller.seek_slider.isSliderDown():
controller.seek_slider.blockSignals(True) controller.seek_slider.blockSignals(True)
if controller.media_info.media_type == MediaType.CD \ if controller.media_info.media_type == MediaType.CD \

View File

@ -525,6 +525,10 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
self.song_menu.setPopupMode(QtWidgets.QToolButton.InstantPopup) self.song_menu.setPopupMode(QtWidgets.QToolButton.InstantPopup)
self.song_menu.setMenu(QtWidgets.QMenu(translate('OpenLP.SlideController', 'Go To'), self.toolbar)) self.song_menu.setMenu(QtWidgets.QMenu(translate('OpenLP.SlideController', 'Go To'), self.toolbar))
def _raise_displays(self):
for display in self.displays:
display.raise_()
def _slide_shortcut_activated(self): def _slide_shortcut_activated(self):
""" """
Called, when a shortcut has been activated to jump to a chorus, verse, etc. Called, when a shortcut has been activated to jump to a chorus, verse, etc.
@ -946,8 +950,12 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
self._reset_blank(self.service_item.is_capable(ItemCapabilities.ProvidesOwnDisplay)) self._reset_blank(self.service_item.is_capable(ItemCapabilities.ProvidesOwnDisplay))
self.info_label.setText(self.service_item.title) self.info_label.setText(self.service_item.title)
self.slide_list = {} self.slide_list = {}
if old_item and old_item.requires_media(): if old_item:
self.on_media_close() # Close the old item if it's not to be used by the new sevice item
if not self.service_item.is_media() and not self.service_item.requires_media():
self.on_media_close()
if old_item.is_command() and not old_item.is_media():
Registry().execute('{name}_stop'.format(name=old_item.name.lower()), [old_item, self.is_live])
row = 0 row = 0
width = self.main_window.control_splitter.sizes()[self.split] width = self.main_window.control_splitter.sizes()[self.split]
if self.service_item.is_text(): if self.service_item.is_text():
@ -989,26 +997,15 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
if self.service_item.is_command(): if self.service_item.is_command():
self.preview_display.load_verses(media_empty_song, True) self.preview_display.load_verses(media_empty_song, True)
self.on_media_start(self.service_item) self.on_media_start(self.service_item)
# Let media window init, then put webengine back on top # Try to get display back on top of media window asap. If the media window
self.application.process_events() # is not loaded by the time _raise_displays is run, lyrics (web display)
for display in self.displays: # will be under the media window (not good).
display.raise_() QtCore.QTimer.singleShot(100, self._raise_displays)
QtCore.QTimer.singleShot(500, self._raise_displays)
QtCore.QTimer.singleShot(1000, self._raise_displays)
self.slide_selected(True) self.slide_selected(True)
if self.service_item.from_service: if self.service_item.from_service:
self.preview_widget.setFocus() self.preview_widget.setFocus()
if old_item:
# Close the old item after the new one is opened
# This avoids the service theme/desktop flashing on screen
# However opening a new item of the same type will automatically
# 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 \
old_item.is_command() and not old_item.is_media() and self.service_item.is_media():
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():
self.on_media_close()
if self.is_live: if self.is_live:
Registry().execute('slidecontroller_{item}_started'.format(item=self.type_prefix), [self.service_item]) Registry().execute('slidecontroller_{item}_started'.format(item=self.type_prefix), [self.service_item])
# Need to process events four times to get correct controller width # Need to process events four times to get correct controller width
@ -1528,7 +1525,9 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
:param item: The service item to be processed :param item: The service item to be processed
""" """
if State().check_preconditions('media'): if State().check_preconditions('media'):
if self.is_live and self.get_hide_mode() == HideMode.Theme: if self.is_live and not item.is_media() and item.requires_media():
self.media_controller.load_video(self.controller_type, item, self.get_hide_mode())
elif self.is_live and self.get_hide_mode() == HideMode.Theme:
self.media_controller.load_video(self.controller_type, item, HideMode.Blank) self.media_controller.load_video(self.controller_type, item, HideMode.Blank)
self.set_hide_mode(HideMode.Blank) self.set_hide_mode(HideMode.Blank)
elif self.is_live or item.is_media(): elif self.is_live or item.is_media():
@ -1542,7 +1541,7 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
Respond to a request to close the Video if we have media Respond to a request to close the Video if we have media
""" """
if State().check_preconditions('media'): if State().check_preconditions('media'):
self.media_controller.media_reset(self) self.media_controller.media_reset(self, delayed=True)
def _reset_blank(self, no_theme): def _reset_blank(self, no_theme):
""" """

View File

@ -68,7 +68,7 @@ class StartTimeForm(QtWidgets.QDialog, Ui_StartTimeDialog, RegistryProperties):
start = self.hour_spin_box.value() * 3600 + self.minute_spin_box.value() * 60 + self.second_spin_box.value() start = self.hour_spin_box.value() * 3600 + self.minute_spin_box.value() * 60 + self.second_spin_box.value()
end = self.hour_finish_spin_box.value() * 3600 + \ end = self.hour_finish_spin_box.value() * 3600 + \
self.minute_finish_spin_box.value() * 60 + self.second_finish_spin_box.value() self.minute_finish_spin_box.value() * 60 + self.second_finish_spin_box.value()
if end > self.item['service_item'].media_length: if end > self.item['service_item'].media_length // 1000:
critical_error_message_box(title=translate('OpenLP.StartTime_form', 'Time Validation Error'), critical_error_message_box(title=translate('OpenLP.StartTime_form', 'Time Validation Error'),
message=translate('OpenLP.StartTime_form', message=translate('OpenLP.StartTime_form',
'Finish time is set after the end of the media item')) 'Finish time is set after the end of the media item'))
@ -78,14 +78,15 @@ class StartTimeForm(QtWidgets.QDialog, Ui_StartTimeDialog, RegistryProperties):
message=translate('OpenLP.StartTime_form', message=translate('OpenLP.StartTime_form',
'Start time is after the finish time of the media item')) 'Start time is after the finish time of the media item'))
return return
self.item['service_item'].start_time = start self.item['service_item'].start_time = start * 1000
self.item['service_item'].end_time = end self.item['service_item'].end_time = end * 1000
return QtWidgets.QDialog.accept(self) return QtWidgets.QDialog.accept(self)
def _time_split(self, seconds): def _time_split(self, milliseconds):
""" """
Split time up into hours minutes and seconds from seconds Split time up into hours minutes and seconds from milliseconds
""" """
seconds = milliseconds // 1000
hours = seconds // 3600 hours = seconds // 3600
seconds -= 3600 * hours seconds -= 3600 * hours
minutes = seconds // 60 minutes = seconds // 60

View File

@ -26,9 +26,9 @@ import pytest
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from openlp.core.common.registry import Registry from openlp.core.common.registry import Registry
from openlp.core.ui import DisplayControllerType 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 from openlp.core.ui.media import ItemMediaInfo, MediaState
from tests.utils.constants import RESOURCE_PATH from tests.utils.constants import RESOURCE_PATH
@ -60,6 +60,36 @@ def test_resize(media_env):
mocked_player.resize.assert_called_with(mocked_display) mocked_player.resize.assert_called_with(mocked_display)
def test_load_video(media_env, settings):
"""
Test that the load_video runs correctly
"""
# GIVEN: A media controller and a service item
mocked_slide_controller = MagicMock()
mocked_service_item = MagicMock()
mocked_service_item.is_capable.return_value = False
settings.setValue('media/live volume', 1)
media_env.media_controller.current_media_players = MagicMock()
media_env.media_controller._check_file_type = MagicMock(return_value=True)
media_env.media_controller._display_controllers = MagicMock(return_value=mocked_slide_controller)
media_env.media_controller._define_display = MagicMock()
media_env.media_controller.media_reset = MagicMock()
media_env.media_controller.media_play = MagicMock()
media_env.media_controller.set_controls_visible = MagicMock()
# WHEN: load_video() is called
media_env.media_controller.load_video(DisplayControllerType.Live, mocked_service_item)
# THEN: The current controller's media should be reset
# The volume should be set from the settings
# The video should have autoplayed
# The controls should have been made visible
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)
media_env.media_controller.set_controls_visible.assert_called_once_with(mocked_slide_controller, True)
def test_check_file_type_null(media_env): def test_check_file_type_null(media_env):
""" """
Test that we don't try to play media when no players available Test that we don't try to play media when no players available
@ -125,10 +155,10 @@ def test_media_play_msg(media_env):
# WHEN: media_play_msg() is called # WHEN: media_play_msg() is called
with patch.object(media_env.media_controller, u'media_play') as mocked_media_play: with patch.object(media_env.media_controller, u'media_play') as mocked_media_play:
media_env.media_controller.media_play_msg(message, False) media_env.media_controller.media_play_msg(message)
# THEN: The underlying method is called # THEN: The underlying method is called
mocked_media_play.assert_called_with(1, False) mocked_media_play.assert_called_with(1)
def test_media_pause_msg(media_env): def test_media_pause_msg(media_env):
@ -161,6 +191,33 @@ def test_media_stop_msg(media_env):
mocked_media_stop.assert_called_with(1) mocked_media_stop.assert_called_with(1)
def test_media_stop(media_env):
"""
Test that the media controller takes the correct actions when stopping media
"""
# GIVEN: A live media controller and a message with two elements
mocked_slide_controller = MagicMock()
mocked_media_player = MagicMock()
mocked_display = MagicMock(hide_mode=None)
mocked_slide_controller.controller_type = 'media player'
mocked_slide_controller.media_info = MagicMock(is_background=False)
mocked_slide_controller.set_hide_mode = MagicMock()
mocked_slide_controller.is_live = True
media_env.media_controller.current_media_players = {'media player': mocked_media_player}
media_env.media_controller.live_hide_timer = MagicMock()
media_env.media_controller._define_display = MagicMock(return_value=mocked_display)
# WHEN: media_stop() is called
result = media_env.media_controller.media_stop(mocked_slide_controller)
# THEN: Result should be successful, media player should be stopped and the hide timer should have started
# The controller's hide mode should be set to Blank
assert result is True
mocked_media_player.stop.assert_called_once_with(mocked_slide_controller)
media_env.media_controller.live_hide_timer.start.assert_called_once()
mocked_slide_controller.set_hide_mode.assert_called_once_with(HideMode.Blank)
def test_media_volume_msg(media_env): def test_media_volume_msg(media_env):
""" """
Test that the media controller responds to the request to change the volume Test that the media controller responds to the request to change the volume
@ -191,6 +248,59 @@ def test_media_seek_msg(media_env):
mocked_media_seek.assert_called_with(1, 800) mocked_media_seek.assert_called_with(1, 800)
def test_media_reset(media_env):
"""
Test that the media controller conducts the correct actions when resetting
"""
# GIVEN: A media controller, mocked slide controller, mocked media player and mocked display
mocked_slide_controller = MagicMock()
mocked_media_player = MagicMock()
mocked_display = MagicMock(hide_mode=None)
mocked_slide_controller.controller_type = 'media player'
mocked_slide_controller.media_info = MagicMock(is_background=False)
mocked_slide_controller.get_hide_mode = MagicMock(return_value=None)
mocked_slide_controller.is_live = False
media_env.media_controller.current_media_players = {'media player': mocked_media_player}
media_env.media_controller.live_hide_timer = MagicMock()
media_env.media_controller._define_display = MagicMock(return_value=mocked_display)
media_env.media_controller._media_set_visibility = MagicMock()
# WHEN: media_reset() is called
media_env.media_controller.media_reset(mocked_slide_controller)
# THEN: The display should be shown, media should be hidden and removed
mocked_display.show_display.assert_called_once_with()
media_env.media_controller._media_set_visibility.assert_called_once_with(mocked_slide_controller, False)
assert 'media player' not in media_env.media_controller.current_media_players
def test_media_hide(media_env, registry):
"""
Test that the media controller conducts the correct actions when hiding
"""
# GIVEN: A media controller, mocked slide controller, mocked media player and mocked display
mocked_slide_controller = MagicMock()
mocked_media_player = MagicMock()
mocked_media_player.get_live_state.return_value = MediaState.Playing
mocked_slide_controller.controller_type = 'media player'
mocked_slide_controller.media_info = MagicMock(is_background=False)
mocked_slide_controller.get_hide_mode = MagicMock(return_value=None)
mocked_slide_controller.is_live = False
Registry().register('live_controller', mocked_slide_controller)
media_env.media_controller.current_media_players = {'media player': mocked_media_player}
media_env.media_controller.live_kill_timer = MagicMock(isActive=MagicMock(return_value=False))
media_env.media_controller._media_set_visibility = MagicMock()
media_env.media_controller.media_pause = MagicMock()
# WHEN: media_hide() is called
media_env.media_controller.media_hide(is_live=True)
# THEN: media should be paused and hidden, but the player should still exist
media_env.media_controller.media_pause.assert_called_once_with(mocked_slide_controller)
media_env.media_controller._media_set_visibility.assert_called_once_with(mocked_slide_controller, False)
assert 'media player' in media_env.media_controller.current_media_players
def test_media_length(media_env): def test_media_length(media_env):
""" """
Test the Media Info basic functionality Test the Media Info basic functionality
@ -334,6 +444,7 @@ def test_media_play(media_env):
media_env.current_media_players = MagicMock() media_env.current_media_players = MagicMock()
Registry().register('settings', MagicMock()) Registry().register('settings', MagicMock())
media_env.live_timer = MagicMock() media_env.live_timer = MagicMock()
media_env.live_hide_timer = MagicMock()
mocked_controller = MagicMock() mocked_controller = MagicMock()
mocked_controller.media_info.is_background = False mocked_controller.media_info.is_background = False
@ -343,4 +454,5 @@ def test_media_play(media_env):
# THEN: The web display should become transparent (only tests that the theme is reset here) # THEN: The web display should become transparent (only tests that the theme is reset here)
# And the function should return true to indicate success # And the function should return true to indicate success
assert result is True assert result is True
media_env.live_hide_timer.stop.assert_called_once_with()
mocked_controller._set_theme.assert_called_once() mocked_controller._set_theme.assert_called_once()

View File

@ -122,7 +122,6 @@ def test_setup(MockedQtWidgets, mocked_get_vlc, mocked_is_macosx, mocked_is_win,
assert mocked_output_display.vlc_media_player == mocked_media_player_new assert mocked_output_display.vlc_media_player == mocked_media_player_new
mocked_output_display.size.assert_called_with() mocked_output_display.size.assert_called_with()
mocked_qframe.resize.assert_called_with((10, 10)) mocked_qframe.resize.assert_called_with((10, 10))
mocked_qframe.raise_.assert_called_with()
mocked_qframe.hide.assert_called_with() mocked_qframe.hide.assert_called_with()
mocked_media_player_new.set_xwindow.assert_called_with(2) mocked_media_player_new.set_xwindow.assert_called_with(2)
assert vlc_player.has_own_widget is True assert vlc_player.has_own_widget is True
@ -848,7 +847,7 @@ def test_reset():
# THEN: The media should be stopped and invisible # THEN: The media should be stopped and invisible
mocked_display.vlc_media_player.stop.assert_called_with() mocked_display.vlc_media_player.stop.assert_called_with()
mocked_display.vlc_widget.setVisible.assert_called_with(False) mocked_display.vlc_widget.setVisible.assert_not_called()
assert MediaState.Off == vlc_player.get_live_state() assert MediaState.Off == vlc_player.get_live_state()
@ -886,15 +885,10 @@ def test_update_ui(mocked_get_vlc):
vlc_player = VlcPlayer(None) vlc_player = VlcPlayer(None)
# WHEN: update_ui() is called # WHEN: update_ui() is called
with patch.object(vlc_player, 'stop') as mocked_stop, \ vlc_player.update_ui(mocked_controller, mocked_display)
patch.object(vlc_player, 'set_visible') as mocked_set_visible:
vlc_player.update_ui(mocked_controller, mocked_display)
# THEN: Certain methods should be called # THEN: Certain methods should be called
mocked_stop.assert_called_with(mocked_controller)
assert 2 == mocked_stop.call_count
mocked_controller.vlc_media_player.get_time.assert_called_with() mocked_controller.vlc_media_player.get_time.assert_called_with()
mocked_set_visible.assert_called_with(mocked_controller, False)
mocked_controller.seek_slider.setSliderPosition.assert_called_with(400000) mocked_controller.seek_slider.setSliderPosition.assert_called_with(400000)
expected_calls = [call(True), call(False)] expected_calls = [call(True), call(False)]
assert expected_calls == mocked_controller.seek_slider.blockSignals.call_args_list assert expected_calls == mocked_controller.seek_slider.blockSignals.call_args_list
@ -921,12 +915,9 @@ def test_update_ui_dvd(mocked_get_vlc):
vlc_player = VlcPlayer(None) vlc_player = VlcPlayer(None)
# WHEN: update_ui() is called # WHEN: update_ui() is called
with patch.object(vlc_player, 'stop') as mocked_stop: vlc_player.update_ui(mocked_controller, mocked_display)
vlc_player.update_ui(mocked_controller, mocked_display)
# THEN: Certain methods should be called # THEN: Certain methods should be called
mocked_stop.assert_called_with(mocked_controller)
assert 1 == mocked_stop.call_count
mocked_controller.vlc_media_player.get_time.assert_called_with() mocked_controller.vlc_media_player.get_time.assert_called_with()
mocked_controller.seek_slider.setSliderPosition.assert_called_with(200) mocked_controller.seek_slider.setSliderPosition.assert_called_with(200)
expected_calls = [call(True), call(False)] expected_calls = [call(True), call(False)]

View File

@ -899,11 +899,137 @@ def test_process_item(mocked_execute, registry, state_media):
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 1 == mocked_execute.call_count, 'Execute should have been called 2 times' assert 1 == mocked_execute.call_count, 'Execute should have been called once'
assert 'mocked_presentation_item_stop' == mocked_execute.call_args_list[0][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.'
@patch.object(Registry, 'execute')
def test_process_item_transition(mocked_execute, registry, state_media):
"""
Test that the correct actions are taken when a media service-item is closed followed by a image service-item
"""
# GIVEN: A mocked presentation service item, a mocked media service item, a mocked Registry.execute
# and a slide controller with many mocks.
# and the setting 'themes/item transitions' = True
mocked_pres_item = MagicMock()
mocked_pres_item.name = 'mocked_image_item'
mocked_pres_item.is_command.return_value = True
mocked_pres_item.is_media.return_value = True
mocked_pres_item.is_image.return_value = False
mocked_pres_item.from_service = False
mocked_pres_item.get_frames.return_value = []
mocked_media_item = MagicMock()
mocked_media_item.name = 'mocked_media_item'
mocked_media_item.get_transition_delay.return_value = 0
mocked_media_item.is_text.return_value = False
mocked_media_item.is_command.return_value = False
mocked_media_item.is_media.return_value = False
mocked_media_item.requires_media.return_value = False
mocked_media_item.is_image.return_value = True
mocked_media_item.from_service = False
mocked_media_item.get_frames.return_value = []
mocked_settings = MagicMock()
mocked_settings.value.return_value = True
mocked_main_window = MagicMock()
Registry().register('main_window', mocked_main_window)
Registry().register('media_controller', MagicMock())
Registry().register('application', MagicMock())
Registry().register('settings', mocked_settings)
slide_controller = SlideController(None)
slide_controller.service_item = mocked_pres_item
slide_controller.is_live = True
slide_controller._reset_blank = MagicMock()
slide_controller.preview_widget = MagicMock()
slide_controller.preview_display = MagicMock()
slide_controller.enable_tool_bar = MagicMock()
slide_controller.on_controller_size_changed = MagicMock()
slide_controller.on_media_start = MagicMock()
slide_controller.on_media_close = MagicMock()
slide_controller.slide_selected = MagicMock()
slide_controller.new_song_menu = MagicMock()
slide_controller.on_stop_loop = MagicMock()
slide_controller.info_label = MagicMock()
slide_controller.song_menu = MagicMock()
slide_controller.displays = [MagicMock()]
slide_controller.toolbar = MagicMock()
slide_controller.split = 0
slide_controller.type_prefix = 'test'
slide_controller.screen_capture = 'old_capture'
# WHEN: _process_item is called
slide_controller._process_item(mocked_media_item, 0)
# THEN: Registry.execute should have been called to start the live item
# Media should be closed
# Controller size change should be called (because it's a live item and the interface might have changed)
# The screen capture should have been reset to none
assert 1 == mocked_execute.call_count, 'Execute should have been called once'
slide_controller.on_media_close.assert_called_once_with()
slide_controller.on_controller_size_changed.assert_called_once()
assert slide_controller.screen_capture is None
@patch.object(Registry, 'execute')
def test_process_item_text(mocked_execute, registry, state_media):
"""
Test that the correct actions are taken a text item is processed
"""
# GIVEN: A mocked presentation service item, a mocked media service item, a mocked Registry.execute
# and a slide controller with many mocks.
# and the setting 'themes/item transitions' = True
mocked_media_item = MagicMock()
mocked_media_item.name = 'mocked_media_item'
mocked_media_item.get_transition_delay.return_value = 0
mocked_media_item.is_text.return_value = True
mocked_media_item.is_command.return_value = False
mocked_media_item.is_media.return_value = False
mocked_media_item.requires_media.return_value = False
mocked_media_item.is_image.return_value = False
mocked_media_item.from_service = False
mocked_media_item.get_frames.return_value = []
mocked_media_item.display_slides = [{'verse': 'Verse name'}]
mocked_settings = MagicMock()
mocked_settings.value.return_value = True
mocked_main_window = MagicMock()
Registry().register('main_window', mocked_main_window)
Registry().register('media_controller', MagicMock())
Registry().register('application', MagicMock())
Registry().register('settings', mocked_settings)
slide_controller = SlideController(None)
slide_controller.service_item = None
slide_controller.is_live = True
slide_controller._reset_blank = MagicMock()
slide_controller.preview_widget = MagicMock()
slide_controller.preview_display = MagicMock()
slide_controller.enable_tool_bar = MagicMock()
slide_controller.on_controller_size_changed = MagicMock()
slide_controller.on_media_start = MagicMock()
slide_controller.on_media_close = MagicMock()
slide_controller.slide_selected = MagicMock()
slide_controller.new_song_menu = MagicMock()
slide_controller.on_stop_loop = MagicMock()
slide_controller.info_label = MagicMock()
slide_controller.song_menu = MagicMock()
slide_controller.displays = [MagicMock()]
slide_controller.toolbar = MagicMock()
slide_controller.split = 0
slide_controller.type_prefix = 'test'
slide_controller.screen_capture = 'old_capture'
# WHEN: _process_item is called
slide_controller._process_item(mocked_media_item, 0)
# THEN: Registry.execute should have been called to start the live item
# Controller size change should be called (because it's a live item and the interface might have changed)
# The screen capture should have been reset to none
# The slide should have been added to the slide list with the correct index
assert 1 == mocked_execute.call_count, 'Execute should have been called once'
slide_controller.on_controller_size_changed.assert_called_once()
assert slide_controller.screen_capture is None
assert slide_controller.slide_list['Verse name'] == 0
@patch.object(Registry, 'execute') @patch.object(Registry, 'execute')
def test_process_item_song_vlc(mocked_execute, registry, state_media): def test_process_item_song_vlc(mocked_execute, registry, state_media):
""" """

View File

@ -65,9 +65,9 @@ def test_time_display(form):
""" """
# GIVEN: A service item with with time # GIVEN: A service item with with time
mocked_serviceitem = MagicMock() mocked_serviceitem = MagicMock()
mocked_serviceitem.start_time = 61 mocked_serviceitem.start_time = 61000
mocked_serviceitem.end_time = 3701 mocked_serviceitem.end_time = 3701000
mocked_serviceitem.media_length = 3701 mocked_serviceitem.media_length = 3701000
# WHEN displaying the UI and pressing enter # WHEN displaying the UI and pressing enter
form.item = {'service_item': mocked_serviceitem} form.item = {'service_item': mocked_serviceitem}
@ -80,7 +80,7 @@ def test_time_display(form):
assert form.hour_spin_box.value() == 0 assert form.hour_spin_box.value() == 0
assert form.minute_spin_box.value() == 1 assert form.minute_spin_box.value() == 1
assert form.second_spin_box.value() == 1 assert form.second_spin_box.value() == 1
assert form.item['service_item'].start_time == 61, 'The start time should stay the same' assert form.item['service_item'].start_time == 61000, 'The start time should stay the same'
# WHEN displaying the UI, changing the time to 2min 3secs and pressing enter # WHEN displaying the UI, changing the time to 2min 3secs and pressing enter
form.item = {'service_item': mocked_serviceitem} form.item = {'service_item': mocked_serviceitem}
@ -95,4 +95,4 @@ def test_time_display(form):
assert form.hour_spin_box.value() == 0 assert form.hour_spin_box.value() == 0
assert form.minute_spin_box.value() == 2 assert form.minute_spin_box.value() == 2
assert form.second_spin_box.value() == 3 assert form.second_spin_box.value() == 3
assert form.item['service_item'].start_time == 123, 'The start time should have changed' assert form.item['service_item'].start_time == 123000, 'The start time should have changed'