diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py index b33bf5901..b15353b44 100644 --- a/openlp/core/ui/media/mediacontroller.py +++ b/openlp/core/ui/media/mediacontroller.py @@ -260,8 +260,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): # if this is an optical device use special handling if service_item.is_capable(ItemCapabilities.IsOptical): self.log_debug('video is optical and live') - path = service_item.get_frame_path() - (name, title, audio_track, subtitle_track, start, end, clip_name) = parse_optical_path(path) + path_string = path_to_str(service_item.get_frame_path()) + (name, title, audio_track, subtitle_track, start, end, clip_name) = parse_optical_path(path_string) is_valid = self.media_setup_optical(name, title, audio_track, subtitle_track, start, end, display, controller) elif service_item.is_capable(ItemCapabilities.CanStream): @@ -281,8 +281,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): elif controller.preview_display: if service_item.is_capable(ItemCapabilities.IsOptical): self.log_debug('video is optical and preview') - path = service_item.get_frame_path() - (name, title, audio_track, subtitle_track, start, end, clip_name) = parse_optical_path(path) + path_string = path_to_str(service_item.get_frame_path()) + (name, title, audio_track, subtitle_track, start, end, clip_name) = parse_optical_path(path_string) is_valid = self.media_setup_optical(name, title, audio_track, subtitle_track, start, end, display, controller) elif service_item.is_capable(ItemCapabilities.CanStream): @@ -482,7 +482,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): start_again = False stopped = False if controller.media_info.is_playing and controller.media_info.length > 0: - if controller.media_info.timer + TICK_TIME >= controller.media_info.length: + if controller.media_info.timer - controller.media_info.start_time + TICK_TIME >= \ + controller.media_info.length: if controller.media_info.is_looping_playback: start_again = True else: @@ -494,12 +495,12 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): stopped = True if start_again: - controller.media_info.timer = 0 + controller.media_info.timer = controller.media_info.start_time self._update_seek_ui(controller) return not stopped def _update_seek_ui(self, controller): - seconds = controller.media_info.timer // 1000 + seconds = (controller.media_info.timer - controller.media_info.start_time) // 1000 minutes = seconds // 60 seconds %= 60 total_seconds = controller.media_info.length // 1000 @@ -592,7 +593,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): 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(controller.media_info.start_time) total_seconds = controller.media_info.length // 1000 total_minutes = total_seconds // 60 total_seconds %= 60 @@ -601,7 +602,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): controller.mediabar.actions['playbackStop'].setDisabled(True) controller.mediabar.actions['playbackPause'].setVisible(False) controller.media_info.is_playing = False - controller.media_info.timer = 0 + controller.media_info.timer = controller.media_info.start_time controller.output_has_changed() return True return False diff --git a/openlp/core/ui/media/mediaplayer.py b/openlp/core/ui/media/mediaplayer.py index bceac4dbd..f6e947ad7 100644 --- a/openlp/core/ui/media/mediaplayer.py +++ b/openlp/core/ui/media/mediaplayer.py @@ -48,12 +48,12 @@ class MediaPlayer(RegistryProperties): """ return False - def setup(self, controller, display, live_display): + def setup(self, controller, display): """ Create the related widgets for the current display + :param controller: Which Controller is running the show. :param display: The display to be updated. - :param live_display: Is the display a live one. """ pass @@ -66,11 +66,11 @@ class MediaPlayer(RegistryProperties): """ return True - def resize(self, display): + def resize(self, controller): """ If the main display size or position is changed, the media widgets should also resized - :param display: The display to be updated. + :param controller: Which Controller is running the show. """ pass @@ -83,53 +83,53 @@ class MediaPlayer(RegistryProperties): """ pass - def pause(self, display): + def pause(self, controller): """ Pause of current Media File - :param display: The display to be updated. + :param controller: Which Controller is running the show. """ pass - def stop(self, display): + def stop(self, controller): """ Stop playing of current Media File - :param display: The display to be updated. + :param controller: Which Controller is running the show. """ pass - def volume(self, display, volume): + def volume(self, controller, volume): """ Change volume of current Media File - :param display: The display to be updated. + :param controller: Which Controller is running the show. :param volume: The volume to set. """ pass - def seek(self, display, seek_value): + def seek(self, controller, seek_value): """ Change playing position of current Media File - :param display: The display to be updated. + :param controller: Which Controller is running the show. :param seek_value: The where to seek to. """ pass - def reset(self, display): + def reset(self, controller): """ Remove the current loaded video - :param display: The display to be updated. + :param controller: Which Controller is running the show. """ pass - def set_visible(self, display, status): + def set_visible(self, controller, status): """ Show/Hide the media widgets - :param display: The display to be updated. + :param controller: Which Controller is running the show. :param status: The status to be set. """ pass diff --git a/openlp/core/ui/media/vlcplayer.py b/openlp/core/ui/media/vlcplayer.py index 2fbb6ace5..32339ca00 100644 --- a/openlp/core/ui/media/vlcplayer.py +++ b/openlp/core/ui/media/vlcplayer.py @@ -101,7 +101,8 @@ class VlcPlayer(MediaPlayer): """ Set up the media player - :param controller: The display where the media is. + :param controller: The controller where the media is + :param display: The display where the media is. :return: """ vlc = get_vlc() @@ -182,12 +183,15 @@ class VlcPlayer(MediaPlayer): 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) + self.media_state_wait(controller, vlc.State.Playing) # If subitems exists, this is a CD audio_cd_tracks = controller.vlc_media.subitems() if not audio_cd_tracks or audio_cd_tracks.count() < 1: return False - controller.vlc_media_player = audio_cd_tracks.item_at_index(controller.media_info.title_track) + controller.vlc_media = audio_cd_tracks.item_at_index(int(controller.media_info.title_track)) + # VLC's start and stop time options work on seconds + controller.vlc_media.add_option(f"start-time={int(controller.media_info.start_time // 1000)}") + controller.vlc_media.add_option(f"stop-time={int(controller.media_info.end_time // 1000)}") elif controller.media_info.media_type == MediaType.Stream: controller.vlc_media = controller.vlc_instance.media_new_location(file[0]) controller.vlc_media.add_options(file[1]) @@ -241,7 +245,7 @@ class VlcPlayer(MediaPlayer): :return: """ vlc = get_vlc() - start_time = 0 + start_time = controller.media_info.start_time log.debug('vlc play') if controller.is_live: if self.get_live_state() != MediaState.Paused and controller.media_info.timer > 0: @@ -284,7 +288,8 @@ class VlcPlayer(MediaPlayer): 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) + controller.seek_slider.setMaximum(controller.media_info.start_time + controller.media_info.length) + controller.seek_slider.setMinimum(controller.media_info.start_time) self.set_state(MediaState.Playing, controller) return True @@ -362,8 +367,7 @@ class VlcPlayer(MediaPlayer): """ 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: + if controller.media_info.media_type == MediaType.DVD: controller.seek_slider.setSliderPosition( controller.vlc_media_player.get_time() - int(controller.media_info.start_time)) else: diff --git a/tests/openlp_core/ui/media/test_vlcplayer.py b/tests/openlp_core/ui/media/test_vlcplayer.py index 958be6ac8..c01029a11 100644 --- a/tests/openlp_core/ui/media/test_vlcplayer.py +++ b/tests/openlp_core/ui/media/test_vlcplayer.py @@ -26,7 +26,7 @@ import sys import pytest from datetime import timedelta from unittest import skipIf -from unittest.mock import MagicMock, call, patch +from unittest.mock import MagicMock, call, patch, ANY from openlp.core.common import is_macosx from openlp.core.common.registry import Registry @@ -367,7 +367,7 @@ def test_load_audio_cd(mocked_normcase, mocked_get_vlc, mocked_is_win): # WHEN: An audio CD is loaded into VLC with patch.object(vlc_player, 'volume') as mocked_volume, \ - patch.object(vlc_player, 'media_state_wait'): + patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait: result = vlc_player.load(mocked_controller, mocked_display, media_path) # THEN: The video should be loaded @@ -377,6 +377,7 @@ def test_load_audio_cd(mocked_normcase, mocked_get_vlc, mocked_is_win): 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_controller, 100) + mocked_media_state_wait.assert_called_with(mocked_controller, ANY) assert result is True @@ -408,11 +409,12 @@ def test_load_audio_cd_on_windows(mocked_normcase, mocked_get_vlc, mocked_is_win mocked_subitems.count.return_value = 1 mocked_subitems.item_at_index.return_value = mocked_vlc_media mocked_vlc_media.subitems.return_value = mocked_subitems + vlc_player = VlcPlayer(None) # WHEN: An audio CD is loaded into VLC with patch.object(vlc_player, 'volume') as mocked_volume, \ - patch.object(vlc_player, 'media_state_wait'): + patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait: result = vlc_player.load(mocked_controller, mocked_display, media_path) # THEN: The video should be loaded @@ -422,6 +424,7 @@ def test_load_audio_cd_on_windows(mocked_normcase, mocked_get_vlc, mocked_is_win 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_controller, 100) + mocked_media_state_wait.assert_called_with(mocked_controller, ANY) assert result is True @@ -482,15 +485,15 @@ def test_media_state_wait(mocked_get_vlc): mocked_vlc = MagicMock() mocked_vlc.State.Error = 1 mocked_get_vlc.return_value = mocked_vlc - mocked_display = MagicMock() - mocked_display.vlc_media.get_state.return_value = 2 + mocked_controller = MagicMock() + mocked_controller.vlc_media.get_state.return_value = 2 Registry.create() mocked_application = MagicMock() Registry().register('application', mocked_application) vlc_player = VlcPlayer(None) # WHEN: media_state_wait() is called - result = vlc_player.media_state_wait(mocked_display, 2) + result = vlc_player.media_state_wait(mocked_controller, 2) # THEN: The results should be True assert result is True @@ -506,15 +509,15 @@ def test_media_state_wait_error(mocked_get_vlc, vlc_env): mocked_vlc = MagicMock() mocked_vlc.State.Error = 1 mocked_get_vlc.return_value = mocked_vlc - mocked_display = MagicMock() - mocked_display.vlc_media.get_state.return_value = 1 + mocked_controller = MagicMock() + mocked_controller.vlc_media.get_state.return_value = 1 Registry.create() mocked_application = MagicMock() Registry().register('application', mocked_application) vlc_player = VlcPlayer(None) # WHEN: media_state_wait() is called - result = vlc_player.media_state_wait(mocked_display, 2) + result = vlc_player.media_state_wait(mocked_controller, 2) # THEN: The results should be True assert result is False @@ -532,15 +535,15 @@ def test_media_state_wait_times_out(mocked_get_vlc, vlc_env): mocked_vlc = MagicMock() mocked_vlc.State.Error = 1 mocked_get_vlc.return_value = mocked_vlc - mocked_display = MagicMock() - mocked_display.vlc_media.get_state.return_value = 2 + mocked_controller = MagicMock() + mocked_controller.vlc_media.get_state.return_value = 2 Registry.create() mocked_application = MagicMock() Registry().register('application', mocked_application) vlc_player = VlcPlayer(None) # WHEN: media_state_wait() is called - result = vlc_player.media_state_wait(mocked_display, 3) + result = vlc_player.media_state_wait(mocked_controller, 3) # THEN: The results should be True assert result is False @@ -582,6 +585,8 @@ def test_play(mocked_get_vlc, mocked_threading): mocked_controller.media_info.start_time = 0 mocked_controller.media_info.media_type = MediaType.Video mocked_controller.media_info.volume = 100 + mocked_controller.media_info.start_time = 20000 + mocked_controller.media_info.length = 10000 mocked_controller.vlc_media_player.get_media.return_value = mocked_media vlc_player = VlcPlayer(None) vlc_player.set_state(MediaState.Paused, mocked_controller) @@ -595,6 +600,9 @@ def test_play(mocked_get_vlc, mocked_threading): # THEN: A bunch of things should happen to play the media mocked_thread.start.assert_called_with() mocked_volume.assert_called_with(mocked_controller, 100) + mocked_controller.seek_slider.setMaximum.assert_called_with(30000) + mocked_controller.seek_slider.setMinimum.assert_called_with(20000) + assert MediaState.Playing == vlc_player.get_live_state() assert result is True, 'The value returned from play() should be True'