mirror of https://gitlab.com/openlp/openlp.git
Merge branch 'vlc_5' into 'master'
VLC - Cleanup See merge request openlp/openlp!613
This commit is contained in:
commit
06da7904ba
|
@ -86,12 +86,12 @@ class ItemMediaInfo(object):
|
|||
"""
|
||||
file_info = None
|
||||
is_background = False
|
||||
is_theme_background = None
|
||||
length = 0
|
||||
start_time = 0
|
||||
end_time = 0
|
||||
title_track = 0
|
||||
is_playing = False
|
||||
old_timer = -1
|
||||
timer = 1000
|
||||
audio_track = 0
|
||||
subtitle_track = 0
|
||||
|
|
|
@ -32,7 +32,7 @@ except ImportError:
|
|||
pymediainfo_available = False
|
||||
pymediainfo_version = '0.0'
|
||||
|
||||
from PyQt5 import QtCore
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.mixins import LogMixin, RegistryProperties
|
||||
|
@ -49,18 +49,24 @@ from openlp.core.ui.media import MediaState, ItemMediaInfo, MediaType, parse_opt
|
|||
get_volume, toggle_looping_playback, is_looping_playback, save_volume
|
||||
from openlp.core.ui.media.remote import register_views
|
||||
from openlp.core.ui.media.vlcplayer import VlcPlayer, get_vlc
|
||||
from openlp.core.ui.media.vlcplayerpl import VlcPlayerPL
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
TICK_TIME = 200
|
||||
HIDE_DELAY_TIME = 2500
|
||||
|
||||
|
||||
class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
||||
class MediaController(QtWidgets.QWidget, RegistryBase, LogMixin, RegistryProperties):
|
||||
"""
|
||||
The implementation of the Media Controller which manages how media is played.
|
||||
"""
|
||||
|
||||
vlc_live_media_tick = QtCore.pyqtSignal()
|
||||
vlc_preview_media_tick = QtCore.pyqtSignal()
|
||||
vlc_live_media_stop = QtCore.pyqtSignal()
|
||||
vlc_preview_media_stop = QtCore.pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
"""
|
||||
"""
|
||||
|
@ -68,23 +74,17 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
self.log_info('MediaController Initialising')
|
||||
|
||||
def setup(self):
|
||||
self.is_theme_background = False
|
||||
self.vlc_player = None
|
||||
self.vlc_playerpl = None
|
||||
self.current_media_players = {}
|
||||
# Timer for video state
|
||||
self.live_timer = QtCore.QTimer()
|
||||
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.setInterval(TICK_TIME)
|
||||
# Signals
|
||||
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)
|
||||
Registry().register_function('playbackPlay', self.media_play_msg)
|
||||
Registry().register_function('playbackPause', self.media_pause_msg)
|
||||
Registry().register_function('playbackStop', self.media_stop_msg)
|
||||
|
@ -106,6 +106,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
"""
|
||||
self.setup()
|
||||
self.vlc_player = VlcPlayer(self)
|
||||
self.vlc_playerpl = VlcPlayerPL(self)
|
||||
State().add_service('mediacontroller', 0)
|
||||
State().add_service('media_live', 0)
|
||||
has_vlc = get_vlc()
|
||||
|
@ -145,6 +146,10 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
"""
|
||||
if State().check_preconditions('mediacontroller'):
|
||||
try:
|
||||
self.vlc_live_media_tick.connect(self._media_state_live)
|
||||
self.vlc_preview_media_tick.connect(self._media_state_preview)
|
||||
self.vlc_live_media_stop.connect(self.live_media_stopped)
|
||||
self.vlc_preview_media_stop.connect(self.preview_media_stopped)
|
||||
self.setup_display(self.live_controller, False)
|
||||
except AttributeError:
|
||||
State().update_pre_conditions('media_live', False)
|
||||
|
@ -152,7 +157,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
'OpenLP.MediaController', 'No Displays have been configured, so Live Media has been disabled'))
|
||||
self.setup_display(self.preview_controller, True)
|
||||
|
||||
def _display_controllers(self, controller_type: DisplayControllerType) -> Type[SlideController]:
|
||||
def _display_controllers(self, controller_type: DisplayControllerType) -> SlideController:
|
||||
"""
|
||||
Decides which controller to use.
|
||||
|
||||
|
@ -165,16 +170,14 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
|
||||
def _media_state_live(self) -> None:
|
||||
"""
|
||||
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 some updating stuff (e.g. update the UI)
|
||||
"""
|
||||
if DisplayControllerType.Live in self.current_media_players:
|
||||
media_player = self.current_media_players[DisplayControllerType.Live]
|
||||
media_player.resize(self.live_controller)
|
||||
media_player.update_ui(self.live_controller, self._define_display(self.live_controller))
|
||||
if not self.tick(self.live_controller):
|
||||
self.live_timer.stop()
|
||||
self.tick(self.live_controller)
|
||||
else:
|
||||
self.live_timer.stop()
|
||||
self.media_stop(self.live_controller)
|
||||
|
||||
def _media_state_preview(self) -> None:
|
||||
|
@ -185,13 +188,26 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
media_player = self.current_media_players[DisplayControllerType.Preview]
|
||||
media_player.resize(self.preview_controller)
|
||||
media_player.update_ui(self.preview_controller, self._define_display(self.preview_controller))
|
||||
if not self.tick(self.preview_controller):
|
||||
self.preview_timer.stop()
|
||||
self.tick(self.preview_controller)
|
||||
else:
|
||||
self.preview_timer.stop()
|
||||
self.media_stop(self.preview_controller)
|
||||
|
||||
def setup_display(self, controller: Type[SlideController], preview: bool) -> None:
|
||||
def live_media_stopped(self) -> None:
|
||||
self.media_stop(self.live_controller)
|
||||
self.tick(self.live_controller)
|
||||
if Registry().get('settings').value('media/live loop') or self.live_controller.media_info.is_theme_background:
|
||||
self.has_started = False
|
||||
self.media_play(self.live_controller)
|
||||
|
||||
def preview_media_stopped(self) -> None:
|
||||
self.media_stop(self.preview_controller)
|
||||
self.tick(self.preview_controller)
|
||||
if Registry().get('settings').value('media/preview loop') or \
|
||||
self.preview_controller.media_info.is_theme_background:
|
||||
self.has_started = False
|
||||
self.media_play(self.preview_controller)
|
||||
|
||||
def setup_display(self, controller: SlideController, preview: bool) -> None:
|
||||
"""
|
||||
After a new display is configured, all media related widgets will be created too
|
||||
|
||||
|
@ -203,9 +219,10 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
if preview:
|
||||
controller.has_audio = False
|
||||
self.vlc_player.setup(controller, self._define_display(controller))
|
||||
self.vlc_playerpl.setup(controller, self._define_display(controller))
|
||||
|
||||
@staticmethod
|
||||
def set_controls_visible(controller: Type[SlideController], value: int) -> None:
|
||||
def set_controls_visible(controller: SlideController, value: int) -> None:
|
||||
"""
|
||||
After a new display is configured, all media related widget will be created too
|
||||
|
||||
|
@ -216,7 +233,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
controller.mediabar.setVisible(value)
|
||||
|
||||
@staticmethod
|
||||
def resize(controller: Type[SlideController], player) -> None:
|
||||
def _resize(controller: SlideController, player) -> None:
|
||||
"""
|
||||
After Mainwindow changes or Splitter moved all related media widgets have to be resized
|
||||
|
||||
|
@ -225,22 +242,23 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
"""
|
||||
player.resize(controller)
|
||||
|
||||
def load_video(self, source, service_item, hidden: bool = False, is_theme_background: bool = False) -> None:
|
||||
def load_video(self, source, service_item, hidden: bool = False, is_theme_background: bool = False) -> bool:
|
||||
"""
|
||||
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 is_theme_background: Is the theme providing a background
|
||||
"""
|
||||
self.is_theme_background = is_theme_background
|
||||
is_valid = True
|
||||
controller = self._display_controllers(source)
|
||||
controller.media_info.is_theme_background = is_theme_background
|
||||
log.debug(f'load_video is_live:{controller.is_live}')
|
||||
# stop running videos
|
||||
self.media_reset(controller)
|
||||
controller.media_info = ItemMediaInfo()
|
||||
controller.media_info.is_theme_background = is_theme_background
|
||||
controller.media_info.media_type = MediaType.Video
|
||||
# background will always loop video.
|
||||
if service_item.is_capable(ItemCapabilities.HasBackgroundAudio):
|
||||
|
@ -282,7 +300,6 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
controller.media_info.timer = service_item.start_time
|
||||
controller.media_info.end_time = service_item.end_time
|
||||
is_valid = self._check_file_type(controller, display)
|
||||
|
||||
if not is_valid:
|
||||
# Media could not be loaded correctly
|
||||
critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'),
|
||||
|
@ -307,8 +324,9 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
'Unable to preview when live is currently streaming'))
|
||||
|
||||
return
|
||||
self._media_bar(controller, 'load')
|
||||
if self.decide_autoplay(service_item, controller, hidden):
|
||||
start_hidden = self.is_theme_background and controller.is_live and \
|
||||
start_hidden = controller.media_info.is_theme_background and controller.is_live and \
|
||||
(controller.current_hide_mode == HideMode.Blank or controller.current_hide_mode == HideMode.Screen)
|
||||
if not self.media_play(controller, start_hidden):
|
||||
critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'),
|
||||
|
@ -341,7 +359,9 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
# Unblank on load set
|
||||
elif self.settings.value('core/auto unblank'):
|
||||
is_autoplay = True
|
||||
if self.is_theme_background:
|
||||
if controller.media_info.is_theme_background:
|
||||
is_autoplay = True
|
||||
if controller.media_info.media_type == MediaType.Stream:
|
||||
is_autoplay = True
|
||||
return is_autoplay
|
||||
|
||||
|
@ -367,7 +387,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
return 0
|
||||
|
||||
def media_setup_optical(self, filename, title, audio_track, subtitle_track, start, end,
|
||||
display: Type[DisplayWindow], controller: Type[SlideController]):
|
||||
display: Type[DisplayWindow], controller: SlideController):
|
||||
"""
|
||||
Setup playback of optical media
|
||||
|
||||
|
@ -401,11 +421,11 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
if display is None:
|
||||
display = controller.preview_display
|
||||
self.vlc_player.load(controller, display, filename)
|
||||
self.resize(controller, self.vlc_player)
|
||||
self._resize(controller, self.vlc_player)
|
||||
self.current_media_players[controller.controller_type] = self.vlc_player
|
||||
return True
|
||||
|
||||
def _check_file_type(self, controller: Type[SlideController], display: Type[DisplayWindow]):
|
||||
def _check_file_type(self, controller: SlideController, display: DisplayWindow):
|
||||
"""
|
||||
Select the correct media Player type from the prioritized Player list
|
||||
|
||||
|
@ -413,18 +433,18 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
:param display: Which display to use
|
||||
"""
|
||||
if controller.media_info.media_type == MediaType.Stream:
|
||||
self.resize(controller, self.vlc_player)
|
||||
self._resize(controller, self.vlc_player)
|
||||
if self.vlc_player.load(controller, display, controller.media_info.file_info):
|
||||
self.current_media_players[controller.controller_type] = self.vlc_player
|
||||
return True
|
||||
return False
|
||||
for file in controller.media_info.file_info:
|
||||
if not file.is_file and not self.vlc_player.can_folder:
|
||||
if not file.is_file and not self.vlc_playerpl.can_folder:
|
||||
return False
|
||||
file = str(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
|
||||
self._resize(controller, self.vlc_playerpl)
|
||||
if self.vlc_playerpl.load(controller, display, file):
|
||||
self.current_media_players[controller.controller_type] = self.vlc_playerpl
|
||||
return True
|
||||
return False
|
||||
|
||||
|
@ -443,7 +463,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
"""
|
||||
return self.media_play(self.live_controller)
|
||||
|
||||
def media_play(self, controller: Type[SlideController], start_hidden=False):
|
||||
def media_play(self, controller: SlideController, start_hidden=False):
|
||||
"""
|
||||
Responds to the request to play a loaded video
|
||||
|
||||
|
@ -463,12 +483,6 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
self._media_set_visibility(controller, True)
|
||||
self._media_bar(controller, "play")
|
||||
# Start Timer for ui updates
|
||||
if controller.is_live:
|
||||
if not self.live_timer.isActive():
|
||||
self.live_timer.start()
|
||||
else:
|
||||
if not self.preview_timer.isActive():
|
||||
self.preview_timer.start()
|
||||
controller.mediabar.seek_slider.blockSignals(False)
|
||||
controller.mediabar.volume_slider.blockSignals(False)
|
||||
controller.media_info.is_playing = True
|
||||
|
@ -482,43 +496,38 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
controller.output_has_changed()
|
||||
return True
|
||||
|
||||
def tick(self, controller):
|
||||
def tick(self, controller) -> None:
|
||||
"""
|
||||
Add a tick while the media is playing but only count if not paused
|
||||
|
||||
:param controller: The Controller to be processed
|
||||
:return: Is the video still running?
|
||||
"""
|
||||
start_again = False
|
||||
stopped = False
|
||||
if controller.media_info.is_playing and controller.media_info.length > 0:
|
||||
controller.media_info.timer = controller.vlc_media_player.get_time()
|
||||
if controller.media_info.old_timer > controller.media_info.timer:
|
||||
if is_looping_playback(controller) or self.is_theme_background:
|
||||
start_again = True
|
||||
else:
|
||||
self.media_stop(controller)
|
||||
stopped = True
|
||||
controller.media_info.old_timer = controller.media_info.timer
|
||||
self._update_seek_ui(controller)
|
||||
else:
|
||||
stopped = True
|
||||
controller.media_info.timer = controller.vlc_media_player.get_time()
|
||||
self._update_seek_ui(controller)
|
||||
return
|
||||
|
||||
if start_again:
|
||||
controller.media_info.timer = controller.media_info.start_time
|
||||
self._update_seek_ui(controller)
|
||||
return not stopped
|
||||
|
||||
def _media_bar(self, controller: Type[SlideController], mode: str) -> None:
|
||||
def _media_bar(self, controller: SlideController, mode: str) -> None:
|
||||
"""
|
||||
Set the media bar state depending on the function called.
|
||||
:param controller: The controller being updated
|
||||
:param mode: The mode the code is being called from
|
||||
:return: None
|
||||
"""
|
||||
loop_set = self.is_theme_background or is_looping_playback(controller)
|
||||
loop_disabled = controller.media_info.media_type is MediaType.Stream or self.is_theme_background
|
||||
controller.mediabar.blockSignals(True)
|
||||
if controller.controller_type in self.current_media_players and \
|
||||
self.current_media_players[controller.controller_type].can_repeat:
|
||||
if controller.media_info.is_theme_background:
|
||||
loop_set = False
|
||||
loop_disabled = True
|
||||
self.current_media_players[controller.controller_type].toggle_loop(controller, True)
|
||||
else:
|
||||
loop_set = is_looping_playback(controller)
|
||||
loop_disabled = False
|
||||
self.current_media_players[controller.controller_type].toggle_loop(controller, loop_set)
|
||||
else:
|
||||
loop_set = False
|
||||
loop_disabled = True
|
||||
if mode == "load":
|
||||
controller.mediabar.actions['playbackPlay'].setDisabled(False)
|
||||
controller.mediabar.actions['playbackPause'].setDisabled(True)
|
||||
|
@ -544,7 +553,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
def _update_seek_ui(controller):
|
||||
if controller.media_info.timer > controller.media_info.end_time:
|
||||
controller.media_info.timer = controller.media_info.end_time
|
||||
|
||||
if controller.media_info.timer < 0:
|
||||
controller.media_info.timer = 0
|
||||
seconds = controller.media_info.timer // 1000
|
||||
minutes = seconds // 60
|
||||
seconds %= 60
|
||||
|
@ -571,7 +581,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
"""
|
||||
return self.media_pause(self.live_controller)
|
||||
|
||||
def media_pause(self, controller: Type[SlideController]):
|
||||
def media_pause(self, controller: SlideController):
|
||||
"""
|
||||
Responds to the request to pause a loaded video
|
||||
|
||||
|
@ -597,14 +607,16 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
"""
|
||||
self.media_loop(msg[0])
|
||||
|
||||
@staticmethod
|
||||
def media_loop(controller: Type[SlideController]):
|
||||
def media_loop(self, controller: Type[SlideController]):
|
||||
"""
|
||||
Responds to the request to loop a loaded video
|
||||
|
||||
:param controller: The controller that needs to be stopped
|
||||
"""
|
||||
toggle_looping_playback(controller)
|
||||
if controller.controller_type in self.current_media_players:
|
||||
self.current_media_players[controller.controller_type].toggle_loop(controller,
|
||||
is_looping_playback(controller))
|
||||
controller.mediabar.actions['playbackLoop'].setChecked(is_looping_playback(controller))
|
||||
|
||||
def media_stop_msg(self, msg: list):
|
||||
|
@ -621,7 +633,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
"""
|
||||
return self.media_stop(self.live_controller)
|
||||
|
||||
def media_stop(self, controller: Type[SlideController]):
|
||||
def media_stop(self, controller: SlideController):
|
||||
"""
|
||||
Responds to the request to stop a loaded video
|
||||
|
||||
|
@ -682,7 +694,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
seek_value = msg[1][0]
|
||||
self.media_seek(controller, seek_value)
|
||||
|
||||
def media_seek(self, controller: Type[SlideController], seek_value):
|
||||
def media_seek(self, controller: SlideController, seek_value):
|
||||
"""
|
||||
Responds to the request to change the seek Slider of a loaded video
|
||||
|
||||
|
@ -695,7 +707,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
controller.media_info.timer = seek_value
|
||||
self._update_seek_ui(controller)
|
||||
|
||||
def media_reset(self, controller: Type[SlideController], delayed: bool = False) -> None:
|
||||
def media_reset(self, controller: SlideController, delayed: bool = False) -> None:
|
||||
"""
|
||||
Responds to the request to reset a loaded video
|
||||
:param controller: The controller to use.
|
||||
|
@ -713,6 +725,9 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
else:
|
||||
self._media_set_visibility(controller, False)
|
||||
del self.current_media_players[controller.controller_type]
|
||||
controller.media_info = ItemMediaInfo()
|
||||
controller.media_info.is_theme_background = False
|
||||
self._media_bar(controller, 'reset')
|
||||
|
||||
def media_hide_msg(self, msg: list):
|
||||
"""
|
||||
|
@ -749,7 +764,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
self._media_set_visibility(self.live_controller, False)
|
||||
del self.current_media_players[self.live_controller.controller_type]
|
||||
|
||||
def _media_set_visibility(self, controller: Type[SlideController], visible):
|
||||
def _media_set_visibility(self, controller: SlideController, visible):
|
||||
"""
|
||||
Set the live video Widget visibility
|
||||
"""
|
||||
|
@ -782,13 +797,13 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
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 self.is_theme_background and hide_mode == HideMode.Theme:
|
||||
if self.live_controller.media_info.is_theme_background and hide_mode == HideMode.Theme:
|
||||
if not playing:
|
||||
self.media_play(self.live_controller)
|
||||
else:
|
||||
self.live_hide_timer.stop()
|
||||
else:
|
||||
if playing and not self.is_theme_background:
|
||||
if playing and not self.live_controller.media_info.is_theme_background:
|
||||
self.media_pause(self.live_controller)
|
||||
self._media_set_visibility(self.live_controller, False)
|
||||
|
||||
|
@ -818,15 +833,13 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
"""
|
||||
Reset all the media controllers when OpenLP shuts down
|
||||
"""
|
||||
self.live_timer.stop()
|
||||
self.live_hide_timer.stop()
|
||||
self.live_kill_timer.stop()
|
||||
self.preview_timer.stop()
|
||||
self.media_reset(self._display_controllers(DisplayControllerType.Live))
|
||||
self.media_reset(self._display_controllers(DisplayControllerType.Preview))
|
||||
|
||||
@staticmethod
|
||||
def _define_display(controller: SlideController) -> Type[DisplayWindow]:
|
||||
def _define_display(controller: SlideController) -> DisplayWindow:
|
||||
"""
|
||||
Extract the correct display for a given controller
|
||||
|
||||
|
|
|
@ -22,7 +22,11 @@
|
|||
The :mod:`~openlp.core.ui.media.mediaplayer` module contains the MediaPlayer class.
|
||||
"""
|
||||
from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.core.common.platform import is_macosx, is_win
|
||||
from openlp.core.display.screens import ScreenList
|
||||
from openlp.core.display.window import DisplayWindow
|
||||
from openlp.core.ui.media import MediaState
|
||||
from openlp.core.ui.slidecontroller import SlideController
|
||||
|
||||
|
||||
class MediaPlayer(RegistryProperties):
|
||||
|
@ -41,6 +45,7 @@ class MediaPlayer(RegistryProperties):
|
|||
self.can_folder = False
|
||||
self.state = {0: MediaState.Off, 1: MediaState.Off}
|
||||
self.has_own_widget = False
|
||||
self.can_repeat = False
|
||||
|
||||
def check_available(self):
|
||||
"""
|
||||
|
@ -66,13 +71,35 @@ class MediaPlayer(RegistryProperties):
|
|||
"""
|
||||
return True
|
||||
|
||||
def resize(self, controller):
|
||||
def add_display(self, controller: SlideController):
|
||||
# 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(controller.vlc_widget.winId())
|
||||
if is_win():
|
||||
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.
|
||||
controller.vlc_media_player.set_nsobject(win_id)
|
||||
else:
|
||||
# for Linux/*BSD using the X Server
|
||||
controller.vlc_media_player.set_xwindow(win_id)
|
||||
self.has_own_widget = True
|
||||
|
||||
def resize(self, controller: SlideController) -> None:
|
||||
"""
|
||||
If the main display size or position is changed, the media widgets
|
||||
should also resized
|
||||
:param controller: Which Controller is running the show.
|
||||
Resize the player
|
||||
|
||||
:param controller: The display where the media is stored within the controller.
|
||||
:return:
|
||||
"""
|
||||
pass
|
||||
if controller.is_live:
|
||||
controller.vlc_widget.setGeometry(ScreenList().current.display_geometry)
|
||||
else:
|
||||
controller.vlc_widget.resize(controller.preview_display.size())
|
||||
|
||||
def play(self, controller, display):
|
||||
"""
|
||||
|
@ -99,68 +126,65 @@ class MediaPlayer(RegistryProperties):
|
|||
"""
|
||||
pass
|
||||
|
||||
def volume(self, controller, volume):
|
||||
def volume(self, controller: SlideController, vol: int) -> None:
|
||||
"""
|
||||
Change volume of current Media File
|
||||
Set the volume
|
||||
|
||||
:param controller: Which Controller is running the show.
|
||||
:param volume: The volume to set.
|
||||
:param vol: The volume to be sets
|
||||
:param controller: The controller where the media is
|
||||
:return:
|
||||
"""
|
||||
pass
|
||||
controller.vlc_media_player.audio_set_volume(vol)
|
||||
|
||||
def seek(self, controller, seek_value):
|
||||
def seek(self, controller: SlideController, seek_value: int) -> None:
|
||||
"""
|
||||
Change playing position of current Media File
|
||||
Go to a particular position
|
||||
|
||||
:param controller: Which Controller is running the show.
|
||||
:param seek_value: The where to seek to.
|
||||
:param seek_value: The position of where a seek goes to
|
||||
:param controller: The controller where the media is
|
||||
"""
|
||||
pass
|
||||
if controller.vlc_media_player.is_seekable():
|
||||
controller.vlc_media_player.set_time(seek_value)
|
||||
|
||||
def reset(self, controller):
|
||||
def reset(self, controller: SlideController) -> None:
|
||||
"""
|
||||
Remove the current loaded video
|
||||
Reset the player
|
||||
|
||||
:param controller: Which Controller is running the show.
|
||||
:param controller: The controller where the media is
|
||||
"""
|
||||
pass
|
||||
controller.vlc_media_player.stop()
|
||||
self.set_state(MediaState.Off, controller)
|
||||
|
||||
def set_visible(self, controller, status):
|
||||
def set_visible(self, controller: SlideController, status: bool) -> None:
|
||||
"""
|
||||
Show/Hide the media widgets
|
||||
Set the visibility
|
||||
|
||||
:param controller: Which Controller is running the show.
|
||||
:param status: The status to be set.
|
||||
:param controller: The controller where the media display is
|
||||
:param status: The visibility status
|
||||
"""
|
||||
pass
|
||||
controller.vlc_widget.setVisible(status)
|
||||
|
||||
def update_ui(self, controller, output_display):
|
||||
def update_ui(self, controller: SlideController, output_display: DisplayWindow) -> None:
|
||||
"""
|
||||
Do some ui related stuff (e.g. update the seek slider)
|
||||
Update the UI
|
||||
|
||||
:param controller: Which Controller is running the show.
|
||||
:param output_display: The display where the media is
|
||||
"""
|
||||
if not controller.mediabar.seek_slider.isSliderDown():
|
||||
controller.mediabar.seek_slider.blockSignals(True)
|
||||
controller.mediabar.seek_slider.setSliderPosition(controller.vlc_media_player.get_time())
|
||||
controller.mediabar.seek_slider.blockSignals(False)
|
||||
|
||||
def toggle_loop(self, controller, loop_required: bool) -> None:
|
||||
"""
|
||||
Changes the looping style
|
||||
:param controller: Which Controller is running the show.
|
||||
:param loop_required: Are we to be toggled or not
|
||||
:return: none
|
||||
"""
|
||||
pass
|
||||
|
||||
def get_media_display_css(self):
|
||||
"""
|
||||
Add css style sheets to htmlbuilder
|
||||
"""
|
||||
return ''
|
||||
|
||||
def get_media_display_javascript(self):
|
||||
"""
|
||||
Add javascript functions to htmlbuilder
|
||||
"""
|
||||
return ''
|
||||
|
||||
def get_media_display_html(self):
|
||||
"""
|
||||
Add html code to htmlbuilder
|
||||
"""
|
||||
return ''
|
||||
|
||||
def get_live_state(self):
|
||||
"""
|
||||
Get the state of the live player
|
||||
|
|
|
@ -28,14 +28,12 @@ import sys
|
|||
import threading
|
||||
from datetime import datetime
|
||||
from time import sleep
|
||||
from typing import Type
|
||||
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.mixins import LogMixin
|
||||
from openlp.core.common.platform import is_linux, is_macosx, is_win
|
||||
from openlp.core.display.screens import ScreenList
|
||||
from openlp.core.common.platform import is_linux, is_win
|
||||
from openlp.core.display.window import DisplayWindow
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.ui.slidecontroller import SlideController
|
||||
|
@ -102,7 +100,7 @@ class VlcPlayer(MediaPlayer, LogMixin):
|
|||
self.parent = parent
|
||||
self.can_folder = True
|
||||
|
||||
def setup(self, controller: SlideController, display: Type[DisplayWindow]) -> None:
|
||||
def setup(self, controller: SlideController, display: DisplayWindow) -> None:
|
||||
"""
|
||||
Set up the media player
|
||||
|
||||
|
@ -142,22 +140,7 @@ class VlcPlayer(MediaPlayer, LogMixin):
|
|||
controller.vlc_media_player = controller.vlc_instance.media_player_new()
|
||||
controller.vlc_widget.resize(controller.size())
|
||||
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(controller.vlc_widget.winId())
|
||||
if is_win():
|
||||
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.
|
||||
controller.vlc_media_player.set_nsobject(win_id)
|
||||
else:
|
||||
# for Linux/*BSD using the X Server
|
||||
controller.vlc_media_player.set_xwindow(win_id)
|
||||
self.has_own_widget = True
|
||||
self.add_display(controller)
|
||||
|
||||
def check_available(self):
|
||||
"""
|
||||
|
@ -165,9 +148,9 @@ class VlcPlayer(MediaPlayer, LogMixin):
|
|||
"""
|
||||
return get_vlc() is not None
|
||||
|
||||
def load(self, controller: SlideController, output_display: Type[DisplayWindow], file: str) -> bool:
|
||||
def load(self, controller: SlideController, output_display: DisplayWindow, file: str) -> bool:
|
||||
"""
|
||||
Load a video into VLC
|
||||
Load a Stream or DVD into VLC
|
||||
|
||||
:param controller: The controller where the media is
|
||||
:param output_display: The display where the media is
|
||||
|
@ -177,6 +160,7 @@ class VlcPlayer(MediaPlayer, LogMixin):
|
|||
if not controller.vlc_instance:
|
||||
return False
|
||||
self.log_debug('load video in VLC Controller')
|
||||
self.add_display(controller)
|
||||
path = None
|
||||
if file and not controller.media_info.media_type == MediaType.Stream:
|
||||
path = os.path.normcase(file)
|
||||
|
@ -219,15 +203,11 @@ class VlcPlayer(MediaPlayer, LogMixin):
|
|||
res = controller.vlc_media_player.video_set_spu(controller.media_info.subtitle_track)
|
||||
self.log_debug('vlc play, subtitle_track set: ' +
|
||||
str(controller.media_info.subtitle_track) + ' ' + str(res))
|
||||
elif controller.media_info.media_type == MediaType.Stream:
|
||||
else:
|
||||
# We must be Streaming
|
||||
controller.vlc_media = controller.vlc_instance.media_new_location(file[0])
|
||||
controller.vlc_media.add_options(file[1])
|
||||
controller.vlc_media_player.set_media(controller.vlc_media)
|
||||
else:
|
||||
controller.vlc_media = controller.vlc_instance.media_new_path(path)
|
||||
controller.vlc_media_player.set_media(controller.vlc_media)
|
||||
controller.media_info.start_time = 0
|
||||
controller.media_info.end_time = controller.media_info.length
|
||||
# parse the metadata of the file
|
||||
controller.vlc_media.parse()
|
||||
controller.mediabar.seek_slider.setMinimum(controller.media_info.start_time)
|
||||
|
@ -235,7 +215,7 @@ class VlcPlayer(MediaPlayer, LogMixin):
|
|||
self.volume(controller, get_volume(controller))
|
||||
return True
|
||||
|
||||
def media_state_wait(self, controller: Type[SlideController], media_state: VlCState) -> bool:
|
||||
def media_state_wait(self, controller: SlideController, media_state: VlCState) -> bool:
|
||||
"""
|
||||
Wait for the video to change its state
|
||||
Wait no longer than 60 seconds. (loading an iso file needs a long time)
|
||||
|
@ -254,19 +234,7 @@ class VlcPlayer(MediaPlayer, LogMixin):
|
|||
return False
|
||||
return True
|
||||
|
||||
def resize(self, controller: Type[SlideController]) -> None:
|
||||
"""
|
||||
Resize the player
|
||||
|
||||
:param controller: The display where the media is stored within the controller.
|
||||
:return:
|
||||
"""
|
||||
if controller.is_live:
|
||||
controller.vlc_widget.setGeometry(ScreenList().current.display_geometry)
|
||||
else:
|
||||
controller.vlc_widget.resize(controller.preview_display.size())
|
||||
|
||||
def play(self, controller: Type[SlideController], output_display: Type[DisplayWindow]) -> bool:
|
||||
def play(self, controller: SlideController, output_display: DisplayWindow) -> bool:
|
||||
"""
|
||||
Play the current item
|
||||
|
||||
|
@ -282,7 +250,7 @@ class VlcPlayer(MediaPlayer, LogMixin):
|
|||
self.set_state(MediaState.Playing, controller)
|
||||
return True
|
||||
|
||||
def pause(self, controller: Type[SlideController]) -> None:
|
||||
def pause(self, controller: SlideController) -> None:
|
||||
"""
|
||||
Pause the current item
|
||||
|
||||
|
@ -295,7 +263,7 @@ class VlcPlayer(MediaPlayer, LogMixin):
|
|||
if self.media_state_wait(controller, VlCState.Paused):
|
||||
self.set_state(MediaState.Paused, controller)
|
||||
|
||||
def stop(self, controller: Type[SlideController]) -> None:
|
||||
def stop(self, controller: SlideController) -> None:
|
||||
"""
|
||||
Stop the current item
|
||||
|
||||
|
@ -304,53 +272,3 @@ class VlcPlayer(MediaPlayer, LogMixin):
|
|||
"""
|
||||
threading.Thread(target=controller.vlc_media_player.stop).start()
|
||||
self.set_state(MediaState.Stopped, controller)
|
||||
|
||||
def volume(self, controller: Type[SlideController], vol: int) -> None:
|
||||
"""
|
||||
Set the volume
|
||||
|
||||
:param vol: The volume to be sets
|
||||
:param controller: The controller where the media is
|
||||
:return:
|
||||
"""
|
||||
controller.vlc_media_player.audio_set_volume(vol)
|
||||
|
||||
def seek(self, controller: Type[SlideController], seek_value: int) -> None:
|
||||
"""
|
||||
Go to a particular position
|
||||
|
||||
:param seek_value: The position of where a seek goes to
|
||||
:param controller: The controller where the media is
|
||||
"""
|
||||
if controller.vlc_media_player.is_seekable():
|
||||
controller.vlc_media_player.set_time(seek_value)
|
||||
|
||||
def reset(self, controller: Type[SlideController]) -> None:
|
||||
"""
|
||||
Reset the player
|
||||
|
||||
:param controller: The controller where the media is
|
||||
"""
|
||||
controller.vlc_media_player.stop()
|
||||
self.set_state(MediaState.Off, controller)
|
||||
|
||||
def set_visible(self, controller: Type[SlideController], status: bool) -> None:
|
||||
"""
|
||||
Set the visibility
|
||||
|
||||
:param controller: The controller where the media display is
|
||||
:param status: The visibility status
|
||||
"""
|
||||
controller.vlc_widget.setVisible(status)
|
||||
|
||||
def update_ui(self, controller: Type[SlideController], output_display: Type[DisplayWindow]) -> None:
|
||||
"""
|
||||
Update the UI
|
||||
|
||||
:param controller: Which Controller is running the show.
|
||||
:param output_display: The display where the media is
|
||||
"""
|
||||
if not controller.mediabar.seek_slider.isSliderDown():
|
||||
controller.mediabar.seek_slider.blockSignals(True)
|
||||
controller.mediabar.seek_slider.setSliderPosition(controller.vlc_media_player.get_time())
|
||||
controller.mediabar.seek_slider.blockSignals(False)
|
||||
|
|
|
@ -0,0 +1,234 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2023 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
"""
|
||||
The :mod:`~openlp.core.ui.media.vlcplayer` module contains our VLC component wrapper
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
import threading
|
||||
from datetime import datetime
|
||||
from time import sleep
|
||||
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.mixins import LogMixin
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.display.window import DisplayWindow
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.ui.slidecontroller import SlideController
|
||||
from openlp.core.ui.media import MediaState, VlCState, get_volume
|
||||
from openlp.core.ui.media.mediaplayer import MediaPlayer
|
||||
from openlp.core.ui.media.vlcplayer import get_vlc
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
STATE_WAIT_TIME = 60
|
||||
|
||||
|
||||
def end_reached(event, controller: SlideController) -> None:
|
||||
"""
|
||||
Process the end of a Track, triggered by VLC
|
||||
:param event: the vlc event triggered
|
||||
:param controller: The controller upon which the event occurs
|
||||
:return: None
|
||||
"""
|
||||
if controller.is_live:
|
||||
Registry().get('media_controller').vlc_live_media_stop.emit()
|
||||
else:
|
||||
Registry().get('media_controller').vlc_preview_media_stop.emit()
|
||||
|
||||
|
||||
def pos_callback(event, controller: SlideController) -> None:
|
||||
"""
|
||||
A Tick event triggered by VLC
|
||||
:param event: The VLC Event triggered
|
||||
:param controller: The controller upon which the event occurs
|
||||
:return: None
|
||||
"""
|
||||
controller.media_info.timer = controller.vlc_media_player.get_time()
|
||||
if controller.is_live:
|
||||
Registry().get('media_controller').vlc_live_media_tick.emit()
|
||||
else:
|
||||
Registry().get('media_controller').vlc_preview_media_tick.emit()
|
||||
|
||||
|
||||
class VlcPlayerPL(MediaPlayer, LogMixin):
|
||||
"""
|
||||
A specialised version of the MediaPlayer class, which provides a VLC display with a Playlist.
|
||||
"""
|
||||
def __init__(self, parent):
|
||||
"""
|
||||
Constructor
|
||||
"""
|
||||
super(VlcPlayerPL, self).__init__(parent, 'vlc')
|
||||
self.original_name = 'VLC'
|
||||
self.display_name = '&VLC'
|
||||
self.parent = parent
|
||||
self.can_folder = True
|
||||
self.can_repeat = True
|
||||
self.can_background = True
|
||||
|
||||
def setup(self, controller: SlideController, display: DisplayWindow) -> None:
|
||||
"""
|
||||
Set up the media player
|
||||
|
||||
:param controller: The controller where the media is
|
||||
:param display: The display where the media is.
|
||||
:return:
|
||||
"""
|
||||
vlc = get_vlc()
|
||||
if controller.is_live:
|
||||
controller.vlc_widget = QtWidgets.QFrame(controller)
|
||||
controller.vlc_widget.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowType.Tool |
|
||||
QtCore.Qt.WindowStaysOnTopHint)
|
||||
else:
|
||||
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 self.settings.value('advanced/hide mouse') and controller.is_live:
|
||||
command_line_options += '--mouse-hide-timeout=0 '
|
||||
if self.settings.value('media/vlc arguments'):
|
||||
options = command_line_options + ' ' + self.settings.value('media/vlc arguments')
|
||||
controller.vlc_instance = vlc.Instance(options)
|
||||
# if the instance is None, it is likely that the comamndline options were invalid, so try again without
|
||||
if not controller.vlc_instance:
|
||||
controller.vlc_instance = vlc.Instance(command_line_options)
|
||||
if controller.vlc_instance:
|
||||
critical_error_message_box(message=translate('MediaPlugin.VlcPlayer',
|
||||
'The VLC arguments are invalid.'))
|
||||
else:
|
||||
return
|
||||
else:
|
||||
controller.vlc_instance = vlc.Instance(command_line_options)
|
||||
if not controller.vlc_instance:
|
||||
return
|
||||
self.log_debug(f"VLC version: {vlc.libvlc_get_version()}")
|
||||
# creating an empty vlc media player
|
||||
self.has_own_widget = True
|
||||
|
||||
def check_available(self):
|
||||
"""
|
||||
Return the availability of VLC
|
||||
"""
|
||||
return get_vlc() is not None
|
||||
|
||||
def load(self, controller: SlideController, output_display: DisplayWindow, file: str) -> bool:
|
||||
"""
|
||||
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/stream to be played
|
||||
:return: Success or Failure
|
||||
"""
|
||||
self.log_debug('load video in VLC Controller')
|
||||
if not controller.vlc_instance:
|
||||
return False
|
||||
# The media player moved here to clear the playlist between uses.
|
||||
controller.vlc_media_player = controller.vlc_instance.media_player_new()
|
||||
controller.vlc_widget.resize(controller.size())
|
||||
controller.vlc_widget.hide()
|
||||
self.add_display(controller)
|
||||
path = os.path.normcase(file)
|
||||
controller.vlc_media = controller.vlc_instance.media_list_new()
|
||||
controller.vlc_media.add_media(path)
|
||||
controller.vlc_media_listPlayer = controller.vlc_instance.media_list_player_new()
|
||||
controller.vlc_media_listPlayer.set_media_player(controller.vlc_media_player)
|
||||
controller.vlc_media_listPlayer.set_media_list(controller.vlc_media)
|
||||
controller.vlc_events = controller.vlc_media_player.event_manager()
|
||||
vlc = get_vlc()
|
||||
controller.vlc_events.event_attach(vlc.EventType.MediaPlayerTimeChanged, pos_callback, controller)
|
||||
controller.vlc_events.event_attach(vlc.EventType.MediaPlayerEndReached, end_reached, controller)
|
||||
controller.media_info.start_time = 0
|
||||
controller.media_info.end_time = controller.media_info.length
|
||||
# parse the metadata of the file
|
||||
controller.mediabar.seek_slider.setMinimum(controller.media_info.start_time)
|
||||
controller.mediabar.seek_slider.setMaximum(controller.media_info.end_time)
|
||||
self.volume(controller, get_volume(controller))
|
||||
return True
|
||||
|
||||
def media_state_wait(self, controller: SlideController, media_state: VlCState) -> bool:
|
||||
"""
|
||||
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 controller: The controller where the media is
|
||||
:return:
|
||||
"""
|
||||
start = datetime.now()
|
||||
while media_state != controller.vlc_media_listPlayer.get_state():
|
||||
sleep(0.1)
|
||||
if controller.vlc_media_listPlayer.get_state() == VlCState.Error:
|
||||
return False
|
||||
self.application.process_events()
|
||||
if (datetime.now() - start).seconds > STATE_WAIT_TIME:
|
||||
return False
|
||||
return True
|
||||
|
||||
def toggle_loop(self, controller, loop_required: bool) -> None:
|
||||
vlc = get_vlc()
|
||||
if loop_required:
|
||||
controller.vlc_media_listPlayer.set_playback_mode(vlc.PlaybackMode().loop)
|
||||
else:
|
||||
controller.vlc_media_listPlayer.set_playback_mode(vlc.PlaybackMode().default)
|
||||
|
||||
def play(self, controller: SlideController, output_display: DisplayWindow) -> bool:
|
||||
"""
|
||||
Play the current item
|
||||
|
||||
:param controller: Which Controller is running the show.
|
||||
:param output_display: The display where the media is
|
||||
:return:
|
||||
"""
|
||||
self.log_debug('vlc play, mediatype: ' + str(controller.media_info.media_type))
|
||||
threading.Thread(target=controller.vlc_media_listPlayer.play).start()
|
||||
if not self.media_state_wait(controller, VlCState.Playing):
|
||||
return False
|
||||
self.volume(controller, get_volume(controller))
|
||||
self.set_state(MediaState.Playing, controller)
|
||||
return True
|
||||
|
||||
def pause(self, controller: SlideController) -> None:
|
||||
"""
|
||||
Pause the current item
|
||||
|
||||
:param controller: The controller which is managing the display
|
||||
:return:
|
||||
"""
|
||||
if controller.vlc_media_listPlayer.get_state() != VlCState.Playing:
|
||||
return
|
||||
controller.vlc_media_listPlayer.pause()
|
||||
if self.media_state_wait(controller, VlCState.Paused):
|
||||
self.set_state(MediaState.Paused, controller)
|
||||
|
||||
def stop(self, controller: SlideController) -> None:
|
||||
"""
|
||||
Stop the current item
|
||||
|
||||
:param controller: The controller where the media is
|
||||
:return:
|
||||
"""
|
||||
threading.Thread(target=controller.vlc_media_listPlayer.stop).start()
|
||||
self.set_state(MediaState.Stopped, controller)
|
|
@ -45,8 +45,9 @@ TEST_MEDIA = [['avi_file.avi', 61495], ['mp3_file.mp3', 134426], ['mpg_file.mpg'
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def media_env(registry):
|
||||
"""Local test setup"""
|
||||
def media_env(qapp, registry):
|
||||
"""Local test setup - qapp need to allow tests to run standalone.
|
||||
"""
|
||||
Registry().register('service_manager', MagicMock())
|
||||
media_controller = MediaController()
|
||||
yield media_controller
|
||||
|
@ -271,7 +272,7 @@ def test_resize(media_env):
|
|||
mocked_display = MagicMock()
|
||||
|
||||
# WHEN: resize() is called
|
||||
media_env.media_controller.resize(mocked_display, mocked_player)
|
||||
media_env.media_controller._resize(mocked_display, mocked_player)
|
||||
|
||||
# THEN: The player's resize method should be called correctly
|
||||
mocked_player.resize.assert_called_with(mocked_display)
|
||||
|
@ -338,7 +339,7 @@ def test_check_file_video(media_env):
|
|||
mocked_controller.media_info = ItemMediaInfo()
|
||||
mocked_controller.media_info.file_info = [TEST_PATH / 'mp3_file.mp3']
|
||||
media_env.media_controller.current_media_players = {}
|
||||
media_env.media_controller.vlc_player = MagicMock()
|
||||
media_env.media_controller.vlc_playerpl = MagicMock()
|
||||
|
||||
# WHEN: calling _check_file_type when no players exists
|
||||
ret = media_env.media_controller._check_file_type(mocked_controller, mocked_display)
|
||||
|
@ -358,7 +359,7 @@ def test_check_file_audio(media_env):
|
|||
mocked_controller.media_info = ItemMediaInfo()
|
||||
mocked_controller.media_info.file_info = [TEST_PATH / 'mp4_file.mp4']
|
||||
media_env.media_controller.current_media_players = {}
|
||||
media_env.media_controller.vlc_player = MagicMock()
|
||||
media_env.media_controller.vlc_playerpl = MagicMock()
|
||||
|
||||
# WHEN: calling _check_file_type when no players exists
|
||||
ret = media_env.media_controller._check_file_type(mocked_controller, mocked_display)
|
||||
|
@ -747,7 +748,7 @@ def test_setup_display(MockItemMediaInfo, media_env):
|
|||
media_env.media_controller.vlc_player = MagicMock()
|
||||
mocked_display = MagicMock()
|
||||
media_env.media_controller._define_display = MagicMock(return_value=mocked_display)
|
||||
media_env.media_controller.vlc_player = MagicMock()
|
||||
media_env.media_controller.vlc_playerpl = MagicMock()
|
||||
controller = MagicMock()
|
||||
|
||||
# WHEN: setup_display() is called
|
||||
|
@ -756,8 +757,8 @@ def test_setup_display(MockItemMediaInfo, media_env):
|
|||
# THEN: The right calls should have been made
|
||||
assert controller.media_info == mocked_media_info
|
||||
assert controller.has_audio is False
|
||||
media_env.media_controller._define_display.assert_called_once_with(controller)
|
||||
media_env.media_controller.vlc_player.setup(controller, mocked_display, False)
|
||||
media_env.media_controller._define_display.assert_called_with(controller)
|
||||
media_env.media_controller.vlc_playerpl.setup(controller, mocked_display, False)
|
||||
|
||||
|
||||
def test_media_play(media_env):
|
||||
|
@ -812,6 +813,7 @@ def test_decide_autoplay_media_normal_hidden_live(media_env, settings):
|
|||
settings.setValue('core/auto unblank', True)
|
||||
settings.setValue('media/media auto start', QtCore.Qt.CheckState.Unchecked)
|
||||
media_env.media_controller.is_live = True
|
||||
media_env.media_controller.media_info = ItemMediaInfo()
|
||||
media_env.media_controller.is_theme_background = True
|
||||
# WHEN: decide_autoplay() is called
|
||||
ret = media_env.media_controller.decide_autoplay(mocked_service_item, media_env.media_controller, HideMode.Theme)
|
||||
|
@ -829,6 +831,7 @@ def test_decide_autoplay_media_normal_not_hidden_live(media_env, settings):
|
|||
settings.setValue('core/auto unblank', False)
|
||||
settings.setValue('media/media auto start', QtCore.Qt.CheckState.Unchecked)
|
||||
media_env.media_controller.is_live = True
|
||||
media_env.media_controller.media_info = ItemMediaInfo()
|
||||
media_env.media_controller.is_theme_background = False
|
||||
# WHEN: decide_autoplay() is called
|
||||
ret = media_env.media_controller.decide_autoplay(mocked_service_item, media_env.media_controller, HideMode.Screen)
|
||||
|
@ -847,6 +850,7 @@ def test_decide_autoplay_media_autostart_not_hidden_live(media_env, settings):
|
|||
settings.setValue('core/auto unblank', False)
|
||||
settings.setValue('media/media auto start', QtCore.Qt.CheckState.Unchecked)
|
||||
media_env.media_controller.is_live = True
|
||||
media_env.media_controller.media_info = ItemMediaInfo()
|
||||
media_env.media_controller.is_theme_background = False
|
||||
# WHEN: decide_autoplay() is called
|
||||
ret = media_env.media_controller.decide_autoplay(mocked_service_item, media_env.media_controller, False)
|
||||
|
@ -865,6 +869,7 @@ def test_decide_autoplay_media_global_autostart_not_hidden_live(media_env, setti
|
|||
settings.setValue('core/auto unblank', False)
|
||||
settings.setValue('media/media auto start', QtCore.Qt.CheckState.Checked)
|
||||
media_env.media_controller.is_live = True
|
||||
media_env.media_controller.media_info = ItemMediaInfo()
|
||||
media_env.media_controller.is_theme_background = False
|
||||
# WHEN: decide_autoplay() is called
|
||||
ret = media_env.media_controller.decide_autoplay(mocked_service_item, media_env.media_controller, False)
|
||||
|
@ -882,7 +887,8 @@ def test_decide_autoplay_media_normal_autounblank_live(media_env, settings):
|
|||
settings.setValue('core/auto unblank', True)
|
||||
settings.setValue('media/media auto start', QtCore.Qt.CheckState.Unchecked)
|
||||
media_env.media_controller.is_live = True
|
||||
media_env.media_controller.is_theme_background = False
|
||||
media_env.media_controller.media_info = ItemMediaInfo()
|
||||
media_env.media_controller.media_info.is_theme_background = False
|
||||
# WHEN: decide_autoplay() is called
|
||||
ret = media_env.media_controller.decide_autoplay(mocked_service_item, media_env.media_controller, HideMode.Screen)
|
||||
# THEN: Autoplay will obey the following
|
||||
|
@ -901,7 +907,8 @@ def test_media_bar_play(media_env, settings):
|
|||
mocked_controller.mediabar.actions['playbackPause'] = MagicMock()
|
||||
mocked_controller.mediabar.actions['playbackStop'] = MagicMock()
|
||||
mocked_controller.mediabar.actions['playbackLoop'] = MagicMock()
|
||||
media_env.is_theme_background = False
|
||||
media_env.media_controller.current_media_players = MagicMock()
|
||||
mocked_controller.media_info.is_theme_background = False
|
||||
settings.setValue('media/live loop', False)
|
||||
# WHEN: _media_bar() is called
|
||||
media_env.media_controller._media_bar(mocked_controller, "play")
|
||||
|
@ -924,7 +931,8 @@ def test_media_bar_stop(media_env, settings, mode):
|
|||
mocked_controller.mediabar.actions['playbackPause'] = MagicMock()
|
||||
mocked_controller.mediabar.actions['playbackStop'] = MagicMock()
|
||||
mocked_controller.mediabar.actions['playbackLoop'] = MagicMock()
|
||||
media_env.is_theme_background = False
|
||||
media_env.media_controller.current_media_players = MagicMock()
|
||||
mocked_controller.media_info.is_theme_background = False
|
||||
settings.setValue('media/live loop', False)
|
||||
# WHEN: _media_bar() is called
|
||||
media_env.media_controller._media_bar(mocked_controller, mode)
|
||||
|
@ -934,10 +942,10 @@ def test_media_bar_stop(media_env, settings, mode):
|
|||
mocked_controller.mediabar.actions['playbackStop'].setDisabled.assert_called_with(False)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("back, stream, result", [(False, MediaType.Video, False),
|
||||
(True, MediaType.Video, False),
|
||||
(False, MediaType.Stream, True)])
|
||||
def test_media_bar_loop_disabled(media_env, settings, back, stream, result):
|
||||
@pytest.mark.parametrize("back, repeat, result", [(False, True, False),
|
||||
(True, True, False),
|
||||
(False, False, False)])
|
||||
def test_media_bar_loop_disabled(media_env, settings, back, repeat, result):
|
||||
"""
|
||||
Test that media bar is set correctly following a list of events
|
||||
"""
|
||||
|
@ -945,14 +953,17 @@ def test_media_bar_loop_disabled(media_env, settings, back, stream, result):
|
|||
mocked_controller = MagicMock()
|
||||
mocked_controller.media_info = ItemMediaInfo()
|
||||
mocked_controller.is_live = True
|
||||
mocked_controller.controller_type = 1 # Live
|
||||
mocked_controller.media_info.is_background = back
|
||||
mocked_controller.media_info.media_type = stream
|
||||
mocked_controller.media_info.is_theme_background = False
|
||||
mocked_controller.mediabar = OpenLPToolbar(None)
|
||||
mocked_controller.mediabar.actions['playbackPlay'] = MagicMock()
|
||||
mocked_controller.mediabar.actions['playbackPause'] = MagicMock()
|
||||
mocked_controller.mediabar.actions['playbackStop'] = MagicMock()
|
||||
mocked_controller.mediabar.actions['playbackLoop'] = MagicMock()
|
||||
media_env.is_theme_background = False
|
||||
mocked_vlc_player = MagicMock()
|
||||
mocked_vlc_player.can_repeat.return_value = repeat
|
||||
media_env.current_media_players = {1: mocked_vlc_player}
|
||||
# WHEN: _media_bar() is called
|
||||
media_env.media_controller._media_bar(mocked_controller, "load")
|
||||
# THEN: The following functions should have been called
|
||||
|
@ -968,14 +979,18 @@ def test_media_bar_loop_checked(media_env, settings, loop, result):
|
|||
mocked_controller = MagicMock()
|
||||
mocked_controller.media_info = ItemMediaInfo()
|
||||
mocked_controller.is_live = True
|
||||
mocked_controller.controller_type = 1 # Live
|
||||
mocked_controller.media_info.is_background = True
|
||||
mocked_controller.media_info.is_theme_background = False
|
||||
mocked_controller.media_info.media_type = MediaType.Video
|
||||
mocked_controller.mediabar = OpenLPToolbar(None)
|
||||
mocked_controller.mediabar.actions['playbackPlay'] = MagicMock()
|
||||
mocked_controller.mediabar.actions['playbackPause'] = MagicMock()
|
||||
mocked_controller.mediabar.actions['playbackStop'] = MagicMock()
|
||||
mocked_controller.mediabar.actions['playbackLoop'] = MagicMock()
|
||||
media_env.is_theme_background = False
|
||||
mocked_vlc_player = MagicMock()
|
||||
mocked_vlc_player.can_repeat.return_value = True
|
||||
media_env.current_media_players = {1: mocked_vlc_player}
|
||||
settings.setValue('media/live loop', loop)
|
||||
# WHEN: _media_bar() is called
|
||||
media_env.media_controller._media_bar(mocked_controller, "load")
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
##########################################################################
|
||||
"""
|
||||
Package to test the openlp.core.ui.media.vlcplayer package.
|
||||
This class is for DVD and Streaming using a Player.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
@ -62,8 +63,8 @@ def test_init(mock_settings):
|
|||
assert vlc_player.can_folder is True
|
||||
|
||||
|
||||
@patch('openlp.core.ui.media.vlcplayer.is_win')
|
||||
@patch('openlp.core.ui.media.vlcplayer.is_macosx')
|
||||
@patch('openlp.core.ui.media.mediaplayer.is_win')
|
||||
@patch('openlp.core.ui.media.mediaplayer.is_macosx')
|
||||
@patch('openlp.core.ui.media.vlcplayer.get_vlc')
|
||||
@patch('openlp.core.ui.media.vlcplayer.QtWidgets')
|
||||
def test_setup(MockedQtWidgets, mocked_get_vlc, mocked_is_macosx, mocked_is_win, mock_settings):
|
||||
|
@ -109,8 +110,8 @@ def test_setup(MockedQtWidgets, mocked_get_vlc, mocked_is_macosx, mocked_is_win,
|
|||
assert vlc_player.has_own_widget is True
|
||||
|
||||
|
||||
@patch('openlp.core.ui.media.vlcplayer.is_win')
|
||||
@patch('openlp.core.ui.media.vlcplayer.is_macosx')
|
||||
@patch('openlp.core.ui.media.mediaplayer.is_win')
|
||||
@patch('openlp.core.ui.media.mediaplayer.is_macosx')
|
||||
@patch('openlp.core.ui.media.vlcplayer.get_vlc')
|
||||
@patch('openlp.core.ui.media.vlcplayer.QtWidgets')
|
||||
def test_setup_has_audio(MockedQtWidgets, mocked_get_vlc, mocked_is_macosx, mocked_is_win, mock_settings):
|
||||
|
@ -144,8 +145,8 @@ def test_setup_has_audio(MockedQtWidgets, mocked_get_vlc, mocked_is_macosx, mock
|
|||
mocked_vlc.Instance.assert_called_with('--no-video-title-show --input-repeat=99999999 ')
|
||||
|
||||
|
||||
@patch('openlp.core.ui.media.vlcplayer.is_win')
|
||||
@patch('openlp.core.ui.media.vlcplayer.is_macosx')
|
||||
@patch('openlp.core.ui.media.mediaplayer.is_win')
|
||||
@patch('openlp.core.ui.media.mediaplayer.is_macosx')
|
||||
@patch('openlp.core.ui.media.vlcplayer.get_vlc')
|
||||
@patch('openlp.core.ui.media.vlcplayer.QtWidgets')
|
||||
def test_setup_visible_mouse(MockedQtWidgets, mocked_get_vlc, mocked_is_macosx, mocked_is_win, mock_settings):
|
||||
|
@ -179,8 +180,8 @@ def test_setup_visible_mouse(MockedQtWidgets, mocked_get_vlc, mocked_is_macosx,
|
|||
mocked_vlc.Instance.assert_called_with('--no-video-title-show --input-repeat=99999999 ')
|
||||
|
||||
|
||||
@patch('openlp.core.ui.media.vlcplayer.is_win')
|
||||
@patch('openlp.core.ui.media.vlcplayer.is_macosx')
|
||||
@patch('openlp.core.ui.media.mediaplayer.is_win')
|
||||
@patch('openlp.core.ui.media.mediaplayer.is_macosx')
|
||||
@patch('openlp.core.ui.media.vlcplayer.get_vlc')
|
||||
@patch('openlp.core.ui.media.vlcplayer.QtWidgets')
|
||||
def test_setup_windows(MockedQtWidgets, mocked_get_vlc, mocked_is_macosx, mocked_is_win, mock_settings):
|
||||
|
@ -214,8 +215,8 @@ def test_setup_windows(MockedQtWidgets, mocked_get_vlc, mocked_is_macosx, mocked
|
|||
mocked_media_player_new.set_hwnd.assert_called_with(2)
|
||||
|
||||
|
||||
@patch('openlp.core.ui.media.vlcplayer.is_win')
|
||||
@patch('openlp.core.ui.media.vlcplayer.is_macosx')
|
||||
@patch('openlp.core.ui.media.mediaplayer.is_win')
|
||||
@patch('openlp.core.ui.media.mediaplayer.is_macosx')
|
||||
@patch('openlp.core.ui.media.vlcplayer.get_vlc')
|
||||
@patch('openlp.core.ui.media.vlcplayer.QtWidgets')
|
||||
def test_setup_osx(MockedQtWidgets, mocked_get_vlc, mocked_is_macosx, mocked_is_win, mock_settings):
|
||||
|
@ -283,7 +284,7 @@ def test_check_not_available(mocked_get_vlc):
|
|||
|
||||
@patch('openlp.core.ui.media.vlcplayer.get_vlc')
|
||||
@patch('openlp.core.ui.media.vlcplayer.os.path.normcase')
|
||||
def test_load(mocked_normcase, mocked_get_vlc, settings):
|
||||
def test_load_stream(mocked_normcase, mocked_get_vlc, settings):
|
||||
"""
|
||||
Test loading a video into VLC
|
||||
"""
|
||||
|
@ -294,24 +295,19 @@ def test_load(mocked_normcase, mocked_get_vlc, settings):
|
|||
mocked_get_vlc.return_value = mocked_vlc
|
||||
mocked_display = MagicMock()
|
||||
mocked_controller = MagicMock()
|
||||
mocked_controller.media_info.media_type = MediaType.Video
|
||||
mocked_controller.media_info.media_type = MediaType.Stream
|
||||
mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path
|
||||
mocked_vlc_media = MagicMock()
|
||||
mocked_controller.vlc_media = MagicMock()
|
||||
mocked_media = MagicMock()
|
||||
mocked_media.get_duration.return_value = 10000
|
||||
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
|
||||
result = vlc_player.load(mocked_controller, mocked_display, media_path)
|
||||
|
||||
# THEN: The video should be loaded
|
||||
mocked_normcase.assert_called_with(media_path)
|
||||
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_controller.vlc_instance.media_new_path.assert_not_called()
|
||||
mocked_controller.vlc_media_player.set_media.assert_called_with(mocked_controller.vlc_media)
|
||||
mocked_controller.vlc_media.parse.assert_called_with()
|
||||
assert result is True
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,581 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2023 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
"""
|
||||
Package to test the openlp.core.ui.media.vlcplayerpl package.
|
||||
This class is for Audio and Video only using a PlayList
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
from datetime import timedelta
|
||||
from unittest.mock import MagicMock, call, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.ui.media import MediaState, MediaType, VlCState
|
||||
from openlp.core.ui.media.vlcplayerpl import VlcPlayerPL
|
||||
from tests.helpers import MockDateTime
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def vlc_env():
|
||||
"""Local test setup"""
|
||||
if 'VLC_PLUGIN_PATH' in os.environ:
|
||||
del os.environ['VLC_PLUGIN_PATH']
|
||||
if 'openlp.core.ui.media.vendor.vlc' in sys.modules:
|
||||
del sys.modules['openlp.core.ui.media.vendor.vlc']
|
||||
yield
|
||||
MockDateTime.revert()
|
||||
|
||||
|
||||
def test_init(mock_settings):
|
||||
"""
|
||||
Test that the VLC player class initialises correctly
|
||||
"""
|
||||
# GIVEN: A mocked out list of extensions
|
||||
# TODO: figure out how to mock out the lists of extensions
|
||||
|
||||
# WHEN: The VlcPlayer class is instantiated
|
||||
vlc_player = VlcPlayerPL(None)
|
||||
|
||||
# THEN: The correct variables are set
|
||||
assert 'VLC' == vlc_player.original_name
|
||||
assert '&VLC' == vlc_player.display_name
|
||||
assert vlc_player.parent is None
|
||||
assert vlc_player.can_folder is True
|
||||
|
||||
|
||||
@patch('openlp.core.ui.media.vlcplayerpl.get_vlc')
|
||||
@patch('openlp.core.ui.media.vlcplayerpl.QtWidgets')
|
||||
def test_setup(MockedQtWidgets, mocked_get_vlc, mock_settings):
|
||||
"""
|
||||
Test the setup method
|
||||
"""
|
||||
# GIVEN: A bunch of mocked out stuff and a VlcPlayer object
|
||||
mock_settings.value.return_value = ''
|
||||
mocked_qframe = MagicMock()
|
||||
mocked_qframe.winId.return_value = 2
|
||||
MockedQtWidgets.QFrame.NoFrame = 1
|
||||
MockedQtWidgets.QFrame.return_value = mocked_qframe
|
||||
mocked_media_player_new = MagicMock()
|
||||
mocked_instance = MagicMock()
|
||||
mocked_instance.media_player_new.return_value = mocked_media_player_new
|
||||
mocked_vlc = MagicMock()
|
||||
mocked_vlc.Instance.return_value = mocked_instance
|
||||
mocked_get_vlc.return_value = mocked_vlc
|
||||
mocked_output_display = MagicMock()
|
||||
mocked_controller = MagicMock()
|
||||
mocked_controller.is_live = True
|
||||
mocked_output_display.size.return_value = (10, 10)
|
||||
vlc_player = VlcPlayerPL(None)
|
||||
|
||||
# WHEN: setup() is run
|
||||
vlc_player.setup(mocked_output_display, mocked_controller)
|
||||
|
||||
# THEN: The VLC widget should be set up correctly
|
||||
assert mocked_output_display.vlc_widget == mocked_qframe
|
||||
mocked_qframe.setFrameStyle.assert_called_with(1)
|
||||
mock_settings.value.assert_any_call('advanced/hide mouse')
|
||||
mock_settings.value.assert_any_call('media/vlc arguments')
|
||||
mocked_vlc.Instance.assert_called_with('--no-video-title-show ')
|
||||
assert mocked_output_display.vlc_instance == mocked_instance
|
||||
assert vlc_player.has_own_widget is True
|
||||
|
||||
|
||||
@patch('openlp.core.ui.media.vlcplayerpl.get_vlc')
|
||||
@patch('openlp.core.ui.media.vlcplayerpl.QtWidgets')
|
||||
def test_setup_has_audio(MockedQtWidgets, mocked_get_vlc, mock_settings):
|
||||
"""
|
||||
Test the setup method when has_audio is True
|
||||
"""
|
||||
# GIVEN: A bunch of mocked out stuff and a VlcPlayer object
|
||||
mock_settings.value.return_value = ''
|
||||
mocked_qframe = MagicMock()
|
||||
mocked_qframe.winId.return_value = 2
|
||||
MockedQtWidgets.QFrame.NoFrame = 1
|
||||
MockedQtWidgets.QFrame.return_value = mocked_qframe
|
||||
mocked_media_player_new = MagicMock()
|
||||
mocked_instance = MagicMock()
|
||||
mocked_instance.media_player_new.return_value = mocked_media_player_new
|
||||
mocked_vlc = MagicMock()
|
||||
mocked_vlc.Instance.return_value = mocked_instance
|
||||
mocked_get_vlc.return_value = mocked_vlc
|
||||
mocked_output_display = MagicMock()
|
||||
mocked_controller = MagicMock()
|
||||
mocked_controller.is_live = True
|
||||
mocked_output_display.size.return_value = (10, 10)
|
||||
vlc_player = VlcPlayerPL(None)
|
||||
|
||||
# WHEN: setup() is run
|
||||
vlc_player.setup(mocked_output_display, mocked_controller)
|
||||
|
||||
# THEN: The VLC instance should be created with the correct options
|
||||
mocked_vlc.Instance.assert_called_with('--no-video-title-show ')
|
||||
|
||||
|
||||
@patch('openlp.core.ui.media.vlcplayerpl.get_vlc')
|
||||
@patch('openlp.core.ui.media.vlcplayerpl.QtWidgets')
|
||||
def test_setup_visible_mouse(MockedQtWidgets, mocked_get_vlc, mock_settings):
|
||||
"""
|
||||
Test the setup method when Settings().value("hide mouse") is False
|
||||
"""
|
||||
# GIVEN: A bunch of mocked out stuff and a VlcPlayer object
|
||||
mock_settings.value.return_value = ''
|
||||
mocked_qframe = MagicMock()
|
||||
mocked_qframe.winId.return_value = 2
|
||||
MockedQtWidgets.QFrame.NoFrame = 1
|
||||
MockedQtWidgets.QFrame.return_value = mocked_qframe
|
||||
mocked_media_player_new = MagicMock()
|
||||
mocked_instance = MagicMock()
|
||||
mocked_instance.media_player_new.return_value = mocked_media_player_new
|
||||
mocked_vlc = MagicMock()
|
||||
mocked_vlc.Instance.return_value = mocked_instance
|
||||
mocked_get_vlc.return_value = mocked_vlc
|
||||
mocked_output_display = MagicMock()
|
||||
mocked_controller = MagicMock()
|
||||
mocked_controller.is_live = True
|
||||
mocked_output_display.size.return_value = (10, 10)
|
||||
vlc_player = VlcPlayerPL(None)
|
||||
|
||||
# WHEN: setup() is run
|
||||
vlc_player.setup(mocked_output_display, mocked_controller)
|
||||
|
||||
# THEN: The VLC instance should be created with the correct options
|
||||
mocked_vlc.Instance.assert_called_with('--no-video-title-show ')
|
||||
|
||||
|
||||
@patch('openlp.core.ui.media.vlcplayerpl.get_vlc')
|
||||
def test_check_available(mocked_get_vlc):
|
||||
"""
|
||||
Check that when the "vlc" module is available, then VLC is set as available
|
||||
"""
|
||||
# GIVEN: A mocked out get_vlc() method and a VlcPlayer instance
|
||||
mocked_get_vlc.return_value = MagicMock()
|
||||
vlc_player = VlcPlayerPL(None)
|
||||
|
||||
# WHEN: vlc
|
||||
is_available = vlc_player.check_available()
|
||||
|
||||
# THEN: VLC should be available
|
||||
assert is_available is True
|
||||
|
||||
|
||||
@patch('openlp.core.ui.media.vlcplayerpl.get_vlc')
|
||||
def test_check_not_available(mocked_get_vlc):
|
||||
"""
|
||||
Check that when the "vlc" module is not available, then VLC is set as unavailable
|
||||
"""
|
||||
# GIVEN: A mocked out get_vlc() method and a VlcPlayer instance
|
||||
mocked_get_vlc.return_value = None
|
||||
vlc_player = VlcPlayerPL(None)
|
||||
|
||||
# WHEN: vlc
|
||||
is_available = vlc_player.check_available()
|
||||
|
||||
# THEN: VLC should NOT be available
|
||||
assert is_available is False
|
||||
|
||||
|
||||
@patch('openlp.core.ui.media.vlcplayerpl.get_vlc')
|
||||
@patch('openlp.core.ui.media.vlcplayerpl.os.path.normcase')
|
||||
def test_load(mocked_normcase, mocked_get_vlc, settings):
|
||||
"""
|
||||
Test loading a video into VLC
|
||||
"""
|
||||
# GIVEN: A mocked out get_vlc() method
|
||||
media_path = '/path/to/media.mp4'
|
||||
mocked_normcase.side_effect = lambda x: x
|
||||
mocked_vlc = MagicMock()
|
||||
mocked_get_vlc.return_value = mocked_vlc
|
||||
mocked_display = MagicMock()
|
||||
mocked_controller = MagicMock()
|
||||
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_controller.vlc_instance.media_new_path.return_value = mocked_vlc_media
|
||||
mocked_controller.vlc_media_player.get_media.return_value = mocked_media
|
||||
vlc_player = VlcPlayerPL(None)
|
||||
|
||||
# WHEN: A video is loaded into VLC
|
||||
result = vlc_player.load(mocked_controller, mocked_display, media_path)
|
||||
# THEN: The video should be loaded
|
||||
mocked_normcase.assert_called_with(media_path)
|
||||
mocked_controller.vlc_instance.media_player_new.assert_called()
|
||||
mocked_controller.vlc_media.add_media.assert_called_with(media_path)
|
||||
assert result is True
|
||||
|
||||
|
||||
@patch('openlp.core.ui.media.vlcplayerpl.get_vlc')
|
||||
@patch('openlp.core.ui.media.vlcplayerpl.datetime', MockDateTime)
|
||||
def test_media_state_wait(mocked_get_vlc):
|
||||
"""
|
||||
Check that waiting for a state change works
|
||||
"""
|
||||
# GIVEN: A mocked out get_vlc method
|
||||
mocked_vlc = MagicMock()
|
||||
mocked_vlc.State.Error = 1
|
||||
mocked_get_vlc.return_value = mocked_vlc
|
||||
mocked_controller = MagicMock()
|
||||
mocked_controller.vlc_media_listPlayer.get_state.return_value = VlCState.Buffering
|
||||
Registry.create()
|
||||
mocked_application = MagicMock()
|
||||
Registry().register('application', mocked_application)
|
||||
vlc_player = VlcPlayerPL(None)
|
||||
|
||||
# WHEN: media_state_wait() is called
|
||||
result = vlc_player.media_state_wait(mocked_controller, VlCState.Buffering)
|
||||
|
||||
# THEN: The results should be True
|
||||
assert result is True
|
||||
|
||||
|
||||
@patch('openlp.core.ui.media.vlcplayerpl.get_vlc')
|
||||
@patch('openlp.core.ui.media.vlcplayerpl.datetime', MockDateTime)
|
||||
def test_media_state_wait_error(mocked_get_vlc, vlc_env):
|
||||
"""
|
||||
Check that getting an error when waiting for a state change returns False
|
||||
"""
|
||||
# GIVEN: A mocked out get_vlc method
|
||||
mocked_vlc = MagicMock()
|
||||
mocked_vlc.State.Error = 1
|
||||
mocked_get_vlc.return_value = mocked_vlc
|
||||
mocked_controller = MagicMock()
|
||||
mocked_controller.vlc_media_listPlayer.get_state.return_value = VlCState.Error
|
||||
Registry.create()
|
||||
mocked_application = MagicMock()
|
||||
Registry().register('application', mocked_application)
|
||||
vlc_player = VlcPlayerPL(None)
|
||||
|
||||
# WHEN: media_state_wait() is called
|
||||
result = vlc_player.media_state_wait(mocked_controller, VlCState.Buffering)
|
||||
|
||||
# THEN: The results should be True
|
||||
assert result is False
|
||||
|
||||
|
||||
@patch('openlp.core.ui.media.vlcplayerpl.get_vlc')
|
||||
@patch('openlp.core.ui.media.vlcplayerpl.datetime', MockDateTime)
|
||||
def test_media_state_wait_times_out(mocked_get_vlc, vlc_env):
|
||||
"""
|
||||
Check that waiting for a state returns False when it times out after 60 seconds
|
||||
"""
|
||||
# GIVEN: A mocked out get_vlc method
|
||||
timeout = MockDateTime.return_values[0] + timedelta(seconds=61)
|
||||
MockDateTime.return_values.append(timeout)
|
||||
mocked_vlc = MagicMock()
|
||||
mocked_vlc.State.Error = 1
|
||||
mocked_get_vlc.return_value = mocked_vlc
|
||||
mocked_controller = MagicMock()
|
||||
mocked_controller.vlc_media_listPlayer.get_state.return_value = VlCState.Buffering
|
||||
Registry.create()
|
||||
mocked_application = MagicMock()
|
||||
Registry().register('application', mocked_application)
|
||||
vlc_player = VlcPlayerPL(None)
|
||||
|
||||
# WHEN: media_state_wait() is called
|
||||
result = vlc_player.media_state_wait(mocked_controller, VlCState.Playing)
|
||||
|
||||
# THEN: The results should be True
|
||||
assert result is False
|
||||
|
||||
|
||||
def test_resize():
|
||||
"""
|
||||
Test resizing the player
|
||||
"""
|
||||
# GIVEN: A display object and a VlcPlayer instance
|
||||
mocked_controller = MagicMock()
|
||||
mocked_controller.preview_display.size.return_value = (10, 10)
|
||||
mocked_controller.is_live = False
|
||||
vlc_player = VlcPlayerPL(None)
|
||||
|
||||
# WHEN: resize is called
|
||||
vlc_player.resize(mocked_controller)
|
||||
|
||||
# THEN: The right methods should have been called
|
||||
mocked_controller.preview_display.size.assert_called_with()
|
||||
mocked_controller.vlc_widget.resize.assert_called_with((10, 10))
|
||||
|
||||
|
||||
@patch('openlp.core.ui.media.vlcplayerpl.threading')
|
||||
@patch('openlp.core.ui.media.vlcplayerpl.get_vlc')
|
||||
def test_play(mocked_get_vlc, mocked_threading, settings):
|
||||
"""
|
||||
Test the play() method
|
||||
"""
|
||||
# GIVEN: A bunch of mocked out things
|
||||
mocked_thread = MagicMock()
|
||||
mocked_threading.Thread.return_value = mocked_thread
|
||||
mocked_vlc = MagicMock()
|
||||
mocked_get_vlc.return_value = mocked_vlc
|
||||
mocked_display = MagicMock()
|
||||
mocked_controller = MagicMock()
|
||||
mocked_media = MagicMock()
|
||||
mocked_controller.vlc_media_player.get_media.return_value = mocked_media
|
||||
vlc_player = VlcPlayerPL(None)
|
||||
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:
|
||||
mocked_media_state_wait.return_value = True
|
||||
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()
|
||||
assert MediaState.Playing == vlc_player.get_live_state()
|
||||
assert result is True, 'The value returned from play() should be True'
|
||||
|
||||
|
||||
@patch('openlp.core.ui.media.vlcplayerpl.threading')
|
||||
@patch('openlp.core.ui.media.vlcplayerpl.get_vlc')
|
||||
def test_play_media_wait_state_not_playing(mocked_get_vlc, mocked_threading):
|
||||
"""
|
||||
Test the play() method when media_wait_state() returns False
|
||||
"""
|
||||
# GIVEN: A bunch of mocked out things
|
||||
mocked_thread = MagicMock()
|
||||
mocked_threading.Thread.return_value = mocked_thread
|
||||
mocked_vlc = MagicMock()
|
||||
mocked_get_vlc.return_value = mocked_vlc
|
||||
mocked_controller = MagicMock()
|
||||
mocked_controller.media_info.start_time = 0
|
||||
mocked_output_display = MagicMock()
|
||||
vlc_player = VlcPlayerPL(None)
|
||||
vlc_player.set_state(MediaState.Paused, mocked_output_display)
|
||||
|
||||
# WHEN: play() is called
|
||||
with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \
|
||||
patch.object(vlc_player, 'volume'):
|
||||
mocked_media_state_wait.return_value = False
|
||||
result = vlc_player.play(mocked_controller, mocked_output_display)
|
||||
|
||||
# THEN: A thread should be started, but the method should return False
|
||||
mocked_thread.start.assert_called_with()
|
||||
assert result is False
|
||||
|
||||
|
||||
@patch('openlp.core.ui.media.vlcplayerpl.get_vlc')
|
||||
def test_pause(mocked_get_vlc):
|
||||
"""
|
||||
Test that the pause method works correctly
|
||||
"""
|
||||
# GIVEN: A mocked out get_vlc method
|
||||
mocked_vlc = MagicMock()
|
||||
mocked_vlc.State.Playing = VlCState.Playing
|
||||
mocked_vlc.State.Paused = VlCState.Paused
|
||||
mocked_get_vlc.return_value = mocked_vlc
|
||||
mocked_display = MagicMock()
|
||||
mocked_display.vlc_media_listPlayer.get_state.return_value = VlCState.Playing
|
||||
vlc_player = VlcPlayerPL(None)
|
||||
|
||||
# WHEN: The media is paused
|
||||
with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait:
|
||||
mocked_media_state_wait.return_value = True
|
||||
vlc_player.pause(mocked_display)
|
||||
|
||||
# THEN: The pause method should exit early
|
||||
mocked_display.vlc_media_listPlayer.get_state.assert_called_with()
|
||||
mocked_display.vlc_media_listPlayer.pause.assert_called_with()
|
||||
mocked_media_state_wait.assert_called_with(mocked_display, VlCState.Paused)
|
||||
assert MediaState.Paused == vlc_player.get_live_state()
|
||||
|
||||
|
||||
@patch('openlp.core.ui.media.vlcplayerpl.get_vlc')
|
||||
def test_pause_not_playing(mocked_get_vlc):
|
||||
"""
|
||||
Test the pause method when the player is not playing
|
||||
"""
|
||||
# GIVEN: A mocked out get_vlc method
|
||||
mocked_vlc = MagicMock()
|
||||
mocked_vlc.State.Playing = 1
|
||||
mocked_get_vlc.return_value = mocked_vlc
|
||||
mocked_display = MagicMock()
|
||||
mocked_display.vlc_media.get_state.return_value = VlCState.Paused
|
||||
vlc_player = VlcPlayerPL(None)
|
||||
|
||||
# WHEN: The media is paused
|
||||
vlc_player.pause(mocked_display)
|
||||
|
||||
# THEN: The pause method should exit early
|
||||
mocked_display.vlc_media_listPlayer.get_state.assert_called_with()
|
||||
assert 0 == mocked_display.vlc_media_player.pause.call_count
|
||||
|
||||
|
||||
@patch('openlp.core.ui.media.vlcplayerpl.get_vlc')
|
||||
def test_pause_fail(mocked_get_vlc):
|
||||
"""
|
||||
Test the pause method when the player fails to pause the media
|
||||
"""
|
||||
# GIVEN: A mocked out get_vlc method
|
||||
mocked_vlc = MagicMock()
|
||||
mocked_vlc.State.Playing = 1
|
||||
mocked_vlc.State.Paused = 2
|
||||
mocked_get_vlc.return_value = mocked_vlc
|
||||
mocked_display = MagicMock()
|
||||
mocked_display.vlc_media_listPlayer.get_state.return_value = VlCState.Playing
|
||||
vlc_player = VlcPlayerPL(None)
|
||||
|
||||
# WHEN: The media is paused
|
||||
with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait:
|
||||
mocked_media_state_wait.return_value = False
|
||||
vlc_player.pause(mocked_display)
|
||||
|
||||
# THEN: The pause method should exit early
|
||||
mocked_display.vlc_media_listPlayer.get_state.assert_called_with()
|
||||
mocked_display.vlc_media_listPlayer.pause.assert_called_with()
|
||||
mocked_media_state_wait.assert_called_with(mocked_display, VlCState.Paused)
|
||||
assert MediaState.Paused is not vlc_player.state
|
||||
|
||||
|
||||
@patch('openlp.core.ui.media.vlcplayerpl.threading')
|
||||
def test_stop(mocked_threading):
|
||||
"""
|
||||
Test stopping the current item
|
||||
"""
|
||||
# GIVEN: A display object and a VlcPlayer instance and some mocked threads
|
||||
mocked_thread = MagicMock()
|
||||
mocked_threading.Thread.return_value = mocked_thread
|
||||
mocked_stop = MagicMock()
|
||||
mocked_display = MagicMock()
|
||||
mocked_display.vlc_media_listPlayer.stop = mocked_stop
|
||||
vlc_player = VlcPlayerPL(None)
|
||||
|
||||
# WHEN: stop is called
|
||||
vlc_player.stop(mocked_display)
|
||||
|
||||
# THEN: A thread should have been started to stop VLC
|
||||
mocked_threading.Thread.assert_called_with(target=mocked_stop)
|
||||
mocked_thread.start.assert_called_with()
|
||||
assert MediaState.Stopped == vlc_player.get_live_state()
|
||||
|
||||
|
||||
def test_volume():
|
||||
"""
|
||||
Test setting the volume
|
||||
"""
|
||||
# GIVEN: A display object and a VlcPlayer instance
|
||||
mocked_display = MagicMock()
|
||||
mocked_display.has_audio = True
|
||||
vlc_player = VlcPlayerPL(None)
|
||||
|
||||
# WHEN: The volume is set
|
||||
vlc_player.volume(mocked_display, 10)
|
||||
|
||||
# THEN: The volume should have been set
|
||||
mocked_display.vlc_media_player.audio_set_volume.assert_called_with(10)
|
||||
|
||||
|
||||
def test_seek_unseekable_media():
|
||||
"""
|
||||
Test seeking something that can't be seeked
|
||||
"""
|
||||
# GIVEN: Unseekable media
|
||||
mocked_display = MagicMock()
|
||||
mocked_display.controller.media_info.media_type = MediaType.Audio
|
||||
mocked_display.vlc_media_player.is_seekable.return_value = False
|
||||
vlc_player = VlcPlayerPL(None)
|
||||
|
||||
# WHEN: seek() is called
|
||||
vlc_player.seek(mocked_display, 100)
|
||||
|
||||
# THEN: nothing should happen
|
||||
mocked_display.vlc_media_player.is_seekable.assert_called_with()
|
||||
assert 0 == mocked_display.vlc_media_player.set_time.call_count
|
||||
|
||||
|
||||
def test_seek_seekable_media():
|
||||
"""
|
||||
Test seeking something that is seekable, but not a DVD
|
||||
"""
|
||||
# GIVEN: Unseekable media
|
||||
mocked_display = MagicMock()
|
||||
mocked_display.controller.media_info.media_type = MediaType.Audio
|
||||
mocked_display.vlc_media_player.is_seekable.return_value = True
|
||||
vlc_player = VlcPlayerPL(None)
|
||||
|
||||
# WHEN: seek() is called
|
||||
vlc_player.seek(mocked_display, 100)
|
||||
|
||||
# THEN: nothing should happen
|
||||
mocked_display.vlc_media_player.is_seekable.assert_called_with()
|
||||
mocked_display.vlc_media_player.set_time.assert_called_with(100)
|
||||
|
||||
|
||||
def test_reset():
|
||||
"""
|
||||
Test the reset() method
|
||||
"""
|
||||
# GIVEN: Some mocked out stuff
|
||||
mocked_display = MagicMock()
|
||||
vlc_player = VlcPlayerPL(None)
|
||||
|
||||
# WHEN: reset() is called
|
||||
vlc_player.reset(mocked_display)
|
||||
|
||||
# THEN: The media should be stopped and invisible
|
||||
mocked_display.vlc_media_player.stop.assert_called_with()
|
||||
mocked_display.vlc_widget.setVisible.assert_not_called()
|
||||
assert MediaState.Off == vlc_player.get_live_state()
|
||||
|
||||
|
||||
def test_set_visible_has_own_widget():
|
||||
"""
|
||||
Test the set_visible() method when the player has its own widget
|
||||
"""
|
||||
# GIVEN: Some mocked out stuff
|
||||
mocked_display = MagicMock()
|
||||
vlc_player = VlcPlayerPL(None)
|
||||
vlc_player.has_own_widget = True
|
||||
|
||||
# WHEN: reset() is called
|
||||
vlc_player.set_visible(mocked_display, True)
|
||||
|
||||
# THEN: The media should be stopped and invsibile
|
||||
mocked_display.vlc_widget.setVisible.assert_called_with(True)
|
||||
|
||||
|
||||
@patch('openlp.core.ui.media.vlcplayerpl.get_vlc')
|
||||
def test_update_ui(mocked_get_vlc):
|
||||
"""
|
||||
Test updating the UI
|
||||
"""
|
||||
# GIVEN: A whole bunch of mocks
|
||||
mocked_vlc = MagicMock()
|
||||
mocked_vlc.State.Ended = 1
|
||||
mocked_get_vlc.return_value = mocked_vlc
|
||||
mocked_controller = MagicMock()
|
||||
mocked_controller.media_info.end_time = 300
|
||||
mocked_controller.mediabar.seek_slider.isSliderDown.return_value = False
|
||||
mocked_display = MagicMock()
|
||||
mocked_controller.vlc_media.get_state.return_value = 1
|
||||
mocked_controller.vlc_media_player.get_time.return_value = 400000
|
||||
vlc_player = VlcPlayerPL(None)
|
||||
|
||||
# WHEN: update_ui() is called
|
||||
vlc_player.update_ui(mocked_controller, mocked_display)
|
||||
|
||||
# THEN: Certain methods should be called
|
||||
mocked_controller.vlc_media_player.get_time.assert_called_with()
|
||||
mocked_controller.mediabar.seek_slider.setSliderPosition.assert_called_with(400000)
|
||||
expected_calls = [call(True), call(False)]
|
||||
assert expected_calls == mocked_controller.mediabar.seek_slider.blockSignals.call_args_list
|
Loading…
Reference in New Issue