diff --git a/openlp/core/common/settings.py b/openlp/core/common/settings.py index 3912620ec..7760bcff0 100644 --- a/openlp/core/common/settings.py +++ b/openlp/core/common/settings.py @@ -212,6 +212,8 @@ class Settings(QtCore.QSettings): 'media/vlc arguments': '', 'media/video': '', 'media/audio': '', + 'media/live volume': 50, + 'media/preview volume': 0, 'remotes/download version': '0.0', 'players/background color': '#000000', 'servicemanager/last directory': None, diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index 95465864f..9ff4176e0 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -214,7 +214,6 @@ class ServiceItem(RegistryProperties): """ Render the frames for printing and return them - :param can_render_chords: bool Whether or not to render the chords """ if not self._print_slides: self._print_slides = [] @@ -345,7 +344,11 @@ class ServiceItem(RegistryProperties): service_data = [slide['title'] for slide in self.slides] elif self.service_item_type == ServiceItemType.Command: for slide in self.slides: - service_data.append({'title': slide['title'], 'image': slide['image'], 'path': slide['path'], + if isinstance(slide['image'], QtGui.QIcon): + image = "clapperboard" + else: + image = slide['image'] + service_data.append({'title': slide['title'], 'image': image, 'path': slide['path'], 'display_title': slide['display_title'], 'notes': slide['notes']}) return {'header': service_header, 'data': service_data} @@ -425,6 +428,8 @@ class ServiceItem(RegistryProperties): self.add_from_command(text_image['path'], text_image['title'], text_image['image']) elif path: self.has_original_files = False + if text_image['image'] == "clapperboard": + text_image['image'] = UiIcons().clapperboard self.add_from_command(path, text_image['title'], text_image['image'], text_image.get('display_title', ''), text_image.get('notes', '')) else: @@ -631,7 +636,7 @@ class ServiceItem(RegistryProperties): """ Validates a service item to make sure it is valid - :param set[str] suffixes: A set of vaild suffixes + :param set[str] suffixes: A set of valid suffixes """ self.is_valid = True for slide in self.slides: @@ -649,7 +654,7 @@ class ServiceItem(RegistryProperties): self.is_valid = False break if suffixes and not self.is_text(): - file_suffix = slide['title'].split('.')[-1] + file_suffix = "*.{suffx}".format(suffx=slide['title'].split('.')[-1]) if file_suffix.lower() not in suffixes: self.is_valid = False break diff --git a/openlp/core/ui/media/__init__.py b/openlp/core/ui/media/__init__.py index 588088c56..52662c7f6 100644 --- a/openlp/core/ui/media/__init__.py +++ b/openlp/core/ui/media/__init__.py @@ -70,7 +70,7 @@ class ItemMediaInfo(object): file_info = None volume = 100 is_background = False - can_loop_playback = False + is_looping_playback = False length = 0 start_time = 0 end_time = 0 @@ -91,7 +91,7 @@ def parse_optical_path(input_string): """ log.debug('parse_optical_path, about to parse: "{text}"'.format(text=input_string)) clip_info = input_string.split(sep=':') - title = int(clip_info[1]) + title = str(clip_info[1]) audio_track = int(clip_info[2]) subtitle_track = int(clip_info[3]) start = float(clip_info[4]) diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py index 9367f390a..03ddac1e3 100644 --- a/openlp/core/ui/media/mediacontroller.py +++ b/openlp/core/ui/media/mediacontroller.py @@ -64,6 +64,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): self.live_timer.setInterval(TICK_TIME) self.preview_timer = QtCore.QTimer() self.preview_timer.setInterval(TICK_TIME) + self.settings = Settings() # Signals self.live_timer.timeout.connect(self.media_state_live) self.preview_timer.timeout.connect(self.media_state_preview) @@ -116,13 +117,13 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): """ if State().check_preconditions('mediacontroller'): try: - self.setup_display(self.live_controller.display, False) + self.setup_display(self.live_controller, False) except AttributeError: State().update_pre_conditions('media_live', False) State().missing_text('media_live', translate( 'OpenLP.MediaController', 'No Displays have been configured, so Live Media has been disabled')) - self.setup_display(self.preview_controller.preview_display, True) + self.setup_display(self.preview_controller, True) def display_controllers(self, controller_type): """ @@ -140,7 +141,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): """ display = self._define_display(self.display_controllers(DisplayControllerType.Live)) if DisplayControllerType.Live in self.current_media_players: - self.current_media_players[DisplayControllerType.Live].resize(display) + self.current_media_players[DisplayControllerType.Live].resize(self.live_controller) self.current_media_players[DisplayControllerType.Live].update_ui(self.live_controller, display) self.tick(self.display_controllers(DisplayControllerType.Live)) if self.current_media_players[DisplayControllerType.Live].get_live_state() is not MediaState.Playing: @@ -148,8 +149,6 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): else: self.live_timer.stop() self.media_stop(self.display_controllers(DisplayControllerType.Live)) - if self.display_controllers(DisplayControllerType.Live).media_info.can_loop_playback: - self.media_play(self.display_controllers(DisplayControllerType.Live), True) def media_state_preview(self): """ @@ -157,7 +156,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): """ display = self._define_display(self.display_controllers(DisplayControllerType.Preview)) if DisplayControllerType.Preview in self.current_media_players: - self.current_media_players[DisplayControllerType.Preview].resize(display) + self.current_media_players[DisplayControllerType.Preview].resize(self.live_controller) self.current_media_players[DisplayControllerType.Preview].update_ui(self.preview_controller, display) self.tick(self.display_controllers(DisplayControllerType.Preview)) if self.current_media_players[DisplayControllerType.Preview].get_preview_state() is not MediaState.Playing: @@ -165,23 +164,19 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): else: self.preview_timer.stop() self.media_stop(self.display_controllers(DisplayControllerType.Preview)) - if self.display_controllers(DisplayControllerType.Preview).media_info.can_loop_playback: - self.media_play(self.display_controllers(DisplayControllerType.Preview), True) - def setup_display(self, display, preview): + def setup_display(self, controller, preview): """ After a new display is configured, all media related widgets will be created too - :param display: Display on which the output is to be played + :param controller: Display on which the output is to be played :param preview: Whether the display is a main or preview display """ - display.media_info = ItemMediaInfo() - display.has_audio = True - # if display.is_live and preview: - # return + controller.media_info = ItemMediaInfo() + controller.has_audio = True if preview: - display.has_audio = False - self.vlc_player.setup(display, preview) + controller.has_audio = False + self.vlc_player.setup(controller, self._define_display(controller), preview) @staticmethod def set_controls_visible(controller, value): @@ -195,33 +190,33 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): controller.mediabar.setVisible(value) @staticmethod - def resize(display, player): + def resize(controller, player): """ After Mainwindow changes or Splitter moved all related media widgets have to be resized - :param display: The display on which output is playing. + :param controller: The display on which output is playing. :param player: The player which is doing the playing. """ - player.resize(display) + player.resize(controller) - def load_video(self, source, service_item, hidden=False, video_behind_text=False): + def load_video(self, source, service_item, hidden=False): """ - Loads and starts a video to run with the option of sound + Loads and starts a video to run and sets the stored sound value. :param source: Where the call originated form :param service_item: The player which is doing the playing :param hidden: The player which is doing the playing - :param video_behind_text: Is the video to be played behind text. """ is_valid = True controller = self.display_controllers(source) # stop running videos self.media_reset(controller) controller.media_info = ItemMediaInfo() - controller.media_info.volume = controller.volume_slider.value() - controller.media_info.is_background = video_behind_text + 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. - controller.media_info.can_loop_playback = video_behind_text if service_item.is_capable(ItemCapabilities.HasBackgroundAudio): controller.media_info.file_info = service_item.background_audio else: @@ -242,15 +237,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): log.debug('video is not optical and live') controller.media_info.length = service_item.media_length is_valid = self._check_file_type(controller, display) - # display.override['theme'] = '' - # display.override['video'] = True - if controller.media_info.is_background: - # ignore start/end time - controller.media_info.start_time = 0 - controller.media_info.end_time = 0 - else: - controller.media_info.start_time = service_item.start_time - controller.media_info.end_time = service_item.end_time + controller.media_info.start_time = service_item.start_time + controller.media_info.end_time = service_item.end_time elif controller.preview_display: if service_item.is_capable(ItemCapabilities.CanStream): controller.media_info.media_type = MediaType.Stream @@ -280,10 +268,10 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): if not controller.is_live: autoplay = True # Visible or background requested or Service Item wants to autostart - elif not hidden or controller.media_info.is_background or service_item.will_auto_start: + elif not hidden or service_item.will_auto_start: autoplay = True # Unblank on load set - elif Settings().value('core/auto unblank'): + elif self.settings.value('core/auto unblank'): autoplay = True if autoplay: if not self.media_play(controller): @@ -344,7 +332,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): if display is None: display = controller.preview_display self.vlc_player.load(display, filename) - self.resize(display, self.vlc_player) + self.resize(controller, self.vlc_player) self.current_media_players[controller.controller_type] = self.vlc_player if audio_track == -1 and subtitle_track == -1: controller.media_info.media_type = MediaType.CD @@ -361,9 +349,9 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): :param stream: Are we streaming or not """ if stream: - self.resize(display, self.vlc_player) - display.media_info.media_type = MediaType.Stream - if self.vlc_player.load(display, None): + self.resize(controller, self.vlc_player) + controller.media_info.media_type = MediaType.Stream + if self.vlc_player.load(controller, display, None): self.current_media_players[controller.controller_type] = self.vlc_player return True return True @@ -372,23 +360,21 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): suffix = '*%s' % file.suffix.lower() file = str(file) if suffix in VIDEO_EXT: - if not controller.media_info.is_background or controller.media_info.is_background and \ - self.vlc_player.can_background: - self.resize(display, self.vlc_player) - if self.vlc_player.load(display, file): - self.current_media_players[controller.controller_type] = self.vlc_player - controller.media_info.media_type = MediaType.Video - return True + 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 if suffix in AUDIO_EXT: - if self.vlc_player.load(display, file): + 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(display, self.vlc_player) - if self.vlc_player.load(display, file): + 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 @@ -423,19 +409,14 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): controller.seek_slider.blockSignals(False) controller.volume_slider.blockSignals(False) return False - if controller.media_info.is_background: - self.media_volume(controller, 0) - else: - self.media_volume(controller, controller.media_info.volume) + self.media_volume(controller, controller.media_info.volume) if first_time: - # if not controller.media_info.is_background: - # display.frame.runJavaScript('show_blank("desktop");') - self.current_media_players[controller.controller_type].set_visible(display, True) + self.current_media_players[controller.controller_type].set_visible(controller, True) controller.mediabar.actions['playbackPlay'].setVisible(False) controller.mediabar.actions['playbackPause'].setVisible(True) controller.mediabar.actions['playbackStop'].setDisabled(False) if controller.is_live: - if controller.hide_menu.defaultAction().isChecked() and not controller.media_info.is_background: + if controller.hide_menu.defaultAction().isChecked(): controller.hide_menu.defaultAction().trigger() # Start Timer for ui updates if not self.live_timer.isActive(): @@ -448,7 +429,11 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): controller.volume_slider.blockSignals(False) controller.media_info.is_playing = True display = self._define_display(controller) - display.setVisible(True) + if controller.is_live: + display.setVisible(False) + controller.preview_display.hide() + else: + display.setVisible(True) return True def tick(self, controller): @@ -460,8 +445,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): start_again = False if controller.media_info.is_playing and controller.media_info.length > 0: if controller.media_info.timer > controller.media_info.length: - self.media_stop(controller, True) - if controller.media_info.can_loop_playback: + self.media_stop(controller) + if controller.media_info.is_looping_playback: start_again = True controller.media_info.timer += TICK_TIME seconds = controller.media_info.timer // 1000 @@ -495,9 +480,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): :param controller: The Controller to be paused """ - display = self._define_display(controller) if controller.controller_type in self.current_media_players: - self.current_media_players[controller.controller_type].pause(display) + self.current_media_players[controller.controller_type].pause(controller) controller.mediabar.actions['playbackPlay'].setVisible(True) controller.mediabar.actions['playbackStop'].setDisabled(False) controller.mediabar.actions['playbackPause'].setVisible(False) @@ -518,8 +502,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): :param controller: The controller that needs to be stopped """ - controller.media_info.can_loop_playback = not controller.media_info.can_loop_playback - controller.mediabar.actions['playbackLoop'].setChecked(controller.media_info.can_loop_playback) + controller.media_info.is_looping_playback = not controller.media_info.is_looping_playback + controller.mediabar.actions['playbackLoop'].setChecked(controller.media_info.is_looping_playback) def media_stop_msg(self, msg): """ @@ -535,19 +519,16 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): """ self.media_stop(Registry().get('live_controller')) - def media_stop(self, controller, looping_background=False): + def media_stop(self, controller): """ Responds to the request to stop a loaded video :param controller: The controller that needs to be stopped - :param looping_background: The background is looping so do not blank. """ - display = self._define_display(controller) if controller.controller_type in self.current_media_players: - # if not looping_background: - # display.frame.runJavaScript('show_blank("black");') - self.current_media_players[controller.controller_type].stop(display) - self.current_media_players[controller.controller_type].set_visible(display, False) + self.current_media_players[controller.controller_type].stop(controller) + self.current_media_players[controller.controller_type].set_visible(controller, False) + controller.preview_display.hide() controller.seek_slider.setSliderPosition(0) total_seconds = controller.media_info.length // 1000 total_minutes = total_seconds // 60 @@ -578,8 +559,12 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): :param volume: The volume to be set """ log.debug('media_volume {vol}'.format(vol=volume)) - display = self._define_display(controller) - self.current_media_players[controller.controller_type].volume(display, volume) + if controller.is_live: + 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) controller.volume_slider.setValue(volume) def media_seek_msg(self, msg): @@ -600,8 +585,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): :param controller: The controller to use. :param seek_value: The value to set. """ - display = self._define_display(controller) - self.current_media_players[controller.controller_type].seek(display, seek_value) + self.current_media_players[controller.controller_type].seek(controller, seek_value) controller.media_info.timer = seek_value def media_reset(self, controller): @@ -610,12 +594,9 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): :param controller: The controller to use. """ self.set_controls_visible(controller, False) - display = self._define_display(controller) if controller.controller_type in self.current_media_players: - display.override = {} - self.current_media_players[controller.controller_type].reset(display) - self.current_media_players[controller.controller_type].set_visible(display, False) - # display.frame.runJavaScript('show_video("setBackBoard", null, null, "hidden");') + self.current_media_players[controller.controller_type].reset(controller) + self.current_media_players[controller.controller_type].set_visible(controller, False) del self.current_media_players[controller.controller_type] def media_hide(self, msg): @@ -631,7 +612,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): 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.media_pause(display.controller) - self.current_media_players[self.live_controller.controller_type].set_visible(display, False) + self.current_media_players[self.live_controller.controller_type].set_visible(self.live_controller, False) def media_blank(self, msg): """ @@ -649,7 +630,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): 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.media_pause(display.controller) - self.current_media_players[self.live_controller.controller_type].set_visible(display, False) + self.current_media_players[self.live_controller.controller_type].set_visible(self.live_controller, False) def media_unblank(self, msg): """ @@ -667,7 +648,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): self.current_media_players[self.live_controller.controller_type].get_live_state() != \ MediaState.Playing: if self.media_play(display.controller): - self.current_media_players[self.live_controller.controller_type].set_visible(display, True) + self.current_media_players[self.live_controller.controller_type].set_visible(self.live_controller, True) # Start Timer for ui updates if not self.live_timer.isActive(): self.live_timer.start() diff --git a/openlp/core/ui/media/mediaplayer.py b/openlp/core/ui/media/mediaplayer.py index f031eebfb..fc0bbb93b 100644 --- a/openlp/core/ui/media/mediaplayer.py +++ b/openlp/core/ui/media/mediaplayer.py @@ -48,7 +48,7 @@ class MediaPlayer(RegistryProperties): """ return False - def setup(self, display, live_display): + def setup(self, controller, display, live_display): """ Create the related widgets for the current display @@ -57,10 +57,10 @@ class MediaPlayer(RegistryProperties): """ pass - def load(self, display, file): + def load(self, controller, display, file): """ Load a new media file and check if it is valid - + :param controller: Which Controller is running the show. :param display: The display to be updated. :param file: The file to be loaded """ @@ -70,7 +70,6 @@ class MediaPlayer(RegistryProperties): """ If the main display size or position is changed, the media widgets should also resized - :param display: The display to be updated. """ pass @@ -192,14 +191,14 @@ class MediaPlayer(RegistryProperties): """ self.state[1] = state - def set_state(self, state, display): + def set_state(self, state, controller): """ - Set the State based on the display being processed + Set the State based on the display being processed within the controller :param state: State to be set - :param display: Identify the Display type + :param controller: Identify the Display type :return: None """ - if display.is_display: + if controller.is_live: self.set_live_state(state) else: self.set_preview_state(state) diff --git a/openlp/core/ui/media/vlcplayer.py b/openlp/core/ui/media/vlcplayer.py index efd5a9ef1..91a0c89e2 100644 --- a/openlp/core/ui/media/vlcplayer.py +++ b/openlp/core/ui/media/vlcplayer.py @@ -27,6 +27,7 @@ import os import sys import threading from datetime import datetime +from time import sleep from PyQt5 import QtCore, QtWidgets @@ -95,50 +96,49 @@ class VlcPlayer(MediaPlayer): self.parent = parent self.can_folder = True - def setup(self, output_display, live_display): + def setup(self, controller, display, live_display): """ Set up the media player - :param output_display: The display where the media is + :param controller: The display where the media is :param live_display: Is the display a live one. :return: """ vlc = get_vlc() - if output_display.is_display: - output_display.vlc_widget = QtWidgets.QFrame() - output_display.vlc_widget.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.Tool | - QtCore.Qt.WindowStaysOnTopHint) + if controller.is_live: + controller.vlc_widget = QtWidgets.QFrame() + controller.vlc_widget.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.Tool | + QtCore.Qt.WindowStaysOnTopHint) else: - output_display.vlc_widget = QtWidgets.QFrame(output_display) - output_display.vlc_widget.setFrameStyle(QtWidgets.QFrame.NoFrame) - + controller.vlc_widget = QtWidgets.QFrame(display) + controller.vlc_widget.setFrameStyle(QtWidgets.QFrame.NoFrame) # creating a basic vlc instance command_line_options = '--no-video-title-show ' if Settings().value('advanced/hide mouse') and live_display: command_line_options += '--mouse-hide-timeout=0 ' if Settings().value('media/vlc arguments'): command_line_options += Settings().value('media/vlc arguments') - output_display.vlc_instance = vlc.Instance(command_line_options) + controller.vlc_instance = vlc.Instance(command_line_options) # creating an empty vlc media player - output_display.vlc_media_player = output_display.vlc_instance.media_player_new() - output_display.vlc_widget.resize(output_display.size()) - output_display.vlc_widget.raise_() - output_display.vlc_widget.hide() + controller.vlc_media_player = controller.vlc_instance.media_player_new() + controller.vlc_widget.resize(controller.size()) + controller.vlc_widget.raise_() + controller.vlc_widget.hide() # The media player has to be 'connected' to the QFrame. # (otherwise a video would be displayed in it's own window) # This is platform specific! # You have to give the id of the QFrame (or similar object) # to vlc, different platforms have different functions for this. - win_id = int(output_display.vlc_widget.winId()) + win_id = int(controller.vlc_widget.winId()) if is_win(): - output_display.vlc_media_player.set_hwnd(win_id) + controller.vlc_media_player.set_hwnd(win_id) elif is_macosx(): # We have to use 'set_nsobject' since Qt5 on OSX uses Cocoa # framework and not the old Carbon. - output_display.vlc_media_player.set_nsobject(win_id) + controller.vlc_media_player.set_nsobject(win_id) else: # for Linux/*BSD using the X Server - output_display.vlc_media_player.set_xwindow(win_id) + controller.vlc_media_player.set_xwindow(win_id) self.has_own_widget = True def check_available(self): @@ -147,74 +147,77 @@ class VlcPlayer(MediaPlayer): """ return get_vlc() is not None - def load(self, output_display, file): + def load(self, controller, output_display, file): """ Load a video into VLC + :param controller: The controller where the media is :param output_display: The display where the media is :param file: file to be played or None for live streaming :return: """ vlc = get_vlc() log.debug('load vid in Vlc Controller') + path = None if file: path = os.path.normcase(file) # create the media - if output_display.media_info.media_type == MediaType.CD: + if controller.media_info.media_type == MediaType.CD: if is_win(): path = '/' + path - output_display.vlc_media = output_display.vlc_instance.media_new_location('cdda://' + path) - output_display.vlc_media_player.set_media(output_display.vlc_media) - output_display.vlc_media_player.play() + controller.vlc_media = controller.vlc_instance.media_new_location('cdda://' + path) + controller.vlc_media_player.set_media(controller.vlc_media) + controller.vlc_media_player.play() # Wait for media to start playing. In this case VLC actually returns an error. self.media_state_wait(output_display, vlc.State.Playing) # If subitems exists, this is a CD - audio_cd_tracks = output_display.vlc_media.subitems() + audio_cd_tracks = controller.vlc_media.subitems() if not audio_cd_tracks or audio_cd_tracks.count() < 1: return False - output_display.vlc_media = audio_cd_tracks.item_at_index(output_display.media_info.title_track) - elif output_display.media_info.media_type == MediaType.Stream: + controller.vlc_media_player = audio_cd_tracks.item_at_index(controller.media_info.title_track) + elif controller.media_info.media_type == MediaType.Stream: stream_cmd = Settings().value('media/stream command') - output_display.vlc_media = output_display.vlc_instance.media_new_location(stream_cmd) + controller.vlc_media = controller.vlc_instance.media_new_location(stream_cmd) else: - output_display.vlc_media = output_display.vlc_instance.media_new_path(path) + controller.vlc_media = controller.vlc_instance.media_new_path(path) # put the media in the media player - output_display.vlc_media_player.set_media(output_display.vlc_media) + controller.vlc_media_player.set_media(controller.vlc_media) # parse the metadata of the file - output_display.vlc_media.parse() - self.volume(output_display, output_display.media_info.volume) + controller.vlc_media.parse() + self.volume(controller, controller.media_info.volume) return True - def media_state_wait(self, output_display, media_state): + def media_state_wait(self, controller, media_state): """ Wait for the video to change its state Wait no longer than 60 seconds. (loading an iso file needs a long time) :param media_state: The state of the playing media - :param output_display: The display where the media is + :param controller: The controller where the media is :return: """ vlc = get_vlc() start = datetime.now() - while media_state != output_display.vlc_media.get_state(): - if output_display.vlc_media.get_state() == vlc.State.Error: + while media_state != controller.vlc_media.get_state(): + sleep(0.1) + if controller.vlc_media.get_state() == vlc.State.Error: return False self.application.process_events() if (datetime.now() - start).seconds > 60: return False return True - def resize(self, output_display): + def resize(self, controller): """ Resize the player - :param output_display: The display where the media is + :param controller: The display where the media is stored within the controller. :return: """ - if output_display.is_display: - output_display.vlc_widget.setGeometry(ScreenList().current.display_geometry) + if controller.is_live: + controller.vlc_widget.setGeometry(ScreenList().current.display_geometry) else: - output_display.vlc_widget.resize(output_display.size()) + controller.vlc_widget.resize(controller.preview_display.size()) def play(self, controller, output_display): """ @@ -227,119 +230,117 @@ class VlcPlayer(MediaPlayer): vlc = get_vlc() start_time = 0 log.debug('vlc play') - if output_display.is_display: - if self.get_live_state() != MediaState.Paused and output_display.media_info.start_time > 0: - start_time = output_display.media_info.start_time + if controller.is_live: + if self.get_live_state() != MediaState.Paused and controller.media_info.start_time > 0: + start_time = controller.media_info.start_time else: - if self.get_preview_state() != MediaState.Paused and output_display.media_info.start_time > 0: - start_time = output_display.media_info.start_time - threading.Thread(target=output_display.vlc_media_player.play).start() - if not self.media_state_wait(output_display, vlc.State.Playing): + if self.get_preview_state() != MediaState.Paused and controller.media_info.start_time > 0: + start_time = controller.media_info.start_time + threading.Thread(target=controller.vlc_media_player.play).start() + if not self.media_state_wait(controller, vlc.State.Playing): return False - if output_display.is_display: - if self.get_live_state() != MediaState.Paused and output_display.media_info.start_time > 0: + if controller.is_live: + if self.get_live_state() != MediaState.Paused and controller.media_info.start_time > 0: log.debug('vlc play, start time set') - start_time = output_display.media_info.start_time + start_time = controller.media_info.start_time else: - if self.get_preview_state() != MediaState.Paused and output_display.media_info.start_time > 0: + if self.get_preview_state() != MediaState.Paused and controller.media_info.start_time > 0: log.debug('vlc play, start time set') - start_time = output_display.media_info.start_time - log.debug('mediatype: ' + str(output_display.media_info.media_type)) + start_time = controller.media_info.start_time + log.debug('mediatype: ' + str(controller.media_info.media_type)) # Set tracks for the optical device - if output_display.media_info.media_type == MediaType.DVD and \ + if controller.media_info.media_type == MediaType.DVD and \ self.get_live_state() != MediaState.Paused and self.get_preview_state() != MediaState.Paused: log.debug('vlc play, playing started') - if output_display.media_info.title_track > 0: - log.debug('vlc play, title_track set: ' + str(output_display.media_info.title_track)) - output_display.vlc_media_player.set_title(output_display.media_info.title_track) - output_display.vlc_media_player.play() - if not self.media_state_wait(output_display, vlc.State.Playing): + if controller.media_info.title_track > 0: + log.debug('vlc play, title_track set: ' + str(controller.media_info.title_track)) + controller.vlc_media_player.set_title(controller.media_info.title_track) + controller.vlc_media_player.play() + if not self.media_state_wait(controller, vlc.State.Playing): return False - if output_display.media_info.audio_track > 0: - output_display.vlc_media_player.audio_set_track(output_display.media_info.audio_track) - log.debug('vlc play, audio_track set: ' + str(output_display.media_info.audio_track)) - if output_display.media_info.subtitle_track > 0: - output_display.vlc_media_player.video_set_spu(output_display.media_info.subtitle_track) - log.debug('vlc play, subtitle_track set: ' + str(output_display.media_info.subtitle_track)) - if output_display.media_info.start_time > 0: - log.debug('vlc play, starttime set: ' + str(output_display.media_info.start_time)) - start_time = output_display.media_info.start_time - output_display.media_info.length = output_display.media_info.end_time - output_display.media_info.start_time - self.volume(output_display, output_display.media_info.volume) - if start_time > 0 and output_display.vlc_media_player.is_seekable(): - output_display.vlc_media_player.set_time(int(start_time)) + if controller.media_info.audio_track > 0: + controller.vlc_media_player.audio_set_track(controller.media_info.audio_track) + log.debug('vlc play, audio_track set: ' + str(controller.media_info.audio_track)) + if controller.media_info.subtitle_track > 0: + 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)) + if controller.media_info.start_time > 0: + log.debug('vlc play, starttime set: ' + str(controller.media_info.start_time)) + start_time = controller.media_info.start_time + controller.media_info.length = controller.media_info.end_time - controller.media_info.start_time + self.volume(controller, controller.media_info.volume) + if start_time > 0 and controller.vlc_media_player.is_seekable(): + controller.vlc_media_player.set_time(int(start_time)) controller.seek_slider.setMaximum(controller.media_info.length) - self.set_state(MediaState.Playing, output_display) - output_display.vlc_widget.raise_() + self.set_state(MediaState.Playing, controller) + controller.vlc_widget.raise_() return True - def pause(self, output_display): + def pause(self, controller): """ Pause the current item - :param output_display: The display where the media is + :param controller: The controller which is managing the display :return: """ vlc = get_vlc() - if output_display.vlc_media.get_state() != vlc.State.Playing: + if controller.vlc_media.get_state() != vlc.State.Playing: return - output_display.vlc_media_player.pause() - if self.media_state_wait(output_display, vlc.State.Paused): - self.set_state(MediaState.Paused, output_display) + controller.vlc_media_player.pause() + if self.media_state_wait(controller, vlc.State.Paused): + self.set_state(MediaState.Paused, controller) - def stop(self, output_display): + def stop(self, controller): """ Stop the current item - :param output_display: The display where the media is + :param controller: The controller where the media is :return: """ - threading.Thread(target=output_display.vlc_media_player.stop).start() - self.set_state(MediaState.Stopped, output_display) + threading.Thread(target=controller.vlc_media_player.stop).start() + self.set_state(MediaState.Stopped, controller) - def volume(self, output_display, vol): + def volume(self, controller, vol): """ Set the volume :param vol: The volume to be sets - :param output_display: The display where the media is + :param controller: The controller where the media is :return: """ - if output_display.has_audio: - output_display.vlc_media_player.audio_set_volume(vol) + controller.vlc_media_player.audio_set_volume(vol) - def seek(self, output_display, seek_value): + def seek(self, controller, seek_value): """ Go to a particular position :param seek_value: The position of where a seek goes to - :param output_display: The display where the media is + :param controller: The controller where the media is """ - if output_display.media_info.media_type == MediaType.CD \ - or output_display.media_info.media_type == MediaType.DVD: - seek_value += int(output_display.media_info.start_time) - if output_display.vlc_media_player.is_seekable(): - output_display.vlc_media_player.set_time(seek_value) + if controller.media_info.media_type == MediaType.CD \ + or controller.media_info.media_type == MediaType.DVD: + seek_value += int(controller.media_info.start_time) + if controller.vlc_media_player.is_seekable(): + controller.vlc_media_player.set_time(seek_value) - def reset(self, output_display): + def reset(self, controller): """ Reset the player - :param output_display: The display where the media is + :param controller: The controller where the media is """ - output_display.vlc_media_player.stop() - output_display.vlc_widget.setVisible(False) - self.set_state(MediaState.Off, output_display) + controller.vlc_media_player.stop() + controller.vlc_widget.setVisible(False) + self.set_state(MediaState.Off, controller) - def set_visible(self, output_display, status): + def set_visible(self, controller, status): """ Set the visibility - :param output_display: The display where the media is + :param controller: The controller where the media display is :param status: The visibility status """ - if self.has_own_widget: - output_display.vlc_widget.setVisible(status) + controller.vlc_widget.setVisible(status) def update_ui(self, controller, output_display): """ @@ -350,18 +351,18 @@ class VlcPlayer(MediaPlayer): """ vlc = get_vlc() # Stop video if playback is finished. - if output_display.vlc_media.get_state() == vlc.State.Ended: - self.stop(output_display) + if controller.vlc_media.get_state() == vlc.State.Ended: + self.stop(controller) if controller.media_info.end_time > 0: - if output_display.vlc_media_player.get_time() > controller.media_info.end_time: - self.stop(output_display) - self.set_visible(output_display, False) + 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(): controller.seek_slider.blockSignals(True) if controller.media_info.media_type == MediaType.CD \ or controller.media_info.media_type == MediaType.DVD: controller.seek_slider.setSliderPosition( - output_display.vlc_media_player.get_time() - int(output_display.controller.media_info.start_time)) + controller.vlc_media_player.get_time() - int(controller.media_info.start_time)) else: - controller.seek_slider.setSliderPosition(output_display.vlc_media_player.get_time()) + controller.seek_slider.setSliderPosition(controller.vlc_media_player.get_time()) controller.seek_slider.blockSignals(False) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 3fb9d421d..8a063992e 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -1241,7 +1241,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi child = QtWidgets.QTreeWidgetItem(tree_widget_item) # prefer to use a display_title if service_item_from_item.is_capable(ItemCapabilities.HasDisplayTitle) or \ - service_item_from_item.service_item_type == ServiceItemType.Image: + service_item_from_item.service_item_type is not ServiceItemType.Text: text = slide['title'].replace('\n', ' ') else: text = service_item_from_item.get_rendered_frame(slide_index) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 5fb4ad7da..e0ea58010 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -402,8 +402,6 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties): self.mediabar.add_toolbar_widget(self.volume_slider) self.controller_layout.addWidget(self.mediabar) self.mediabar.setVisible(False) - if not self.is_live: - self.volume_slider.setEnabled(False) # Signals self.seek_slider.valueChanged.connect(self.send_to_plugins) self.volume_slider.valueChanged.connect(self.send_to_plugins) @@ -885,6 +883,7 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties): width = self.main_window.control_splitter.sizes()[self.split] if self.service_item.is_text(): self.preview_display.load_verses(service_item.rendered_slides) + self.preview_display.show() for display in self.displays: display.load_verses(service_item.rendered_slides) for slide_index, slide in enumerate(self.service_item.display_slides): @@ -909,17 +908,8 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties): for slide_index, slide in enumerate(self.service_item.slides): row += 1 self.slide_list[str(row)] = row - 1 - # If current slide set background to image - # if not self.service_item.is_command() and slide_index == slide_no: - # self.service_item.bg_image_bytes = \ - # self.image_manager.get_image_bytes(slide['filename'], ImageSource.ImagePlugin) self.preview_widget.replace_service_item(self.service_item, width, slide_no) self.enable_tool_bar(self.service_item) - # Pass to display for viewing. - # Postpone image build, we need to do this later to avoid the theme - # flashing on the screen - # if not self.service_item.is_image(): - # self.display.build_html(self.service_item) if self.service_item.is_media(): self.on_media_start(self.service_item) self.slide_selected(True) @@ -1397,25 +1387,6 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties): self.play_slides_once.setText(UiStrings().PlaySlidesToEnd) self.on_toggle_loop() - # def set_audio_items_visibility(self, visible): - # """ - # Set the visibility of the audio stuff - # """ - # self.toolbar.set_widget_visible(AUDIO_LIST, visible) - - # def set_audio_pause_clicked(self, checked): - # """ - # Pause the audio player - - # :param checked: is the check box checked. - # """ - # if not self.audio_pause_item.isVisible(): - # return - # if checked: - # self.display.audio_player.pause() - # else: - # self.display.audio_player.play() - def timerEvent(self, event): """ If the timer event is for this window select next slide @@ -1511,12 +1482,10 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties): Respond to a request to close the Video """ self.media_controller.media_reset(self) - self.preview_display.hide() def _reset_blank(self, no_theme): """ - Used by command items which provide their own displays to reset the - screen hide attributes + Used by command items which provide their own displays to reset the screen hide attributes :param no_theme: Does the new item support theme-blanking. """ @@ -1549,30 +1518,6 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties): else: return None - # def on_next_track_clicked(self): - # """ - # Go to the next track when next is clicked - # """ - # self.display.audio_player.next() - # - # def on_audio_time_remaining(self, time): - # """ - # Update how much time is remaining - # - # :param time: the time remaining - # """ - # seconds = (self.display.audio_player.player.duration() - self.display.audio_player.player.position()) // 1000 - # minutes = seconds // 60 - # seconds %= 60 - # self.audio_time_label.setText(' %02d:%02d ' % (minutes, seconds)) - # - # def on_track_triggered(self, field=None): - # """ - # Start playing a track - # """ - # action = self.sender() - # self.display.audio_player.go_to(action.data()) - class PreviewController(RegistryBase, SlideController): """ diff --git a/openlp/core/ui/themewizard.py b/openlp/core/ui/themewizard.py index 5738a70c5..b1fcc578b 100644 --- a/openlp/core/ui/themewizard.py +++ b/openlp/core/ui/themewizard.py @@ -71,7 +71,7 @@ class Ui_ThemeWizard(object): self.background_label = QtWidgets.QLabel(self.background_page) self.background_label.setObjectName('background_label') self.background_combo_box = QtWidgets.QComboBox(self.background_page) - self.background_combo_box.addItems(['', '', '', '', '', '']) + self.background_combo_box.addItems(['', '', '', '']) self.background_combo_box.setObjectName('background_combo_box') self.background_type_layout.addRow(self.background_label, self.background_combo_box) self.background_type_layout.setItem(1, QtWidgets.QFormLayout.LabelRole, self.spacer) @@ -421,11 +421,8 @@ class Ui_ThemeWizard(object): self.background_combo_box.setItemText(BackgroundType.Solid, translate('OpenLP.ThemeWizard', 'Solid color')) self.background_combo_box.setItemText(BackgroundType.Gradient, translate('OpenLP.ThemeWizard', 'Gradient')) self.background_combo_box.setItemText(BackgroundType.Image, UiStrings().Image) - self.background_combo_box.setItemText(BackgroundType.Video, UiStrings().Video) self.background_combo_box.setItemText(BackgroundType.Transparent, translate('OpenLP.ThemeWizard', 'Transparent')) - self.background_combo_box.setItemText(BackgroundType.Stream, - translate('OpenLP.ThemeWizard', 'Live Stream')) self.color_label.setText(translate('OpenLP.ThemeWizard', 'color:')) self.gradient_start_label.setText(translate('OpenLP.ThemeWizard', 'Starting color:')) self.gradient_end_label.setText(translate('OpenLP.ThemeWizard', 'Ending color:')) diff --git a/openlp/plugins/media/forms/mediaclipselectorform.py b/openlp/plugins/media/forms/mediaclipselectorform.py index 504e4d196..d3b912999 100644 --- a/openlp/plugins/media/forms/mediaclipselectorform.py +++ b/openlp/plugins/media/forms/mediaclipselectorform.py @@ -256,7 +256,7 @@ class MediaClipSelectorForm(QtWidgets.QDialog, Ui_MediaClipSelector, RegistryPro titles = self.vlc_media_player.video_get_title_description() self.titles_combo_box.clear() for title in titles: - self.titles_combo_box.addItem(title[1].decode(), title[0]) + self.titles_combo_box.addItem(title.name.decode(), title.id) # Re-enable signals self.blockSignals(False) # Main title is usually title #1 @@ -626,6 +626,7 @@ class MediaClipSelectorForm(QtWidgets.QDialog, Ui_MediaClipSelector, RegistryPro vlc = get_vlc() start = datetime.now() while media_state != self.vlc_media_player.get_state(): + sleep(0.1) if self.vlc_media_player.get_state() == vlc.State.Error: return False if (datetime.now() - start).seconds > 15: diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 725f45755..3bd83a357 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -240,7 +240,10 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties): track_str = str(track) track_info = QtCore.QFileInfo(track_str) item_name = None - if track_str.startswith('optical:'): + # Dont add the live stream in when reloading the UI. + if track_str == UiStrings().LiveStream: + continue + elif track_str.startswith('optical:'): # Handle optical based item (file_name, title, audio_track, subtitle_track, start, end, clip_name) = parse_optical_path(track_str) item_name = QtWidgets.QListWidgetItem(clip_name) diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index 6170fd78e..7e53d310a 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -152,7 +152,7 @@ class ImpressController(PresentationController): self.toggle_presentation_screen(False) return desktop except Exception: - log.warning('Failed to get UNO desktop') + log.exception('Failed to get UNO desktop') return None def get_com_desktop(self): @@ -232,7 +232,7 @@ class ImpressController(PresentationController): self.conf_provider = self.manager.createInstanceWithContext( 'com.sun.star.configuration.ConfigurationProvider', uno.getComponentContext()) # Setup lookup properties to get Impress settings - properties = tuple(self.create_property('nodepath', 'org.openoffice.Office.Impress')) + properties = (self.create_property('nodepath', 'org.openoffice.Office.Impress'),) try: # Get an updateable configuration view impress_conf_props = self.conf_provider.createInstanceWithArguments( @@ -308,7 +308,7 @@ class ImpressDocument(PresentationDocument): if desktop is None: return False self.desktop = desktop - properties = tuple(self.controller.create_property('Hidden', True)) + properties = (self.controller.create_property('Hidden', True),) try: self.document = desktop.loadComponentFromURL(url, '_blank', 0, properties) except Exception: @@ -333,7 +333,7 @@ class ImpressDocument(PresentationDocument): return temp_folder_path = self.get_temp_folder() thumb_dir_url = temp_folder_path.as_uri() - properties = tuple(self.controller.create_property('FilterName', 'impress_png_Export')) + properties = (self.controller.create_property('FilterName', 'impress_png_Export'),) doc = self.document pages = doc.getDrawPages() if not pages: diff --git a/openlp/plugins/songs/lib/importers/openoffice.py b/openlp/plugins/songs/lib/importers/openoffice.py index 7a831c02b..79c09a8db 100644 --- a/openlp/plugins/songs/lib/importers/openoffice.py +++ b/openlp/plugins/songs/lib/importers/openoffice.py @@ -144,7 +144,7 @@ class OpenOfficeImport(SongImport): """ self.file_path = file_path url = file_path.as_uri() - properties = tuple(self.create_property('Hidden', True)) + properties = (self.create_property('Hidden', True),) try: self.document = self.desktop.loadComponentFromURL(url, '_blank', 0, properties) if not self.document.supportsService("com.sun.star.presentation.PresentationDocument") and not \ diff --git a/tests/functional/openlp_core/ui/test_media.py b/tests/functional/openlp_core/ui/media/test_media.py similarity index 94% rename from tests/functional/openlp_core/ui/test_media.py rename to tests/functional/openlp_core/ui/media/test_media.py index f4dc8352c..77855518c 100644 --- a/tests/functional/openlp_core/ui/test_media.py +++ b/tests/functional/openlp_core/ui/media/test_media.py @@ -138,9 +138,9 @@ class TestMedia(TestCase, TestMixin): (device_path, title_track, audio_track, subtitle_track, start, end, name) = parse_optical_path(path) # THEN: The return values should match the original values - assert org_title_track == title_track, 'Returned title_track should match the original' + assert org_title_track == int(title_track), 'Returned title_track should match the original' assert org_audio_track == audio_track, 'Returned audio_track should match the original' - assert org_subtitle_track == subtitle_track, 'Returned subtitle_track should match the original' + assert org_subtitle_track == int(subtitle_track), 'Returned subtitle_track should match the original' assert org_start == start, 'Returned start should match the original' assert org_end == end, 'Returned end should match the original' assert org_name == name, 'Returned end should match the original' @@ -166,9 +166,9 @@ class TestMedia(TestCase, TestMixin): (device_path, title_track, audio_track, subtitle_track, start, end, name) = parse_optical_path(path) # THEN: The return values should match the original values - assert org_title_track == title_track, 'Returned title_track should match the original' + assert org_title_track == int(title_track), 'Returned title_track should match the original' assert org_audio_track == audio_track, 'Returned audio_track should match the original' - assert org_subtitle_track == subtitle_track, 'Returned subtitle_track should match the original' + assert org_subtitle_track == int(subtitle_track), 'Returned subtitle_track should match the original' assert org_start == start, 'Returned start should match the original' assert org_end == end, 'Returned end should match the original' assert org_name == name, 'Returned end should match the original' diff --git a/tests/functional/openlp_core/ui/media/test_vlcplayer.py b/tests/functional/openlp_core/ui/media/test_vlcplayer.py index a79e17f48..2e48e526a 100644 --- a/tests/functional/openlp_core/ui/media/test_vlcplayer.py +++ b/tests/functional/openlp_core/ui/media/test_vlcplayer.py @@ -28,7 +28,7 @@ from unittest import TestCase, skip from unittest.mock import MagicMock, call, patch from openlp.core.common.registry import Registry -from openlp.core.ui.media import MediaState, MediaType +from openlp.core.ui.media import ItemMediaInfo, MediaState, MediaType from openlp.core.ui.media.vlcplayer import VlcPlayer, get_vlc from tests.helpers import MockDateTime from tests.helpers.testmixin import TestMixin @@ -127,7 +127,7 @@ class TestVLCPlayer(TestCase, TestMixin): vlc_player = VlcPlayer(None) # WHEN: setup() is run - vlc_player.setup(mocked_output_display, mocked_controller) + vlc_player.setup(mocked_output_display, mocked_controller, True) # THEN: The VLC widget should be set up correctly assert mocked_output_display.vlc_widget == mocked_qframe @@ -177,7 +177,7 @@ class TestVLCPlayer(TestCase, TestMixin): vlc_player = VlcPlayer(None) # WHEN: setup() is run - vlc_player.setup(mocked_output_display, mocked_controller) + vlc_player.setup(mocked_output_display, mocked_controller, True) # THEN: The VLC instance should be created with the correct options mocked_vlc.Instance.assert_called_with('--no-video-title-show ') @@ -215,7 +215,7 @@ class TestVLCPlayer(TestCase, TestMixin): vlc_player = VlcPlayer(None) # WHEN: setup() is run - vlc_player.setup(mocked_output_display, mocked_controller) + vlc_player.setup(mocked_output_display, mocked_controller, True) # THEN: The VLC instance should be created with the correct options mocked_vlc.Instance.assert_called_with('--no-video-title-show ') @@ -252,7 +252,7 @@ class TestVLCPlayer(TestCase, TestMixin): vlc_player = VlcPlayer(None) # WHEN: setup() is run - vlc_player.setup(mocked_output_display, mocked_controller) + vlc_player.setup(mocked_output_display, mocked_controller, True) # THEN: set_hwnd should be called mocked_media_player_new.set_hwnd.assert_called_with(2) @@ -289,7 +289,7 @@ class TestVLCPlayer(TestCase, TestMixin): vlc_player = VlcPlayer(None) # WHEN: setup() is run - vlc_player.setup(mocked_output_display, mocked_controller) + vlc_player.setup(mocked_output_display, mocked_controller, True) # THEN: set_nsobject should be called mocked_media_player_new.set_nsobject.assert_called_with(2) @@ -336,27 +336,28 @@ class TestVLCPlayer(TestCase, TestMixin): mocked_vlc = MagicMock() mocked_get_vlc.return_value = mocked_vlc mocked_display = MagicMock() - mocked_display.media_info.volume = 100 - mocked_display.media_info.media_type = MediaType.Video - mocked_display.media_info.file_info.absoluteFilePath.return_value = media_path + mocked_controller = MagicMock() + mocked_controller.media_info.volume = 100 + mocked_controller.media_info.media_type = MediaType.Video + mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path mocked_vlc_media = MagicMock() mocked_media = MagicMock() mocked_media.get_duration.return_value = 10000 - mocked_display.vlc_instance.media_new_path.return_value = mocked_vlc_media - mocked_display.vlc_media_player.get_media.return_value = mocked_media + mocked_controller.vlc_instance.media_new_path.return_value = mocked_vlc_media + mocked_controller.vlc_media_player.get_media.return_value = mocked_media vlc_player = VlcPlayer(None) # WHEN: A video is loaded into VLC with patch.object(vlc_player, 'volume') as mocked_volume: - result = vlc_player.load(mocked_display, media_path) + result = vlc_player.load(mocked_controller, mocked_display, media_path) # THEN: The video should be loaded mocked_normcase.assert_called_with(media_path) - mocked_display.vlc_instance.media_new_path.assert_called_with(media_path) - assert mocked_vlc_media == mocked_display.vlc_media - mocked_display.vlc_media_player.set_media.assert_called_with(mocked_vlc_media) + mocked_controller.vlc_instance.media_new_path.assert_called_with(media_path) + assert mocked_vlc_media == mocked_controller.vlc_media + mocked_controller.vlc_media_player.set_media.assert_called_with(mocked_vlc_media) mocked_vlc_media.parse.assert_called_with() - mocked_volume.assert_called_with(mocked_display, 100) + mocked_volume.assert_called_with(mocked_controller, 100) assert result is True @patch('openlp.core.ui.media.vlcplayer.is_win') @@ -373,14 +374,15 @@ class TestVLCPlayer(TestCase, TestMixin): mocked_vlc = MagicMock() mocked_get_vlc.return_value = mocked_vlc mocked_display = MagicMock() - mocked_display.media_info.volume = 100 - mocked_display.media_info.media_type = MediaType.CD - mocked_display.media_info.title_track = 1 + mocked_controller = MagicMock() + mocked_controller.media_info.volume = 100 + mocked_controller.media_info.media_type = MediaType.CD + mocked_controller.media_info.title_track = 1 mocked_vlc_media = MagicMock() mocked_media = MagicMock() mocked_media.get_duration.return_value = 10000 - mocked_display.vlc_instance.media_new_location.return_value = mocked_vlc_media - mocked_display.vlc_media_player.get_media.return_value = mocked_media + mocked_controller.vlc_instance.media_new_location.return_value = mocked_vlc_media + mocked_controller.vlc_media_player.get_media.return_value = mocked_media mocked_subitems = MagicMock() mocked_subitems.count.return_value = 1 mocked_subitems.item_at_index.return_value = mocked_vlc_media @@ -390,15 +392,15 @@ class TestVLCPlayer(TestCase, TestMixin): # WHEN: An audio CD is loaded into VLC with patch.object(vlc_player, 'volume') as mocked_volume, \ patch.object(vlc_player, 'media_state_wait'): - result = vlc_player.load(mocked_display, media_path) + result = vlc_player.load(mocked_controller, mocked_display, media_path) # THEN: The video should be loaded mocked_normcase.assert_called_with(media_path) - mocked_display.vlc_instance.media_new_location.assert_called_with('cdda://' + media_path) - assert mocked_vlc_media == mocked_display.vlc_media - mocked_display.vlc_media_player.set_media.assert_called_with(mocked_vlc_media) + mocked_controller.vlc_instance.media_new_location.assert_called_with('cdda://' + media_path) + assert mocked_vlc_media == mocked_controller.vlc_media + mocked_controller.vlc_media_player.set_media.assert_called_with(mocked_vlc_media) mocked_vlc_media.parse.assert_called_with() - mocked_volume.assert_called_with(mocked_display, 100) + mocked_volume.assert_called_with(mocked_controller, 100) assert result is True @patch('openlp.core.ui.media.vlcplayer.is_win') @@ -415,15 +417,16 @@ class TestVLCPlayer(TestCase, TestMixin): mocked_vlc = MagicMock() mocked_get_vlc.return_value = mocked_vlc mocked_display = MagicMock() - mocked_display.media_info.volume = 100 - mocked_display.media_info.media_type = MediaType.CD - mocked_display.media_info.file_info.absoluteFilePath.return_value = media_path - mocked_display.media_info.title_track = 1 + mocked_controller = MagicMock() + mocked_controller.media_info.volume = 100 + mocked_controller.media_info.media_type = MediaType.CD + mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path + mocked_controller.media_info.title_track = 1 mocked_vlc_media = MagicMock() mocked_media = MagicMock() mocked_media.get_duration.return_value = 10000 - mocked_display.vlc_instance.media_new_location.return_value = mocked_vlc_media - mocked_display.vlc_media_player.get_media.return_value = mocked_media + mocked_controller.vlc_instance.media_new_location.return_value = mocked_vlc_media + mocked_controller.vlc_media_player.get_media.return_value = mocked_media mocked_subitems = MagicMock() mocked_subitems.count.return_value = 1 mocked_subitems.item_at_index.return_value = mocked_vlc_media @@ -433,15 +436,15 @@ class TestVLCPlayer(TestCase, TestMixin): # WHEN: An audio CD is loaded into VLC with patch.object(vlc_player, 'volume') as mocked_volume, \ patch.object(vlc_player, 'media_state_wait'): - result = vlc_player.load(mocked_display, media_path) + result = vlc_player.load(mocked_controller, mocked_display, media_path) # THEN: The video should be loaded mocked_normcase.assert_called_with(media_path) - mocked_display.vlc_instance.media_new_location.assert_called_with('cdda:///' + media_path) - assert mocked_vlc_media == mocked_display.vlc_media - mocked_display.vlc_media_player.set_media.assert_called_with(mocked_vlc_media) + mocked_controller.vlc_instance.media_new_location.assert_called_with('cdda:///' + media_path) + assert mocked_vlc_media == mocked_controller.vlc_media + mocked_controller.vlc_media_player.set_media.assert_called_with(mocked_vlc_media) mocked_vlc_media.parse.assert_called_with() - mocked_volume.assert_called_with(mocked_display, 100) + mocked_volume.assert_called_with(mocked_controller, 100) assert result is True @patch('openlp.core.ui.media.vlcplayer.is_win') @@ -458,15 +461,19 @@ class TestVLCPlayer(TestCase, TestMixin): mocked_vlc = MagicMock() mocked_get_vlc.return_value = mocked_vlc mocked_display = MagicMock() - mocked_display.media_info.volume = 100 - mocked_display.media_info.media_type = MediaType.CD - mocked_display.media_info.file_info.absoluteFilePath.return_value = media_path - mocked_display.media_info.title_track = 1 + mocked_controller = MagicMock() + mocked_controller.media_info.volume = 100 + mocked_controller.media_info.media_type = MediaType.CD + mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path + mocked_controller.media_info.title_track = 1 mocked_vlc_media = MagicMock() + mocked_vlc_media_player = MagicMock() + mocked_controller.vlc_media = mocked_vlc_media + mocked_controller.vlc_media_player = mocked_vlc_media_player mocked_media = MagicMock() mocked_media.get_duration.return_value = 10000 - mocked_display.vlc_instance.media_new_location.return_value = mocked_vlc_media - mocked_display.vlc_media_player.get_media.return_value = mocked_media + mocked_controller.vlc_instance.media_new_location.return_value = mocked_vlc_media + mocked_controller.vlc_media_player.get_media.return_value = mocked_media mocked_subitems = MagicMock() mocked_subitems.count.return_value = 0 mocked_subitems.item_at_index.return_value = mocked_vlc_media @@ -475,14 +482,14 @@ class TestVLCPlayer(TestCase, TestMixin): # WHEN: An audio CD is loaded into VLC with patch.object(vlc_player, 'volume'), patch.object(vlc_player, 'media_state_wait'): - result = vlc_player.load(mocked_display, media_path) + result = vlc_player.load(mocked_controller, mocked_display, media_path) # THEN: The video should be loaded mocked_normcase.assert_called_with(media_path) - mocked_display.vlc_instance.media_new_location.assert_called_with('cdda://' + media_path) - assert mocked_vlc_media == mocked_display.vlc_media + mocked_controller.vlc_instance.media_new_location.assert_called_with('cdda://' + media_path) + assert mocked_vlc_media == mocked_controller.vlc_media assert 0 == mocked_subitems.item_at_index.call_count - mocked_display.vlc_media_player.set_media.assert_called_with(mocked_vlc_media) + mocked_controller.vlc_media_player.set_media.assert_called_with(mocked_vlc_media) assert 0 == mocked_vlc_media.parse.call_count assert result is False @@ -562,17 +569,17 @@ class TestVLCPlayer(TestCase, TestMixin): Test resizing the player """ # GIVEN: A display object and a VlcPlayer instance - mocked_display = MagicMock() - mocked_display.size.return_value = (10, 10) - mocked_display.is_display = False + mocked_controller = MagicMock() + mocked_controller.preview_display.size.return_value = (10, 10) + mocked_controller.is_live = False vlc_player = VlcPlayer(None) # WHEN: resize is called - vlc_player.resize(mocked_display) + vlc_player.resize(mocked_controller) # THEN: The right methods should have been called - mocked_display.size.assert_called_with() - mocked_display.vlc_widget.resize.assert_called_with((10, 10)) + mocked_controller.preview_display.size.assert_called_with() + mocked_controller.vlc_widget.resize.assert_called_with((10, 10)) @patch('openlp.core.ui.media.vlcplayer.threading') @patch('openlp.core.ui.media.vlcplayer.get_vlc') @@ -586,27 +593,27 @@ class TestVLCPlayer(TestCase, TestMixin): mocked_vlc = MagicMock() mocked_get_vlc.return_value = mocked_vlc mocked_display = MagicMock() + mocked_controller = MagicMock() mocked_media = MagicMock() mocked_media.get_duration.return_value = 50000 - mocked_output_display = MagicMock() - mocked_output_display.media_info.start_time = 0 - mocked_output_display.media_info.media_type = MediaType.Video - mocked_output_display.media_info.volume = 100 - mocked_output_display.vlc_media_player.get_media.return_value = mocked_media + mocked_controller.media_info.start_time = 0 + mocked_controller.media_info.media_type = MediaType.Video + mocked_controller.media_info.volume = 100 + mocked_controller.vlc_media_player.get_media.return_value = mocked_media vlc_player = VlcPlayer(None) - vlc_player.set_state(MediaState.Paused, mocked_output_display) + vlc_player.set_state(MediaState.Paused, mocked_controller) # WHEN: play() is called 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 - result = vlc_player.play(mocked_display, mocked_output_display) + result = vlc_player.play(mocked_controller, mocked_display) # THEN: A bunch of things should happen to play the media mocked_thread.start.assert_called_with() - mocked_volume.assert_called_with(mocked_output_display, 100) + mocked_volume.assert_called_with(mocked_controller, 100) assert MediaState.Playing == vlc_player.get_live_state() - mocked_output_display.vlc_widget.raise_.assert_called_with() + mocked_controller.vlc_widget.raise_.assert_called_with() assert result is True, 'The value returned from play() should be True' @patch('openlp.core.ui.media.vlcplayer.threading') @@ -649,13 +656,14 @@ class TestVLCPlayer(TestCase, TestMixin): mocked_get_vlc.return_value = mocked_vlc mocked_controller = MagicMock() mocked_output_display = MagicMock() - mocked_output_display.media_info.start_time = 0 - mocked_output_display.media_info.end_time = 50 - mocked_output_display.media_info.media_type = MediaType.DVD - mocked_output_display.media_info.volume = 100 - mocked_output_display.media_info.title_track = 1 - mocked_output_display.media_info.audio_track = 1 - mocked_output_display.media_info.subtitle_track = 1 + mocked_controller.media_info = ItemMediaInfo() + mocked_controller.media_info.start_time = 0 + mocked_controller.media_info.end_time = 50 + mocked_controller.media_info.media_type = MediaType.DVD + mocked_controller.media_info.volume = 100 + mocked_controller.media_info.title_track = 1 + mocked_controller.media_info.audio_track = 1 + mocked_controller.media_info.subtitle_track = 1 vlc_player = VlcPlayer(None) vlc_player.set_state(MediaState.Paused, mocked_output_display) @@ -667,13 +675,13 @@ class TestVLCPlayer(TestCase, TestMixin): # THEN: A bunch of things should happen to play the media mocked_thread.start.assert_called_with() - mocked_output_display.vlc_media_player.set_title.assert_called_with(1) - mocked_output_display.vlc_media_player.play.assert_called_with() - mocked_output_display.vlc_media_player.audio_set_track.assert_called_with(1) - mocked_output_display.vlc_media_player.video_set_spu.assert_called_with(1) - mocked_volume.assert_called_with(mocked_output_display, 100) + mocked_controller.vlc_media_player.set_title.assert_called_with(1) + mocked_controller.vlc_media_player.play.assert_called_with() + mocked_controller.vlc_media_player.audio_set_track.assert_called_with(1) + mocked_controller.vlc_media_player.video_set_spu.assert_called_with(1) + mocked_volume.assert_called_with(mocked_controller, 100) assert MediaState.Playing == vlc_player.get_live_state() - mocked_output_display.vlc_widget.raise_.assert_called_with() + mocked_controller.vlc_widget.raise_.assert_called_with() assert result is True, 'The value returned from play() should be True' @patch('openlp.core.ui.media.vlcplayer.get_vlc') @@ -782,21 +790,6 @@ class TestVLCPlayer(TestCase, TestMixin): # THEN: The volume should have been set mocked_display.vlc_media_player.audio_set_volume.assert_called_with(10) - def test_volume_no_audio(self): - """ - Test setting the volume when there's no audio - """ - # GIVEN: A display object and a VlcPlayer instance - mocked_display = MagicMock() - mocked_display.has_audio = False - vlc_player = VlcPlayer(None) - - # WHEN: The volume is set - vlc_player.volume(mocked_display, 10) - - # THEN: The volume should NOT have been set - assert 0 == mocked_display.vlc_media_player.audio_set_volume.call_count - def test_seek_unseekable_media(self): """ Test seeking something that can't be seeked @@ -880,21 +873,6 @@ class TestVLCPlayer(TestCase, TestMixin): # THEN: The media should be stopped and invsibile mocked_display.vlc_widget.setVisible.assert_called_with(True) - def test_set_visible_no_widget(self): - """ - Test the set_visible() method when the player doesn't have a widget - """ - # GIVEN: Some mocked out stuff - mocked_display = MagicMock() - vlc_player = VlcPlayer(None) - vlc_player.has_own_widget = False - - # WHEN: reset() is called - vlc_player.set_visible(mocked_display, True) - - # THEN: The media should be stopped and invsibile - assert 0 == mocked_display.vlc_widget.setVisible.call_count - @patch('openlp.core.ui.media.vlcplayer.get_vlc') def test_update_ui(self, mocked_get_vlc): """ @@ -908,8 +886,8 @@ class TestVLCPlayer(TestCase, TestMixin): mocked_controller.media_info.end_time = 300 mocked_controller.seek_slider.isSliderDown.return_value = False mocked_display = MagicMock() - mocked_display.vlc_media.get_state.return_value = 1 - mocked_display.vlc_media_player.get_time.return_value = 400000 + mocked_controller.vlc_media.get_state.return_value = 1 + mocked_controller.vlc_media_player.get_time.return_value = 400000 vlc_player = VlcPlayer(None) # WHEN: update_ui() is called @@ -918,10 +896,10 @@ class TestVLCPlayer(TestCase, TestMixin): vlc_player.update_ui(mocked_controller, mocked_display) # THEN: Certain methods should be called - mocked_stop.assert_called_with(mocked_display) + mocked_stop.assert_called_with(mocked_controller) assert 2 == mocked_stop.call_count - mocked_display.vlc_media_player.get_time.assert_called_with() - mocked_set_visible.assert_called_with(mocked_display, False) + 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) expected_calls = [call(True), call(False)] assert expected_calls == mocked_controller.seek_slider.blockSignals.call_args_list @@ -936,13 +914,14 @@ class TestVLCPlayer(TestCase, TestMixin): mocked_vlc.State.Ended = 1 mocked_get_vlc.return_value = mocked_vlc mocked_controller = MagicMock() + mocked_controller.media_info = ItemMediaInfo() mocked_controller.media_info.start_time = 100 mocked_controller.media_info.end_time = 300 mocked_controller.seek_slider.isSliderDown.return_value = False mocked_display = MagicMock() - mocked_display.vlc_media.get_state.return_value = 1 - mocked_display.vlc_media_player.get_time.return_value = 300 - mocked_display.controller.media_info.media_type = MediaType.DVD + mocked_controller.vlc_media.get_state.return_value = 1 + mocked_controller.vlc_media_player.get_time.return_value = 300 + mocked_controller.media_info.media_type = MediaType.DVD vlc_player = VlcPlayer(None) # WHEN: update_ui() is called @@ -950,9 +929,9 @@ class TestVLCPlayer(TestCase, TestMixin): vlc_player.update_ui(mocked_controller, mocked_display) # THEN: Certain methods should be called - mocked_stop.assert_called_with(mocked_display) + mocked_stop.assert_called_with(mocked_controller) assert 1 == mocked_stop.call_count - mocked_display.vlc_media_player.get_time.assert_called_with() - mocked_controller.seek_slider.setSliderPosition.assert_called_with(300) + mocked_controller.vlc_media_player.get_time.assert_called_with() + mocked_controller.seek_slider.setSliderPosition.assert_called_with(200) expected_calls = [call(True), call(False)] assert expected_calls == mocked_controller.seek_slider.blockSignals.call_args_list diff --git a/tests/functional/openlp_plugins/presentations/test_impresscontroller.py b/tests/functional/openlp_plugins/presentations/test_impresscontroller.py index 69fa9eddc..40ca51afe 100644 --- a/tests/functional/openlp_plugins/presentations/test_impresscontroller.py +++ b/tests/functional/openlp_plugins/presentations/test_impresscontroller.py @@ -24,7 +24,7 @@ Functional tests to test the Impress class and related methods. import shutil from tempfile import mkdtemp from unittest import TestCase -from unittest.mock import MagicMock, patch +from unittest.mock import MagicMock, call, patch from openlp.core.common.settings import Settings from openlp.plugins.presentations.lib.impresscontroller import ImpressController, ImpressDocument, TextType @@ -86,7 +86,7 @@ class TestImpressController(TestCase, TestMixin): assert result is False @patch('openlp.plugins.presentations.lib.impresscontroller.log') - def test_check_available1(self, mocked_log): + def test_check_available_on_windows(self, mocked_log): """ Test `ImpressController.check_available` on Windows """ @@ -106,7 +106,7 @@ class TestImpressController(TestCase, TestMixin): @patch('openlp.plugins.presentations.lib.impresscontroller.log') @patch('openlp.plugins.presentations.lib.impresscontroller.is_win', return_value=False) - def test_check_available2(self, mocked_is_win, mocked_log): + def test_check_available_on_linux(self, mocked_is_win, mocked_log): """ Test `ImpressController.check_available` when not on Windows """ @@ -122,6 +122,44 @@ class TestImpressController(TestCase, TestMixin): assert mocked_get_com_servicemanager.called is False assert result is True + @patch('openlp.plugins.presentations.lib.impresscontroller.is_win', return_value=True) + def test_start_process_on_windows(self, mocked_is_win): + """ + Test that start_process() on Windows starts the process + """ + # GIVEN: An ImpressController object + controller = ImpressController(plugin=self.mock_plugin) + controller.get_com_servicemanager = MagicMock(return_value=MagicMock()) + + # WHEN: start_process() is called + controller.start_process() + + # THEN: The correct methods should have been called + controller.get_com_servicemanager.assert_called_once() + assert controller.manager._FlagAsMethod.call_args_list == [call('Bridge_GetStruct'), + call('Bridge_GetValueObject')] + + @patch('openlp.plugins.presentations.lib.impresscontroller.is_win', return_value=False) + @patch('openlp.plugins.presentations.lib.impresscontroller.get_uno_command', return_value='libreoffice') + @patch('openlp.plugins.presentations.lib.impresscontroller.QtCore.QProcess') + def test_start_process_on_linux(self, MockQProcess, mocked_get_uno_command, mocked_is_win): + """ + Test that start_process() on Linux starts the process + """ + # GIVEN: An ImpressController object + mocked_process = MagicMock() + MockQProcess.return_value = mocked_process + controller = ImpressController(plugin=self.mock_plugin) + + # WHEN: start_process() is called + controller.start_process() + + # THEN: The correct methods should have been called + mocked_get_uno_command.assert_called_once() + MockQProcess.assert_called_once() + assert controller.process is mocked_process + mocked_process.startDetached.assert_called_once_with('libreoffice') + class TestImpressDocument(TestCase): """