VLC plays and handles missing live display gracefully. Preview still works.

bzr-revno: 2857
This commit is contained in:
Tim Bentley 2019-04-12 17:37:45 +01:00
commit 8389ed4a8f
22 changed files with 560 additions and 1663 deletions

View File

@ -15,6 +15,7 @@ documentation/build/html
*.e4* *.e4*
*eric[1-9]project *eric[1-9]project
.git .git
env
# Git files # Git files
.gitignore .gitignore
htmlcov htmlcov

View File

@ -129,6 +129,9 @@ class Settings(QtCore.QSettings):
``advanced/slide limits`` to ``SlideLimits.Wrap``. **NOTE**, this means that the rules have to cover all cases! ``advanced/slide limits`` to ``SlideLimits.Wrap``. **NOTE**, this means that the rules have to cover all cases!
So, if the type of the old value is bool, then there must be two rules. So, if the type of the old value is bool, then there must be two rules.
""" """
on_monitor_default = True
if log.isEnabledFor(logging.DEBUG):
on_monitor_default = False
__default_settings__ = { __default_settings__ = {
'settings/version': 0, 'settings/version': 0,
'advanced/add page break': False, 'advanced/add page break': False,
@ -185,6 +188,7 @@ class Settings(QtCore.QSettings):
'core/click live slide to unblank': False, 'core/click live slide to unblank': False,
'core/blank warning': False, 'core/blank warning': False,
'core/ccli number': '', 'core/ccli number': '',
'core/experimental': False,
'core/has run wizard': False, 'core/has run wizard': False,
'core/language': '[en]', 'core/language': '[en]',
'core/last version test': '', 'core/last version test': '',
@ -202,13 +206,13 @@ class Settings(QtCore.QSettings):
'core/view mode': 'default', 'core/view mode': 'default',
# The other display settings (display position and dimensions) are defined in the ScreenList class due to a # The other display settings (display position and dimensions) are defined in the ScreenList class due to a
# circular dependency. # circular dependency.
'core/display on monitor': True, 'core/display on monitor': on_monitor_default,
'core/override position': False, 'core/override position': False,
'core/monitor': {}, 'core/monitor': {},
'core/application version': '0.0', 'core/application version': '0.0',
'images/background color': '#000000', 'images/background color': '#000000',
'media/players': 'system,webkit', 'media/media auto start': QtCore.Qt.Unchecked,
'media/override player': QtCore.Qt.Unchecked, 'media/stream command': '',
'remotes/download version': '0.0', 'remotes/download version': '0.0',
'players/background color': '#000000', 'players/background color': '#000000',
'servicemanager/last directory': None, 'servicemanager/last directory': None,
@ -311,7 +315,9 @@ class Settings(QtCore.QSettings):
('bibles/proxy name', '', []), # Just remove these bible proxy settings. They weren't used in 2.4! ('bibles/proxy name', '', []), # Just remove these bible proxy settings. They weren't used in 2.4!
('bibles/proxy address', '', []), ('bibles/proxy address', '', []),
('bibles/proxy username', '', []), ('bibles/proxy username', '', []),
('bibles/proxy password', '', []) ('bibles/proxy password', '', []),
('media/players', '', []),
('media/override player', '', [])
] ]
@staticmethod @staticmethod

View File

@ -46,6 +46,7 @@ class BackgroundType(object):
Image = 2 Image = 2
Transparent = 3 Transparent = 3
Video = 4 Video = 4
Stream = 5
@staticmethod @staticmethod
def to_string(background_type): def to_string(background_type):
@ -62,6 +63,8 @@ class BackgroundType(object):
return 'transparent' return 'transparent'
elif background_type == BackgroundType.Video: elif background_type == BackgroundType.Video:
return 'video' return 'video'
elif background_type == BackgroundType.Stream:
return 'stream'
@staticmethod @staticmethod
def from_string(type_string): def from_string(type_string):
@ -78,6 +81,8 @@ class BackgroundType(object):
return BackgroundType.Transparent return BackgroundType.Transparent
elif type_string == 'video': elif type_string == 'video':
return BackgroundType.Video return BackgroundType.Video
elif type_string == 'stream':
return BackgroundType.Stream
class BackgroundGradientType(object): class BackgroundGradientType(object):

View File

@ -48,6 +48,7 @@ class MediaType(object):
CD = 3 CD = 3
DVD = 4 DVD = 4
Folder = 5 Folder = 5
Stream = 6
class ItemMediaInfo(object): class ItemMediaInfo(object):

View File

@ -23,7 +23,6 @@
The :mod:`~openlp.core.ui.media.mediacontroller` module contains a base class for media components and other widgets The :mod:`~openlp.core.ui.media.mediacontroller` module contains a base class for media components and other widgets
related to playing media, such as sliders. related to playing media, such as sliders.
""" """
import datetime
import logging import logging
try: try:
@ -33,7 +32,7 @@ except ImportError:
pymediainfo_available = False pymediainfo_available = False
from subprocess import check_output from subprocess import check_output
from PyQt5 import QtCore, QtWidgets from PyQt5 import QtCore
from openlp.core.state import State from openlp.core.state import State
from openlp.core.api.http import register_endpoint from openlp.core.api.http import register_endpoint
@ -44,11 +43,9 @@ from openlp.core.common.settings import Settings
from openlp.core.lib.serviceitem import ItemCapabilities from openlp.core.lib.serviceitem import ItemCapabilities
from openlp.core.lib.ui import critical_error_message_box from openlp.core.lib.ui import critical_error_message_box
from openlp.core.ui import DisplayControllerType from openlp.core.ui import DisplayControllerType
from openlp.core.ui.icons import UiIcons
from openlp.core.ui.media import MediaState, ItemMediaInfo, MediaType, parse_optical_path from openlp.core.ui.media import MediaState, ItemMediaInfo, MediaType, parse_optical_path
from openlp.core.ui.media.endpoint import media_endpoint from openlp.core.ui.media.endpoint import media_endpoint
from openlp.core.ui.media.vlcplayer import VlcPlayer, get_vlc from openlp.core.ui.media.vlcplayer import VlcPlayer, get_vlc
from openlp.core.widgets.toolbar import OpenLPToolbar
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -56,45 +53,6 @@ log = logging.getLogger(__name__)
TICK_TIME = 200 TICK_TIME = 200
class MediaSlider(QtWidgets.QSlider):
"""
Allows the mouse events of a slider to be overridden and extra functionality added
"""
def __init__(self, direction, manager, controller):
"""
Constructor
"""
super(MediaSlider, self).__init__(direction)
self.manager = manager
self.controller = controller
def mouseMoveEvent(self, event):
"""
Override event to allow hover time to be displayed.
:param event: The triggering event
"""
time_value = QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width())
self.setToolTip('%s' % datetime.timedelta(seconds=int(time_value / 1000)))
QtWidgets.QSlider.mouseMoveEvent(self, event)
def mousePressEvent(self, event):
"""
Mouse Press event no new functionality
:param event: The triggering event
"""
QtWidgets.QSlider.mousePressEvent(self, event)
def mouseReleaseEvent(self, event):
"""
Set the slider position when the mouse is clicked and released on the slider.
:param event: The triggering event
"""
self.setValue(QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width()))
QtWidgets.QSlider.mouseReleaseEvent(self, event)
class MediaController(RegistryBase, LogMixin, RegistryProperties): class MediaController(RegistryBase, LogMixin, RegistryProperties):
""" """
The implementation of the Media Controller. The Media Controller adds an own class for every Player. The implementation of the Media Controller. The Media Controller adds an own class for every Player.
@ -116,7 +74,6 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
def setup(self): def setup(self):
self.vlc_player = None self.vlc_player = None
self.display_controllers = {}
self.current_media_players = {} self.current_media_players = {}
# Timer for video state # Timer for video state
self.live_timer = QtCore.QTimer() self.live_timer = QtCore.QTimer()
@ -168,117 +125,71 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
self.setup() self.setup()
self.vlc_player = VlcPlayer(self) self.vlc_player = VlcPlayer(self)
State().add_service("mediacontroller", 0) State().add_service("mediacontroller", 0)
State().add_service("media_live", 0, requires="mediacontroller")
if get_vlc() and pymediainfo_available: if get_vlc() and pymediainfo_available:
State().update_pre_conditions("mediacontroller", True) State().update_pre_conditions("mediacontroller", True)
State().update_pre_conditions('media_live', True)
else: else:
State().missing_text("mediacontroller", translate('OpenLP.SlideController', State().missing_text("mediacontroller", translate('OpenLP.SlideController',
"VLC or pymediainfo are missing, so you are unable to play any media")) "VLC or pymediainfo are missing, so you are unable to play any media"))
self._generate_extensions_lists() self._generate_extensions_lists()
return True return True
def bootstrap_post_set_up(self):
"""
Set up the controllers.
:return:
"""
try:
self.setup_display(self.live_controller.display, False)
except AttributeError:
State().update_pre_conditions('media_live', False)
self.setup_display(self.preview_controller.preview_display, True)
def display_controllers(self, controller_type):
"""
Decides which controller to use.
:param controller_type: The controller type where a player will be placed
"""
if controller_type == DisplayControllerType.Live:
return self.live_controller
else:
return self.preview_controller
def media_state_live(self): def media_state_live(self):
""" """
Check if there is a running Live media Player and do updating stuff (e.g. update the UI) Check if there is a running Live media Player and do updating stuff (e.g. update the UI)
""" """
display = self._define_display(self.display_controllers[DisplayControllerType.Live]) display = self._define_display(self.display_controllers(DisplayControllerType.Live))
if DisplayControllerType.Live in self.current_media_players: if DisplayControllerType.Live in self.current_media_players:
self.current_media_players[DisplayControllerType.Live].resize(display) self.current_media_players[DisplayControllerType.Live].resize(display)
self.current_media_players[DisplayControllerType.Live].update_ui(display) self.current_media_players[DisplayControllerType.Live].update_ui(self.live_controller, display)
self.tick(self.display_controllers[DisplayControllerType.Live]) self.tick(self.display_controllers(DisplayControllerType.Live))
if self.current_media_players[DisplayControllerType.Live].get_live_state() is not MediaState.Playing: if self.current_media_players[DisplayControllerType.Live].get_live_state() is not MediaState.Playing:
self.live_timer.stop() self.live_timer.stop()
else: else:
self.live_timer.stop() self.live_timer.stop()
self.media_stop(self.display_controllers[DisplayControllerType.Live]) self.media_stop(self.display_controllers(DisplayControllerType.Live))
if self.display_controllers[DisplayControllerType.Live].media_info.can_loop_playback: if self.display_controllers(DisplayControllerType.Live).media_info.can_loop_playback:
self.media_play(self.display_controllers[DisplayControllerType.Live], True) self.media_play(self.display_controllers(DisplayControllerType.Live), True)
def media_state_preview(self): def media_state_preview(self):
""" """
Check if there is a running Preview media Player and do updating stuff (e.g. update the UI) Check if there is a running Preview media Player and do updating stuff (e.g. update the UI)
""" """
display = self._define_display(self.display_controllers[DisplayControllerType.Preview]) display = self._define_display(self.display_controllers(DisplayControllerType.Preview))
if DisplayControllerType.Preview in self.current_media_players: if DisplayControllerType.Preview in self.current_media_players:
self.current_media_players[DisplayControllerType.Preview].resize(display) self.current_media_players[DisplayControllerType.Preview].resize(display)
self.current_media_players[DisplayControllerType.Preview].update_ui(display) self.current_media_players[DisplayControllerType.Preview].update_ui(self.preview_controller, display)
self.tick(self.display_controllers[DisplayControllerType.Preview]) self.tick(self.display_controllers(DisplayControllerType.Preview))
if self.current_media_players[DisplayControllerType.Preview].get_preview_state() is not MediaState.Playing: if self.current_media_players[DisplayControllerType.Preview].get_preview_state() is not MediaState.Playing:
self.preview_timer.stop() self.preview_timer.stop()
else: else:
self.preview_timer.stop() self.preview_timer.stop()
self.media_stop(self.display_controllers[DisplayControllerType.Preview]) self.media_stop(self.display_controllers(DisplayControllerType.Preview))
if self.display_controllers[DisplayControllerType.Preview].media_info.can_loop_playback: if self.display_controllers(DisplayControllerType.Preview).media_info.can_loop_playback:
self.media_play(self.display_controllers[DisplayControllerType.Preview], True) self.media_play(self.display_controllers(DisplayControllerType.Preview), True)
def register_controller(self, controller):
"""
Registers media controls where the players will be placed to run.
:param controller: The controller where a player will be placed
"""
self.display_controllers[controller.controller_type] = controller
self.setup_generic_controls(controller)
def setup_generic_controls(self, controller):
"""
Set up controls on the control_panel for a given controller
:param controller: First element is the controller which should be used
"""
controller.media_info = ItemMediaInfo()
# Build a Media ToolBar
controller.mediabar = OpenLPToolbar(controller)
controller.mediabar.add_toolbar_action('playbackPlay', text='media_playback_play',
icon=UiIcons().play,
tooltip=translate('OpenLP.SlideController', 'Start playing media.'),
triggers=controller.send_to_plugins)
controller.mediabar.add_toolbar_action('playbackPause', text='media_playback_pause',
icon=UiIcons().pause,
tooltip=translate('OpenLP.SlideController', 'Pause playing media.'),
triggers=controller.send_to_plugins)
controller.mediabar.add_toolbar_action('playbackStop', text='media_playback_stop',
icon=UiIcons().stop,
tooltip=translate('OpenLP.SlideController', 'Stop playing media.'),
triggers=controller.send_to_plugins)
controller.mediabar.add_toolbar_action('playbackLoop', text='media_playback_loop',
icon=UiIcons().repeat, checked=False,
tooltip=translate('OpenLP.SlideController', 'Loop playing media.'),
triggers=controller.send_to_plugins)
controller.position_label = QtWidgets.QLabel()
controller.position_label.setText(' 00:00 / 00:00')
controller.position_label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
controller.position_label.setToolTip(translate('OpenLP.SlideController', 'Video timer.'))
controller.position_label.setMinimumSize(90, 0)
controller.position_label.setObjectName('position_label')
controller.mediabar.add_toolbar_widget(controller.position_label)
# Build the seek_slider.
controller.seek_slider = MediaSlider(QtCore.Qt.Horizontal, self, controller)
controller.seek_slider.setMaximum(1000)
controller.seek_slider.setTracking(True)
controller.seek_slider.setMouseTracking(True)
controller.seek_slider.setToolTip(translate('OpenLP.SlideController', 'Video position.'))
controller.seek_slider.setGeometry(QtCore.QRect(90, 260, 221, 24))
controller.seek_slider.setObjectName('seek_slider')
controller.mediabar.add_toolbar_widget(controller.seek_slider)
# Build the volume_slider.
controller.volume_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
controller.volume_slider.setTickInterval(10)
controller.volume_slider.setTickPosition(QtWidgets.QSlider.TicksAbove)
controller.volume_slider.setMinimum(0)
controller.volume_slider.setMaximum(100)
controller.volume_slider.setTracking(True)
controller.volume_slider.setToolTip(translate('OpenLP.SlideController', 'Audio Volume.'))
controller.volume_slider.setValue(controller.media_info.volume)
controller.volume_slider.setGeometry(QtCore.QRect(90, 160, 221, 24))
controller.volume_slider.setObjectName('volume_slider')
controller.mediabar.add_toolbar_widget(controller.volume_slider)
controller.controller_layout.addWidget(controller.mediabar)
controller.mediabar.setVisible(False)
if not controller.is_live:
controller.volume_slider.setEnabled(False)
# Signals
controller.seek_slider.valueChanged.connect(controller.send_to_plugins)
controller.volume_slider.valueChanged.connect(controller.send_to_plugins)
def setup_display(self, display, preview): def setup_display(self, display, preview):
""" """
@ -287,14 +198,13 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
:param display: Display on which the output is to be played :param display: Display on which the output is to be played
:param preview: Whether the display is a main or preview display :param preview: Whether the display is a main or preview display
""" """
# clean up possible running old media files display.media_info = ItemMediaInfo()
self.finalise()
display.has_audio = True display.has_audio = True
if display.is_live and preview: # if display.is_live and preview:
return # return
if preview: if preview:
display.has_audio = False display.has_audio = False
self.vlc_player.setup(display) self.vlc_player.setup(display, preview)
def set_controls_visible(self, controller, value): def set_controls_visible(self, controller, value):
""" """
@ -305,9 +215,9 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
""" """
# Generic controls # Generic controls
controller.mediabar.setVisible(value) controller.mediabar.setVisible(value)
if controller.is_live and controller.display: # if controller.is_live and controller.display:
if self.current_media_players and value: # if self.current_media_players and value:
controller.display.set_transparency(False) # controller.display.set_transparency(False)
@staticmethod @staticmethod
def resize(display, player): def resize(display, player):
@ -319,7 +229,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
""" """
player.resize(display) player.resize(display)
def video(self, source, service_item, hidden=False, video_behind_text=False): def load_video(self, source, service_item, hidden=False, video_behind_text=False):
""" """
Loads and starts a video to run with the option of sound Loads and starts a video to run with the option of sound
@ -329,7 +239,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
:param video_behind_text: Is the video to be played behind text. :param video_behind_text: Is the video to be played behind text.
""" """
is_valid = True is_valid = True
controller = self.display_controllers[source] controller = self.display_controllers(source)
# stop running videos # stop running videos
self.media_reset(controller) self.media_reset(controller)
controller.media_info = ItemMediaInfo() controller.media_info = ItemMediaInfo()
@ -354,8 +264,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
log.debug('video is not optical and live') log.debug('video is not optical and live')
controller.media_info.length = service_item.media_length controller.media_info.length = service_item.media_length
is_valid = self._check_file_type(controller, display) is_valid = self._check_file_type(controller, display)
display.override['theme'] = '' # display.override['theme'] = ''
display.override['video'] = True # display.override['video'] = True
if controller.media_info.is_background: if controller.media_info.is_background:
# ignore start/end time # ignore start/end time
controller.media_info.start_time = 0 controller.media_info.start_time = 0
@ -379,10 +289,10 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'), critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'),
translate('MediaPlugin.MediaItem', 'Unsupported File')) translate('MediaPlugin.MediaItem', 'Unsupported File'))
return False return False
log.debug('video mediatype: ' + str(controller.media_info.media_type)) log.debug('video media type: ' + str(controller.media_info.media_type))
# dont care about actual theme, set a black background # dont care about actual theme, set a black background
if controller.is_live and not controller.media_info.is_background: # if controller.is_live and not controller.media_info.is_background:
display.frame.runJavaScript('show_video("setBackBoard", null, null,"visible");') # display.frame.runJavaScript('show_video("setBackBoard", null, null,"visible");')
# now start playing - Preview is autoplay! # now start playing - Preview is autoplay!
autoplay = False autoplay = False
# Preview requested # Preview requested
@ -471,28 +381,26 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
for file in controller.media_info.file_info: for file in controller.media_info.file_info:
if file.is_file: if file.is_file:
suffix = '*%s' % file.suffix.lower() suffix = '*%s' % file.suffix.lower()
player = self.vlc_player
file = str(file) file = str(file)
if suffix in player.video_extensions_list: if suffix in self.vlc_player.video_extensions_list:
if not controller.media_info.is_background or controller.media_info.is_background and \ if not controller.media_info.is_background or controller.media_info.is_background and \
player.can_background: self.vlc_player.can_background:
self.resize(display, player) self.resize(display, self.vlc_player)
if player.load(display, file): if self.vlc_player.load(display, file):
self.current_media_players[controller.controller_type] = player self.current_media_players[controller.controller_type] = self.vlc_player
controller.media_info.media_type = MediaType.Video controller.media_info.media_type = MediaType.Video
return True return True
if suffix in player.audio_extensions_list: if suffix in self.vlc_player.audio_extensions_list:
if player.load(display, file): if self.vlc_player.load(display, file):
self.current_media_players[controller.controller_type] = player self.current_media_players[controller.controller_type] = self.vlc_player
controller.media_info.media_type = MediaType.Audio controller.media_info.media_type = MediaType.Audio
return True return True
else: else:
player = self.vlc_player
file = str(file) file = str(file)
if player.can_folder: if self.vlc_player.can_folder:
self.resize(display, player) self.resize(display, self.vlc_player)
if player.load(display, file): if self.vlc_player.load(display, file):
self.current_media_players[controller.controller_type] = player self.current_media_players[controller.controller_type] = self.vlc_player
controller.media_info.media_type = MediaType.Video controller.media_info.media_type = MediaType.Video
return True return True
return False return False
@ -509,8 +417,6 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
def on_media_play(self): def on_media_play(self):
""" """
Responds to the request to play a loaded video from the web. Responds to the request to play a loaded video from the web.
:param msg: First element is the controller which should be used
""" """
self.media_play(Registry().get('live_controller'), False) self.media_play(Registry().get('live_controller'), False)
@ -524,7 +430,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
controller.seek_slider.blockSignals(True) controller.seek_slider.blockSignals(True)
controller.volume_slider.blockSignals(True) controller.volume_slider.blockSignals(True)
display = self._define_display(controller) display = self._define_display(controller)
if not self.current_media_players[controller.controller_type].play(display): if not self.current_media_players[controller.controller_type].play(controller, display):
controller.seek_slider.blockSignals(False) controller.seek_slider.blockSignals(False)
controller.volume_slider.blockSignals(False) controller.volume_slider.blockSignals(False)
return False return False
@ -533,8 +439,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
else: else:
self.media_volume(controller, controller.media_info.volume) self.media_volume(controller, controller.media_info.volume)
if first_time: if first_time:
if not controller.media_info.is_background: # if not controller.media_info.is_background:
display.frame.runJavaScript('show_blank("desktop");') # display.frame.runJavaScript('show_blank("desktop");')
self.current_media_players[controller.controller_type].set_visible(display, True) self.current_media_players[controller.controller_type].set_visible(display, True)
controller.mediabar.actions['playbackPlay'].setVisible(False) controller.mediabar.actions['playbackPlay'].setVisible(False)
controller.mediabar.actions['playbackPause'].setVisible(True) controller.mediabar.actions['playbackPause'].setVisible(True)
@ -591,8 +497,6 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
def on_media_pause(self): def on_media_pause(self):
""" """
Responds to the request to pause a loaded video from the web. Responds to the request to pause a loaded video from the web.
:param msg: First element is the controller which should be used
""" """
self.media_pause(Registry().get('live_controller')) self.media_pause(Registry().get('live_controller'))
@ -639,8 +543,6 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
def on_media_stop(self): def on_media_stop(self):
""" """
Responds to the request to stop a loaded video from the web. Responds to the request to stop a loaded video from the web.
:param msg: First element is the controller which should be used
""" """
self.media_stop(Registry().get('live_controller')) self.media_stop(Registry().get('live_controller'))
@ -653,8 +555,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
""" """
display = self._define_display(controller) display = self._define_display(controller)
if controller.controller_type in self.current_media_players: if controller.controller_type in self.current_media_players:
if not looping_background: # if not looping_background:
display.frame.runJavaScript('show_blank("black");') # display.frame.runJavaScript('show_blank("black");')
self.current_media_players[controller.controller_type].stop(display) self.current_media_players[controller.controller_type].stop(display)
self.current_media_players[controller.controller_type].set_visible(display, False) self.current_media_players[controller.controller_type].set_visible(display, False)
controller.seek_slider.setSliderPosition(0) controller.seek_slider.setSliderPosition(0)
@ -725,7 +627,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
display.override = {} display.override = {}
self.current_media_players[controller.controller_type].reset(display) self.current_media_players[controller.controller_type].reset(display)
self.current_media_players[controller.controller_type].set_visible(display, False) self.current_media_players[controller.controller_type].set_visible(display, False)
display.frame.runJavaScript('show_video("setBackBoard", null, null, "hidden");') # display.frame.runJavaScript('show_video("setBackBoard", null, null, "hidden");')
del self.current_media_players[controller.controller_type] del self.current_media_players[controller.controller_type]
def media_hide(self, msg): def media_hide(self, msg):
@ -788,8 +690,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
""" """
self.live_timer.stop() self.live_timer.stop()
self.preview_timer.stop() self.preview_timer.stop()
for controller in self.display_controllers: self.media_reset(self.display_controllers(DisplayControllerType.Live))
self.media_reset(self.display_controllers[controller]) self.media_reset(self.display_controllers(DisplayControllerType.Preview))
@staticmethod @staticmethod
def _define_display(controller): def _define_display(controller):

View File

@ -52,11 +52,12 @@ class MediaPlayer(RegistryProperties):
""" """
return False return False
def setup(self, display): def setup(self, display, live_display):
""" """
Create the related widgets for the current display Create the related widgets for the current display
:param display: The display to be updated. :param display: The display to be updated.
:param live_display: Is the display a live one.
""" """
pass pass
@ -78,10 +79,11 @@ class MediaPlayer(RegistryProperties):
""" """
pass pass
def play(self, display): def play(self, controller, display):
""" """
Starts playing of current Media File Starts playing of current Media File
:param controller: Which Controller is running the show.
:param display: The display to be updated. :param display: The display to be updated.
""" """
pass pass
@ -206,7 +208,7 @@ class MediaPlayer(RegistryProperties):
:param display: Identify the Display type :param display: Identify the Display type
:return: None :return: None
""" """
if display.controller.is_live: if display.is_display:
self.set_live_state(state) self.set_live_state(state)
else: else:
self.set_preview_state(state) self.set_preview_state(state)

View File

@ -0,0 +1,148 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2019 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; version 2 of the License. #
# #
# 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, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`~openlp.core.ui.media.playertab` module holds the configuration tab for the media stuff.
"""
from PyQt5 import QtWidgets
# from PyQt5.QtMultimedia import QCameraInfo, QAudioDeviceInfo, QAudio
from openlp.core.common import is_linux, is_win
from openlp.core.common.i18n import translate
from openlp.core.common.settings import Settings
from openlp.core.lib.settingstab import SettingsTab
from openlp.core.ui.icons import UiIcons
LINUX_STREAM = 'v4l2:///dev/video0'
WIN_STREAM = 'dshow:// :dshow-vdev='
class MediaTab(SettingsTab):
"""
MediaTab is the Media settings tab in the settings dialog.
"""
def __init__(self, parent):
"""
Constructor
"""
# self.media_players = Registry().get('media_controller').media_players
# self.saved_used_players = None
self.icon_path = UiIcons().video
player_translated = translate('OpenLP.MediaTab', 'Media')
super(MediaTab, self).__init__(parent, 'Media', player_translated)
def setup_ui(self):
"""
Set up the UI
"""
self.setObjectName('MediaTab')
super(MediaTab, self).setup_ui()
self.live_media_group_box = QtWidgets.QGroupBox(self.left_column)
self.live_media_group_box.setObjectName('live_media_group_box')
self.media_layout = QtWidgets.QVBoxLayout(self.live_media_group_box)
self.media_layout.setObjectName('live_media_layout')
self.auto_start_check_box = QtWidgets.QCheckBox(self.live_media_group_box)
self.auto_start_check_box.setObjectName('auto_start_check_box')
self.media_layout.addWidget(self.auto_start_check_box)
self.left_layout.addWidget(self.live_media_group_box)
self.stream_media_group_box = QtWidgets.QGroupBox(self.left_column)
self.stream_media_group_box.setObjectName('stream_media_group_box')
self.stream_media_layout = QtWidgets.QHBoxLayout(self.stream_media_group_box)
self.stream_media_layout.setObjectName('live_media_layout')
self.stream_media_layout.setContentsMargins(0, 0, 0, 0)
self.stream_edit = QtWidgets.QPlainTextEdit(self)
self.stream_media_layout.addWidget(self.stream_edit)
self.browse_button = QtWidgets.QToolButton(self)
self.browse_button.setIcon(UiIcons().undo)
self.stream_media_layout.addWidget(self.browse_button)
self.left_layout.addWidget(self.stream_media_group_box)
self.left_layout.addWidget(self.stream_media_group_box)
self.left_layout.addStretch()
self.right_layout.addStretch()
# # Signals and slots
self.browse_button.clicked.connect(self.on_revert)
def retranslateUi(self):
"""
Translate the UI on the fly
"""
self.live_media_group_box.setTitle(translate('MediaPlugin.MediaTab', 'Live Media'))
self.stream_media_group_box.setTitle(translate('MediaPlugin.MediaTab', 'Stream Media Command'))
self.auto_start_check_box.setText(translate('MediaPlugin.MediaTab', 'Start automatically'))
def load(self):
"""
Load the settings
"""
self.auto_start_check_box.setChecked(Settings().value(self.settings_section + '/media auto start'))
self.stream_edit.setPlainText(Settings().value(self.settings_section + '/stream command'))
if not self.stream_edit.toPlainText():
if is_linux:
self.stream_edit.setPlainText(LINUX_STREAM)
elif is_win:
self.stream_edit.setPlainText(WIN_STREAM)
def save(self):
"""
Save the settings
"""
setting_key = self.settings_section + '/media auto start'
if Settings().value(setting_key) != self.auto_start_check_box.checkState():
Settings().setValue(setting_key, self.auto_start_check_box.checkState())
# settings = Settings()
# settings.beginGroup(self.settings_section)
# settings.setValue('background color', self.background_color)
# settings.endGroup()
# old_players, override_player = get_media_players()
# if self.used_players != old_players:
# # clean old Media stuff
# set_media_players(self.used_players, override_player)
# self.settings_form.register_post_process('mediaitem_suffix_reset')
# self.settings_form.register_post_process('mediaitem_media_rebuild')
# self.settings_form.register_post_process('config_screen_changed')
def post_set_up(self, post_update=False):
"""
Late setup for players as the MediaController has to be initialised first.
:param post_update: Indicates if called before or after updates.
"""
pass
# for key, player in self.media_players.items():
# player = self.media_players[key]
# checkbox = MediaQCheckBox(self.media_player_group_box)
# checkbox.setEnabled(player.available)
# checkbox.setObjectName(player.name + '_check_box')
# checkbox.setToolTip(player.get_info())
# checkbox.set_player_name(player.name)
# self.player_check_boxes[player.name] = checkbox
# checkbox.stateChanged.connect(self.on_player_check_box_changed)
# self.media_player_layout.addWidget(checkbox)
# if player.available and player.name in self.used_players:
# checkbox.setChecked(True)
# else:
# checkbox.setChecked(False)
# self.update_player_list()
# self.retranslate_players()
def on_revert(self):
pass

View File

@ -1,269 +0,0 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2019 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; version 2 of the License. #
# #
# 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, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`~openlp.core.ui.media.playertab` module holds the configuration tab for the media stuff.
"""
import platform
from PyQt5 import QtCore, QtWidgets
from openlp.core.common.i18n import UiStrings, translate
# from openlp.core.common.registry import Registry
from openlp.core.common.settings import Settings
from openlp.core.lib.settingstab import SettingsTab
from openlp.core.lib.ui import create_button
from openlp.core.ui.icons import UiIcons
from openlp.core.widgets.buttons import ColorButton
class MediaQCheckBox(QtWidgets.QCheckBox):
"""
MediaQCheckBox adds an extra property, player_name to the QCheckBox class.
"""
def set_player_name(self, name):
"""
Set the player name
"""
self.player_name = name
class PlayerTab(SettingsTab):
"""
MediaTab is the Media settings tab in the settings dialog.
"""
def __init__(self, parent):
"""
Constructor
"""
# self.media_players = Registry().get('media_controller').media_players
self.saved_used_players = None
self.icon_path = UiIcons().player
player_translated = translate('OpenLP.PlayerTab', 'Players')
super(PlayerTab, self).__init__(parent, 'Players', player_translated)
def setup_ui(self):
"""
Set up the UI
"""
self.setObjectName('MediaTab')
super(PlayerTab, self).setup_ui()
self.background_color_group_box = QtWidgets.QGroupBox(self.left_column)
self.background_color_group_box.setObjectName('background_color_group_box')
self.form_layout = QtWidgets.QFormLayout(self.background_color_group_box)
self.form_layout.setObjectName('form_layout')
self.color_layout = QtWidgets.QHBoxLayout()
self.background_color_label = QtWidgets.QLabel(self.background_color_group_box)
self.background_color_label.setObjectName('background_color_label')
self.color_layout.addWidget(self.background_color_label)
self.background_color_button = ColorButton(self.background_color_group_box)
self.background_color_button.setObjectName('background_color_button')
self.color_layout.addWidget(self.background_color_button)
self.form_layout.addRow(self.color_layout)
self.information_label = QtWidgets.QLabel(self.background_color_group_box)
self.information_label.setObjectName('information_label')
self.information_label.setWordWrap(True)
self.form_layout.addRow(self.information_label)
self.left_layout.addWidget(self.background_color_group_box)
self.right_column.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
self.media_player_group_box = QtWidgets.QGroupBox(self.left_column)
self.media_player_group_box.setObjectName('media_player_group_box')
self.media_player_layout = QtWidgets.QVBoxLayout(self.media_player_group_box)
self.media_player_layout.setObjectName('media_player_layout')
self.player_check_boxes = {}
self.left_layout.addWidget(self.media_player_group_box)
self.player_order_group_box = QtWidgets.QGroupBox(self.left_column)
self.player_order_group_box.setObjectName('player_order_group_box')
self.player_order_layout = QtWidgets.QHBoxLayout(self.player_order_group_box)
self.player_order_layout.setObjectName('player_order_layout')
self.player_order_list_widget = QtWidgets.QListWidget(self.player_order_group_box)
size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
size_policy.setHorizontalStretch(0)
size_policy.setVerticalStretch(0)
size_policy.setHeightForWidth(self.player_order_list_widget.sizePolicy().hasHeightForWidth())
self.player_order_list_widget.setSizePolicy(size_policy)
self.player_order_list_widget.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
self.player_order_list_widget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.player_order_list_widget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.player_order_list_widget.setObjectName('player_order_list_widget')
self.player_order_layout.addWidget(self.player_order_list_widget)
self.ordering_button_layout = QtWidgets.QVBoxLayout()
self.ordering_button_layout.setObjectName('ordering_button_layout')
self.ordering_button_layout.addStretch(1)
self.ordering_up_button = create_button(self, 'ordering_up_button', role='up',
click=self.on_up_button_clicked)
self.ordering_down_button = create_button(self, 'ordering_down_button', role='down',
click=self.on_down_button_clicked)
self.ordering_button_layout.addWidget(self.ordering_up_button)
self.ordering_button_layout.addWidget(self.ordering_down_button)
self.ordering_button_layout.addStretch(1)
self.player_order_layout.addLayout(self.ordering_button_layout)
self.left_layout.addWidget(self.player_order_group_box)
self.left_layout.addStretch()
self.right_layout.addStretch()
# Signals and slots
self.background_color_button.colorChanged.connect(self.on_background_color_changed)
def retranslate_ui(self):
"""
Translate the UI on the fly
"""
self.media_player_group_box.setTitle(translate('OpenLP.PlayerTab', 'Available Media Players'))
self.player_order_group_box.setTitle(translate('OpenLP.PlayerTab', 'Player Search Order'))
self.background_color_group_box.setTitle(UiStrings().BackgroundColor)
self.background_color_label.setText(UiStrings().BackgroundColorColon)
self.information_label.setText(translate('OpenLP.PlayerTab',
'Visible background for videos with aspect ratio different to screen.'))
self.retranslate_players()
def on_background_color_changed(self, color):
"""
Set the background color
:param color: The color to be set.
"""
self.background_color = color
def on_player_check_box_changed(self, check_state):
"""
Add or remove players depending on their status
:param check_state: The requested status.
"""
player = self.sender().player_name
if check_state == QtCore.Qt.Checked:
if player not in self.used_players:
self.used_players.append(player)
else:
if player in self.used_players:
self.used_players.remove(player)
self.update_player_list()
def update_player_list(self):
"""
Update the list of media players
"""
self.player_order_list_widget.clear()
for player in self.used_players:
if player in list(self.player_check_boxes.keys()):
if len(self.used_players) == 1:
# At least one media player has to stay active
self.player_check_boxes['%s' % player].setEnabled(False)
else:
self.player_check_boxes['%s' % player].setEnabled(True)
self.player_order_list_widget.addItem(self.media_players[str(player)].original_name)
def on_up_button_clicked(self):
"""
Move a media player up in the order
"""
row = self.player_order_list_widget.currentRow()
if row <= 0:
return
item = self.player_order_list_widget.takeItem(row)
self.player_order_list_widget.insertItem(row - 1, item)
self.player_order_list_widget.setCurrentRow(row - 1)
self.used_players.insert(row - 1, self.used_players.pop(row))
def on_down_button_clicked(self):
"""
Move a media player down in the order
"""
row = self.player_order_list_widget.currentRow()
if row == -1 or row > self.player_order_list_widget.count() - 1:
return
item = self.player_order_list_widget.takeItem(row)
self.player_order_list_widget.insertItem(row + 1, item)
self.player_order_list_widget.setCurrentRow(row + 1)
self.used_players.insert(row + 1, self.used_players.pop(row))
def load(self):
"""
Load the settings
"""
if self.saved_used_players:
self.used_players = self.saved_used_players
# self.used_players = get_media_players()[0]
self.saved_used_players = self.used_players
settings = Settings()
settings.beginGroup(self.settings_section)
self.update_player_list()
self.background_color = settings.value('background color')
self.initial_color = self.background_color
settings.endGroup()
self.background_color_button.color = self.background_color
def save(self):
"""
Save the settings
"""
settings = Settings()
settings.beginGroup(self.settings_section)
settings.setValue('background color', self.background_color)
settings.endGroup()
# old_players, override_player = get_media_players()
# if self.used_players != old_players:
# # clean old Media stuff
# set_media_players(self.used_players, override_player)
# self.settings_form.register_post_process('mediaitem_suffix_reset')
# self.settings_form.register_post_process('mediaitem_media_rebuild')
# self.settings_form.register_post_process('config_screen_changed')
def post_set_up(self, post_update=False):
"""
Late setup for players as the MediaController has to be initialised first.
:param post_update: Indicates if called before or after updates.
"""
for key, player in self.media_players.items():
player = self.media_players[key]
checkbox = MediaQCheckBox(self.media_player_group_box)
checkbox.setEnabled(player.available)
checkbox.setObjectName(player.name + '_check_box')
checkbox.setToolTip(player.get_info())
checkbox.set_player_name(player.name)
self.player_check_boxes[player.name] = checkbox
checkbox.stateChanged.connect(self.on_player_check_box_changed)
self.media_player_layout.addWidget(checkbox)
if player.available and player.name in self.used_players:
checkbox.setChecked(True)
else:
checkbox.setChecked(False)
self.update_player_list()
self.retranslate_players()
def retranslate_players(self):
"""
Translations for players is dependent on their setup as well
"""
for key in self.media_players and self.player_check_boxes:
player = self.media_players[key]
checkbox = self.player_check_boxes[player.name]
checkbox.set_player_name(player.name)
if player.available:
checkbox.setText(player.display_name)
else:
checkbox_text = translate('OpenLP.PlayerTab', '%s (unavailable)') % player.display_name
if player.name == 'vlc':
checkbox_text += ' ' + translate('OpenLP.PlayerTab',
'NOTE: To use VLC you must install the %s version',
'Will insert "32bit" or "64bit"') % platform.architecture()[0]
checkbox.setText(checkbox_text)

View File

@ -1,332 +0,0 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2019 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; version 2 of the License. #
# #
# 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, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`~openlp.core.ui.media.systemplayer` contains the system (aka QtMultimedia) player component.
"""
import functools
import logging
import mimetypes
from PyQt5 import QtCore, QtMultimedia, QtMultimediaWidgets
from openlp.core.common.i18n import translate
from openlp.core.threading import ThreadWorker, is_thread_finished, run_thread
from openlp.core.ui.media import MediaState
from openlp.core.ui.media.mediaplayer import MediaPlayer
log = logging.getLogger(__name__)
ADDITIONAL_EXT = {
'audio/ac3': ['.ac3'],
'audio/flac': ['.flac'],
'audio/x-m4a': ['.m4a'],
'audio/midi': ['.mid', '.midi'],
'audio/x-mp3': ['.mp3'],
'audio/mpeg': ['.mp3', '.mp2', '.mpga', '.mpega', '.m4a'],
'audio/qcelp': ['.qcp'],
'audio/x-wma': ['.wma'],
'audio/x-ms-wma': ['.wma'],
'video/x-flv': ['.flv'],
'video/x-matroska': ['.mpv', '.mkv'],
'video/x-wmv': ['.wmv'],
'video/x-mpg': ['.mpg'],
'video/mpeg': ['.mp4', '.mts', '.mov'],
'video/x-ms-wmv': ['.wmv']
}
class SystemPlayer(MediaPlayer):
"""
A specialised version of the MediaPlayer class, which provides a QtMultimedia display.
"""
def __init__(self, parent):
"""
Constructor
"""
super(SystemPlayer, self).__init__(parent, 'system')
self.original_name = 'System'
self.display_name = '&System'
self.parent = parent
self.additional_extensions = ADDITIONAL_EXT
self.media_player = QtMultimedia.QMediaPlayer(None, QtMultimedia.QMediaPlayer.VideoSurface)
mimetypes.init()
media_service = self.media_player.service()
log.info(media_service.__class__.__name__)
# supportedMimeTypes doesn't return anything on Linux and Windows and
# the mimetypes it returns on Mac OS X may not be playable.
supported_codecs = self.media_player.supportedMimeTypes()
for mime_type in supported_codecs:
mime_type = str(mime_type)
log.info(mime_type)
if mime_type.startswith('audio/'):
self._add_to_list(self.audio_extensions_list, mime_type)
elif mime_type.startswith('video/'):
self._add_to_list(self.video_extensions_list, mime_type)
def _add_to_list(self, mime_type_list, mime_type):
"""
Add mimetypes to the provided list
"""
# Add all extensions which mimetypes provides us for supported types.
extensions = mimetypes.guess_all_extensions(mime_type)
for extension in extensions:
ext = '*%s' % extension
if ext not in mime_type_list:
mime_type_list.append(ext)
log.info('MediaPlugin: %s extensions: %s', mime_type, ' '.join(extensions))
def disconnect_slots(self, signal):
"""
Safely disconnect the slots from `signal`
"""
try:
signal.disconnect()
except TypeError:
# If disconnect() is called on a signal without slots, it throws a TypeError
pass
def setup(self, display):
"""
Set up the player widgets
:param display:
"""
display.video_widget = QtMultimediaWidgets.QVideoWidget(display)
display.video_widget.resize(display.size())
display.media_player = QtMultimedia.QMediaPlayer(display)
display.media_player.setVideoOutput(display.video_widget)
display.video_widget.raise_()
display.video_widget.hide()
self.has_own_widget = True
def check_available(self):
"""
Check if the player is available
"""
return True
def load(self, display):
"""
Load a video into the display
:param display: The display where the media is
"""
log.debug('load vid in System Controller')
controller = display.controller
volume = controller.media_info.volume
path = controller.media_info.file_info.absoluteFilePath()
# Check if file is playable due to mimetype filters being nonexistent on Linux and Windows
if self.check_media(path):
display.media_player.setMedia(QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(path)))
self.volume(display, volume)
return True
else:
return False
def resize(self, display):
"""
Resize the display
:param display: The display where the media is
"""
display.video_widget.resize(display.size())
def play(self, display):
"""
Play the current media item
:param display: The display where the media is
"""
log.info('Play the current item')
controller = display.controller
start_time = 0
if display.controller.is_live:
if self.get_live_state() != QtMultimedia.QMediaPlayer.PausedState and controller.media_info.start_time > 0:
start_time = controller.media_info.start_time
else:
if self.get_preview_state() != QtMultimedia.QMediaPlayer.PausedState and \
controller.media_info.start_time > 0:
start_time = controller.media_info.start_time
display.media_player.play()
if start_time > 0:
self.seek(display, controller.media_info.start_time * 1000)
self.volume(display, controller.media_info.volume)
self.disconnect_slots(display.media_player.durationChanged)
display.media_player.durationChanged.connect(functools.partial(self.set_duration, controller))
self.set_state(MediaState.Playing, display)
display.video_widget.raise_()
return True
def pause(self, display):
"""
Pause the current media item
:param display: The display where the media is
"""
display.media_player.pause()
if display.controller.is_live:
if self.get_live_state() == QtMultimedia.QMediaPlayer.PausedState:
self.set_state(MediaState.Paused, display)
else:
if self.get_preview_state() == QtMultimedia.QMediaPlayer.PausedState:
self.set_state(MediaState.Paused, display)
def stop(self, display):
"""
Stop the current media item
:param display: The display where the media is
"""
display.media_player.stop()
self.set_visible(display, False)
self.set_state(MediaState.Stopped, display)
def volume(self, display, volume):
"""
Set the volume
:param display: The display where the media is
:param volume: The volume to be set
"""
if display.has_audio:
display.media_player.setVolume(volume)
def seek(self, display, seek_value):
"""
Go to a particular point in the current media item
:param display: The display where the media is
:param seek_value: The where to seek to
"""
display.media_player.setPosition(seek_value)
def reset(self, display):
"""
Reset the media player
:param display: The display where the media is
"""
display.media_player.stop()
display.media_player.setMedia(QtMultimedia.QMediaContent())
self.set_visible(display, False)
display.video_widget.setVisible(False)
self.set_state(MediaState.Off, display)
def set_visible(self, display, status):
"""
Set the visibility of the widget
:param display: The display where the media is
:param status: The visibility status to be set
"""
if self.has_own_widget:
display.video_widget.setVisible(status)
@staticmethod
def set_duration(controller, duration):
"""
:param controller: the controller displaying the media
:param duration: how long is the media
:return:
"""
controller.seek_slider.setMaximum(controller.media_info.length)
def update_ui(self, display):
"""
Update the UI
:param display: The display where the media is
"""
if display.media_player.state() == QtMultimedia.QMediaPlayer.PausedState and self.state != MediaState.Paused:
self.pause(display)
controller = display.controller
if controller.media_info.end_time > 0:
if display.media_player.position() > controller.media_info.end_time:
self.stop(display)
self.set_visible(display, False)
if not controller.seek_slider.isSliderDown():
controller.seek_slider.blockSignals(True)
controller.seek_slider.setSliderPosition(display.media_player.position())
controller.seek_slider.blockSignals(False)
def get_media_display_css(self):
"""
Add css style sheets to htmlbuilder
"""
return ''
def get_info(self):
"""
Return some info about this player
"""
return (translate('Media.player', 'This media player uses your operating system '
'to provide media capabilities.') +
'<br/> <strong>' + translate('Media.player', 'Audio') +
'</strong><br/>' + str(self.audio_extensions_list) +
'<br/><strong>' + translate('Media.player', 'Video') +
'</strong><br/>' + str(self.video_extensions_list) + '<br/>')
def check_media(self, path):
"""
Check if a file can be played
Uses a separate QMediaPlayer in a thread
:param path: Path to file to be checked
:return: True if file can be played otherwise False
"""
check_media_worker = CheckMediaWorker(path)
check_media_worker.setVolume(0)
run_thread(check_media_worker, 'check_media')
while not is_thread_finished('check_media'):
self.application.processEvents()
return check_media_worker.result
class CheckMediaWorker(QtMultimedia.QMediaPlayer, ThreadWorker):
"""
Class used to check if a media file is playable
"""
def __init__(self, path):
super(CheckMediaWorker, self).__init__(None, QtMultimedia.QMediaPlayer.VideoSurface)
self.path = path
def start(self):
"""
Start the thread worker
"""
self.result = None
self.error.connect(functools.partial(self.signals, 'error'))
self.mediaStatusChanged.connect(functools.partial(self.signals, 'media'))
self.setMedia(QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(self.path)))
self.play()
def signals(self, origin, status):
if origin == 'media' and status == self.BufferedMedia:
self.result = True
self.stop()
self.quit.emit()
elif origin == 'error' and status != self.NoError:
self.result = False
self.stop()
self.quit.emit()

View File

@ -152,43 +152,42 @@ class VlcPlayer(MediaPlayer):
self.audio_extensions_list = AUDIO_EXT self.audio_extensions_list = AUDIO_EXT
self.video_extensions_list = VIDEO_EXT self.video_extensions_list = VIDEO_EXT
def setup(self, display): def setup(self, output_display, live_display):
""" """
Set up the media player Set up the media player
:param display: The display where the media is :param output_display: The display where the media is
:param live_display: Is the display a live one.
:return: :return:
""" """
vlc = get_vlc() vlc = get_vlc()
display.vlc_widget = QtWidgets.QFrame(display) output_display.vlc_widget = QtWidgets.QFrame(output_display)
display.vlc_widget.setFrameStyle(QtWidgets.QFrame.NoFrame) output_display.vlc_widget.setFrameStyle(QtWidgets.QFrame.NoFrame)
# creating a basic vlc instance # creating a basic vlc instance
command_line_options = '--no-video-title-show' command_line_options = '--no-video-title-show'
if not display.has_audio: if Settings().value('advanced/hide mouse') and live_display:
command_line_options += ' --no-audio --no-video-title-show'
if Settings().value('advanced/hide mouse') and display.controller.is_live:
command_line_options += ' --mouse-hide-timeout=0' command_line_options += ' --mouse-hide-timeout=0'
display.vlc_instance = vlc.Instance(command_line_options) output_display.vlc_instance = vlc.Instance(command_line_options)
# creating an empty vlc media player # creating an empty vlc media player
display.vlc_media_player = display.vlc_instance.media_player_new() output_display.vlc_media_player = output_display.vlc_instance.media_player_new()
display.vlc_widget.resize(display.size()) output_display.vlc_widget.resize(output_display.size())
display.vlc_widget.raise_() output_display.vlc_widget.raise_()
display.vlc_widget.hide() output_display.vlc_widget.hide()
# The media player has to be 'connected' to the QFrame. # The media player has to be 'connected' to the QFrame.
# (otherwise a video would be displayed in it's own window) # (otherwise a video would be displayed in it's own window)
# This is platform specific! # This is platform specific!
# You have to give the id of the QFrame (or similar object) # You have to give the id of the QFrame (or similar object)
# to vlc, different platforms have different functions for this. # to vlc, different platforms have different functions for this.
win_id = int(display.vlc_widget.winId()) win_id = int(output_display.vlc_widget.winId())
if is_win(): if is_win():
display.vlc_media_player.set_hwnd(win_id) output_display.vlc_media_player.set_hwnd(win_id)
elif is_macosx(): elif is_macosx():
# We have to use 'set_nsobject' since Qt5 on OSX uses Cocoa # We have to use 'set_nsobject' since Qt5 on OSX uses Cocoa
# framework and not the old Carbon. # framework and not the old Carbon.
display.vlc_media_player.set_nsobject(win_id) output_display.vlc_media_player.set_nsobject(win_id)
else: else:
# for Linux/*BSD using the X Server # for Linux/*BSD using the X Server
display.vlc_media_player.set_xwindow(win_id) output_display.vlc_media_player.set_xwindow(win_id)
self.has_own_widget = True self.has_own_widget = True
def check_available(self): def check_available(self):
@ -197,43 +196,45 @@ class VlcPlayer(MediaPlayer):
""" """
return get_vlc() is not None return get_vlc() is not None
def load(self, display, file): def load(self, output_display, file):
""" """
Load a video into VLC Load a video into VLC
:param display: The display where the media is :param output_display: The display where the media is
:param file: file to be played :param file: file to be played
:return: :return:
""" """
vlc = get_vlc() vlc = get_vlc()
log.debug('load vid in Vlc Controller') log.debug('load vid in Vlc Controller')
controller = display.controller controller = output_display
volume = controller.media_info.volume volume = controller.media_info.volume
path = os.path.normcase(file) path = os.path.normcase(file)
# create the media # create the media
if controller.media_info.media_type == MediaType.CD: if controller.media_info.media_type == MediaType.CD:
if is_win(): if is_win():
path = '/' + path path = '/' + path
display.vlc_media = display.vlc_instance.media_new_location('cdda://' + path) output_display.vlc_media = output_display.vlc_instance.media_new_location('cdda://' + path)
display.vlc_media_player.set_media(display.vlc_media) output_display.vlc_media_player.set_media(output_display.vlc_media)
display.vlc_media_player.play() output_display.vlc_media_player.play()
# Wait for media to start playing. In this case VLC actually returns an error. # Wait for media to start playing. In this case VLC actually returns an error.
self.media_state_wait(display, vlc.State.Playing) self.media_state_wait(output_display, vlc.State.Playing)
# If subitems exists, this is a CD # If subitems exists, this is a CD
audio_cd_tracks = display.vlc_media.subitems() audio_cd_tracks = output_display.vlc_media.subitems()
if not audio_cd_tracks or audio_cd_tracks.count() < 1: if not audio_cd_tracks or audio_cd_tracks.count() < 1:
return False return False
display.vlc_media = audio_cd_tracks.item_at_index(controller.media_info.title_track) output_display.vlc_media = audio_cd_tracks.item_at_index(controller.media_info.title_track)
elif controller.media_info.media_type == MediaType.Stream:
output_display.vlc_media = output_display.vlc_instance.media_new_location('ZZZZZZ')
else: else:
display.vlc_media = display.vlc_instance.media_new_path(path) output_display.vlc_media = output_display.vlc_instance.media_new_path(path)
# put the media in the media player # put the media in the media player
display.vlc_media_player.set_media(display.vlc_media) output_display.vlc_media_player.set_media(output_display.vlc_media)
# parse the metadata of the file # parse the metadata of the file
display.vlc_media.parse() output_display.vlc_media.parse()
self.volume(display, volume) self.volume(output_display, volume)
return True return True
def media_state_wait(self, display, media_state): def media_state_wait(self, output_display, media_state):
""" """
Wait for the video to change its state Wait for the video to change its state
Wait no longer than 60 seconds. (loading an iso file needs a long time) Wait no longer than 60 seconds. (loading an iso file needs a long time)
@ -244,171 +245,171 @@ class VlcPlayer(MediaPlayer):
""" """
vlc = get_vlc() vlc = get_vlc()
start = datetime.now() start = datetime.now()
while media_state != display.vlc_media.get_state(): while media_state != output_display.vlc_media.get_state():
if display.vlc_media.get_state() == vlc.State.Error: if output_display.vlc_media.get_state() == vlc.State.Error:
return False return False
self.application.process_events() self.application.process_events()
if (datetime.now() - start).seconds > 60: if (datetime.now() - start).seconds > 60:
return False return False
return True return True
def resize(self, display): def resize(self, output_display):
""" """
Resize the player Resize the player
:param display: The display where the media is :param output_display: The display where the media is
:return: :return:
""" """
display.vlc_widget.resize(display.size()) output_display.vlc_widget.resize(output_display.size())
def play(self, display): def play(self, controller, output_display):
""" """
Play the current item Play the current item
:param display: The display where the media is :param controller: Which Controller is running the show.
:param output_display: The display where the media is
:return: :return:
""" """
vlc = get_vlc() vlc = get_vlc()
controller = display.controller
start_time = 0 start_time = 0
log.debug('vlc play') log.debug('vlc play')
if display.controller.is_live: if output_display.is_display:
if self.get_live_state() != MediaState.Paused and controller.media_info.start_time > 0: if self.get_live_state() != MediaState.Paused and output_display.media_info.start_time > 0:
start_time = controller.media_info.start_time start_time = output_display.media_info.start_time
else: else:
if self.get_preview_state() != MediaState.Paused and controller.media_info.start_time > 0: if self.get_preview_state() != MediaState.Paused and output_display.media_info.start_time > 0:
start_time = controller.media_info.start_time start_time = output_display.media_info.start_time
threading.Thread(target=display.vlc_media_player.play).start() threading.Thread(target=output_display.vlc_media_player.play).start()
if not self.media_state_wait(display, vlc.State.Playing): if not self.media_state_wait(output_display, vlc.State.Playing):
return False return False
if display.controller.is_live: if output_display.is_display:
if self.get_live_state() != MediaState.Paused and controller.media_info.start_time > 0: if self.get_live_state() != MediaState.Paused and output_display.media_info.start_time > 0:
log.debug('vlc play, start time set') log.debug('vlc play, start time set')
start_time = controller.media_info.start_time start_time = output_display.media_info.start_time
else: else:
if self.get_preview_state() != MediaState.Paused and controller.media_info.start_time > 0: if self.get_preview_state() != MediaState.Paused and output_display.media_info.start_time > 0:
log.debug('vlc play, start time set') log.debug('vlc play, start time set')
start_time = controller.media_info.start_time start_time = output_display.media_info.start_time
log.debug('mediatype: ' + str(controller.media_info.media_type)) log.debug('mediatype: ' + str(output_display.media_info.media_type))
# Set tracks for the optical device # Set tracks for the optical device
if controller.media_info.media_type == MediaType.DVD and \ if output_display.media_info.media_type == MediaType.DVD and \
self.get_live_state() != MediaState.Paused and self.get_preview_state() != MediaState.Paused: self.get_live_state() != MediaState.Paused and self.get_preview_state() != MediaState.Paused:
log.debug('vlc play, playing started') log.debug('vlc play, playing started')
if controller.media_info.title_track > 0: if output_display.media_info.title_track > 0:
log.debug('vlc play, title_track set: ' + str(controller.media_info.title_track)) log.debug('vlc play, title_track set: ' + str(output_display.media_info.title_track))
display.vlc_media_player.set_title(controller.media_info.title_track) output_display.vlc_media_player.set_title(output_display.media_info.title_track)
display.vlc_media_player.play() output_display.vlc_media_player.play()
if not self.media_state_wait(display, vlc.State.Playing): if not self.media_state_wait(output_display, vlc.State.Playing):
return False return False
if controller.media_info.audio_track > 0: if output_display.media_info.audio_track > 0:
display.vlc_media_player.audio_set_track(controller.media_info.audio_track) output_display.vlc_media_player.audio_set_track(output_display.media_info.audio_track)
log.debug('vlc play, audio_track set: ' + str(controller.media_info.audio_track)) log.debug('vlc play, audio_track set: ' + str(output_display.media_info.audio_track))
if controller.media_info.subtitle_track > 0: if output_display.media_info.subtitle_track > 0:
display.vlc_media_player.video_set_spu(controller.media_info.subtitle_track) output_display.vlc_media_player.video_set_spu(output_display.media_info.subtitle_track)
log.debug('vlc play, subtitle_track set: ' + str(controller.media_info.subtitle_track)) log.debug('vlc play, subtitle_track set: ' + str(output_display.media_info.subtitle_track))
if controller.media_info.start_time > 0: if output_display.media_info.start_time > 0:
log.debug('vlc play, starttime set: ' + str(controller.media_info.start_time)) log.debug('vlc play, starttime set: ' + str(output_display.media_info.start_time))
start_time = controller.media_info.start_time start_time = output_display.media_info.start_time
controller.media_info.length = controller.media_info.end_time - controller.media_info.start_time output_display.media_info.length = output_display.media_info.end_time - output_display.media_info.start_time
self.volume(display, controller.media_info.volume) self.volume(output_display, output_display.media_info.volume)
if start_time > 0 and display.vlc_media_player.is_seekable(): if start_time > 0 and output_display.vlc_media_player.is_seekable():
display.vlc_media_player.set_time(int(start_time)) output_display.vlc_media_player.set_time(int(start_time))
controller.seek_slider.setMaximum(controller.media_info.length) controller.seek_slider.setMaximum(output_display.media_info.length)
self.set_state(MediaState.Playing, display) self.set_state(MediaState.Playing, output_display)
display.vlc_widget.raise_() output_display.vlc_widget.raise_()
return True return True
def pause(self, display): def pause(self, output_display):
""" """
Pause the current item Pause the current item
:param display: The display where the media is :param output_display: The display where the media is
:return: :return:
""" """
vlc = get_vlc() vlc = get_vlc()
if display.vlc_media.get_state() != vlc.State.Playing: if output_display.vlc_media.get_state() != vlc.State.Playing:
return return
display.vlc_media_player.pause() output_display.vlc_media_player.pause()
if self.media_state_wait(display, vlc.State.Paused): if self.media_state_wait(output_display, vlc.State.Paused):
self.set_state(MediaState.Paused, display) self.set_state(MediaState.Paused, output_display)
def stop(self, display): def stop(self, output_display):
""" """
Stop the current item Stop the current item
:param display: The display where the media is :param output_display: The display where the media is
:return: :return:
""" """
threading.Thread(target=display.vlc_media_player.stop).start() threading.Thread(target=output_display.vlc_media_player.stop).start()
self.set_state(MediaState.Stopped, display) self.set_state(MediaState.Stopped, output_display)
def volume(self, display, vol): def volume(self, output_display, vol):
""" """
Set the volume Set the volume
:param vol: The volume to be sets :param vol: The volume to be sets
:param display: The display where the media is :param output_display: The display where the media is
:return: :return:
""" """
if display.has_audio: if output_display.has_audio:
display.vlc_media_player.audio_set_volume(vol) output_display.vlc_media_player.audio_set_volume(vol)
def seek(self, display, seek_value): def seek(self, output_display, seek_value):
""" """
Go to a particular position Go to a particular position
:param seek_value: The position of where a seek goes to :param seek_value: The position of where a seek goes to
:param display: The display where the media is :param output_display: The display where the media is
""" """
if display.controller.media_info.media_type == MediaType.CD \ if output_display.controller.media_info.media_type == MediaType.CD \
or display.controller.media_info.media_type == MediaType.DVD: or output_display.controller.media_info.media_type == MediaType.DVD:
seek_value += int(display.controller.media_info.start_time) seek_value += int(output_display.controller.media_info.start_time)
if display.vlc_media_player.is_seekable(): if output_display.vlc_media_player.is_seekable():
display.vlc_media_player.set_time(seek_value) output_display.vlc_media_player.set_time(seek_value)
def reset(self, display): def reset(self, output_display):
""" """
Reset the player Reset the player
:param display: The display where the media is :param output_display: The display where the media is
""" """
display.vlc_media_player.stop() output_display.vlc_media_player.stop()
display.vlc_widget.setVisible(False) output_display.vlc_widget.setVisible(False)
self.set_state(MediaState.Off, display) self.set_state(MediaState.Off, output_display)
def set_visible(self, display, status): def set_visible(self, output_display, status):
""" """
Set the visibility Set the visibility
:param display: The display where the media is :param output_display: The display where the media is
:param status: The visibility status :param status: The visibility status
""" """
if self.has_own_widget: if self.has_own_widget:
display.vlc_widget.setVisible(status) output_display.vlc_widget.setVisible(status)
def update_ui(self, display): def update_ui(self, controller, output_display):
""" """
Update the UI Update the UI
:param display: The display where the media is :param controller: Which Controller is running the show.
:param output_display: The display where the media is
""" """
vlc = get_vlc() vlc = get_vlc()
# Stop video if playback is finished. # Stop video if playback is finished.
if display.vlc_media.get_state() == vlc.State.Ended: if output_display.vlc_media.get_state() == vlc.State.Ended:
self.stop(display) self.stop(output_display)
controller = display.controller
if controller.media_info.end_time > 0: if controller.media_info.end_time > 0:
if display.vlc_media_player.get_time() > controller.media_info.end_time: if output_display.vlc_media_player.get_time() > controller.media_info.end_time:
self.stop(display) self.stop(output_display)
self.set_visible(display, False) self.set_visible(output_display, False)
if not controller.seek_slider.isSliderDown(): if not controller.seek_slider.isSliderDown():
controller.seek_slider.blockSignals(True) controller.seek_slider.blockSignals(True)
if display.controller.media_info.media_type == MediaType.CD \ if controller.media_info.media_type == MediaType.CD \
or display.controller.media_info.media_type == MediaType.DVD: or controller.media_info.media_type == MediaType.DVD:
controller.seek_slider.setSliderPosition( controller.seek_slider.setSliderPosition(
display.vlc_media_player.get_time() - int(display.controller.media_info.start_time)) output_display.vlc_media_player.get_time() - int(output_display.controller.media_info.start_time))
else: else:
controller.seek_slider.setSliderPosition(display.vlc_media_player.get_time()) controller.seek_slider.setSliderPosition(output_display.vlc_media_player.get_time())
controller.seek_slider.blockSignals(False) controller.seek_slider.blockSignals(False)
def get_info(self): def get_info(self):

View File

@ -41,7 +41,7 @@ class ScreensTab(SettingsTab):
""" """
Initialise the screen settings tab Initialise the screen settings tab
""" """
self.icon_path = UiIcons().settings self.icon_path = UiIcons().desktop
screens_translated = translate('OpenLP.ScreensTab', 'Screens') screens_translated = translate('OpenLP.ScreensTab', 'Screens')
super(ScreensTab, self).__init__(parent, 'Screens', screens_translated) super(ScreensTab, self).__init__(parent, 'Screens', screens_translated)
self.settings_section = 'core' self.settings_section = 'core'

View File

@ -30,13 +30,14 @@ from openlp.core.state import State
from openlp.core.api.tab import ApiTab from openlp.core.api.tab import ApiTab
from openlp.core.common.mixins import RegistryProperties from openlp.core.common.mixins import RegistryProperties
from openlp.core.common.registry import Registry from openlp.core.common.registry import Registry
from openlp.core.common.settings import Settings
from openlp.core.lib import build_icon from openlp.core.lib import build_icon
from openlp.core.projectors.tab import ProjectorTab from openlp.core.projectors.tab import ProjectorTab
from openlp.core.ui.advancedtab import AdvancedTab from openlp.core.ui.advancedtab import AdvancedTab
from openlp.core.ui.generaltab import GeneralTab from openlp.core.ui.generaltab import GeneralTab
from openlp.core.ui.screenstab import ScreensTab from openlp.core.ui.screenstab import ScreensTab
from openlp.core.ui.themestab import ThemesTab from openlp.core.ui.themestab import ThemesTab
# from openlp.core.ui.media.playertab import PlayerTab from openlp.core.ui.media.mediatab import MediaTab
from openlp.core.ui.settingsdialog import Ui_SettingsDialog from openlp.core.ui.settingsdialog import Ui_SettingsDialog
@ -78,8 +79,8 @@ class SettingsForm(QtWidgets.QDialog, Ui_SettingsDialog, RegistryProperties):
self.insert_tab(self.advanced_tab) self.insert_tab(self.advanced_tab)
self.insert_tab(self.screens_tab) self.insert_tab(self.screens_tab)
self.insert_tab(self.themes_tab) self.insert_tab(self.themes_tab)
self.insert_tab(self.advanced_tab) if Settings().value('core/experimental'):
# self.insert_tab(self.player_tab) self.insert_tab(self.player_tab)
self.insert_tab(self.projector_tab) self.insert_tab(self.projector_tab)
self.insert_tab(self.api_tab) self.insert_tab(self.api_tab)
for plugin in State().list_plugins(): for plugin in State().list_plugins():
@ -160,15 +161,17 @@ class SettingsForm(QtWidgets.QDialog, Ui_SettingsDialog, RegistryProperties):
self.themes_tab = ThemesTab(self) self.themes_tab = ThemesTab(self)
self.projector_tab = ProjectorTab(self) self.projector_tab = ProjectorTab(self)
self.advanced_tab = AdvancedTab(self) self.advanced_tab = AdvancedTab(self)
# self.player_tab = PlayerTab(self) if Settings().value('core/experimental'):
self.player_tab = MediaTab(self)
self.api_tab = ApiTab(self) self.api_tab = ApiTab(self)
self.screens_tab = ScreensTab(self) self.screens_tab = ScreensTab(self)
except Exception as e: except Exception as e:
print(e) print(e)
# Post setup
self.general_tab.post_set_up() self.general_tab.post_set_up()
self.themes_tab.post_set_up() self.themes_tab.post_set_up()
self.advanced_tab.post_set_up() self.advanced_tab.post_set_up()
if Settings().value('core/experimental'):
self.player_tab.post_set_up()
self.api_tab.post_set_up() self.api_tab.post_set_up()
for plugin in State().list_plugins(): for plugin in State().list_plugins():
if plugin.settings_tab: if plugin.settings_tab:

View File

@ -23,6 +23,7 @@
The :mod:`slidecontroller` module contains the most important part of OpenLP - the slide controller The :mod:`slidecontroller` module contains the most important part of OpenLP - the slide controller
""" """
import copy import copy
import datetime
from collections import deque from collections import deque
from threading import Lock from threading import Lock
@ -70,6 +71,45 @@ NON_TEXT_MENU = [
] ]
class MediaSlider(QtWidgets.QSlider):
"""
Allows the mouse events of a slider to be overridden and extra functionality added
"""
def __init__(self, direction, manager, controller):
"""
Constructor
"""
super(MediaSlider, self).__init__(direction)
self.manager = manager
self.controller = controller
def mouseMoveEvent(self, event):
"""
Override event to allow hover time to be displayed.
:param event: The triggering event
"""
time_value = QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width())
self.setToolTip('%s' % datetime.timedelta(seconds=int(time_value / 1000)))
QtWidgets.QSlider.mouseMoveEvent(self, event)
def mousePressEvent(self, event):
"""
Mouse Press event no new functionality
:param event: The triggering event
"""
QtWidgets.QSlider.mousePressEvent(self, event)
def mouseReleaseEvent(self, event):
"""
Set the slider position when the mouse is clicked and released on the slider.
:param event: The triggering event
"""
self.setValue(QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width()))
QtWidgets.QSlider.mouseReleaseEvent(self, event)
class InfoLabel(QtWidgets.QLabel): class InfoLabel(QtWidgets.QLabel):
""" """
InfoLabel is a subclassed QLabel. Created to provide the ablilty to add a ellipsis if the text is cut off. Original InfoLabel is a subclassed QLabel. Created to provide the ablilty to add a ellipsis if the text is cut off. Original
@ -316,8 +356,59 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
'Clear'), 'Clear'),
triggers=self.on_clear) triggers=self.on_clear)
self.controller_layout.addWidget(self.toolbar) self.controller_layout.addWidget(self.toolbar)
# Build the Media Toolbar # Build a Media ToolBar
self.media_controller.register_controller(self) self.mediabar = OpenLPToolbar(self)
self.mediabar.add_toolbar_action('playbackPlay', text='media_playback_play',
icon=UiIcons().play,
tooltip=translate('OpenLP.SlideController', 'Start playing media.'),
triggers=self.send_to_plugins)
self.mediabar.add_toolbar_action('playbackPause', text='media_playback_pause',
icon=UiIcons().pause,
tooltip=translate('OpenLP.SlideController', 'Pause playing media.'),
triggers=self.send_to_plugins)
self.mediabar.add_toolbar_action('playbackStop', text='media_playback_stop',
icon=UiIcons().stop,
tooltip=translate('OpenLP.SlideController', 'Stop playing media.'),
triggers=self.send_to_plugins)
self.mediabar.add_toolbar_action('playbackLoop', text='media_playback_loop',
icon=UiIcons().repeat, checked=False,
tooltip=translate('OpenLP.SlideController', 'Loop playing media.'),
triggers=self.send_to_plugins)
self.position_label = QtWidgets.QLabel()
self.position_label.setText(' 00:00 / 00:00')
self.position_label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.position_label.setToolTip(translate('OpenLP.SlideController', 'Video timer.'))
self.position_label.setMinimumSize(90, 0)
self.position_label.setObjectName('position_label')
self.mediabar.add_toolbar_widget(self.position_label)
# Build the seek_slider.
self.seek_slider = MediaSlider(QtCore.Qt.Horizontal, self, self)
self.seek_slider.setMaximum(1000)
self.seek_slider.setTracking(True)
self.seek_slider.setMouseTracking(True)
self.seek_slider.setToolTip(translate('OpenLP.SlideController', 'Video position.'))
self.seek_slider.setGeometry(QtCore.QRect(90, 260, 221, 24))
self.seek_slider.setObjectName('seek_slider')
self.mediabar.add_toolbar_widget(self.seek_slider)
# Build the volume_slider.
self.volume_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.volume_slider.setTickInterval(10)
self.volume_slider.setTickPosition(QtWidgets.QSlider.TicksAbove)
self.volume_slider.setMinimum(0)
self.volume_slider.setMaximum(100)
self.volume_slider.setTracking(True)
self.volume_slider.setToolTip(translate('OpenLP.SlideController', 'Audio Volume.'))
# self.volume_slider.setValue(self.media_info.volume)
self.volume_slider.setGeometry(QtCore.QRect(90, 160, 221, 24))
self.volume_slider.setObjectName('volume_slider')
self.mediabar.add_toolbar_widget(self.volume_slider)
self.controller_layout.addWidget(self.mediabar)
self.mediabar.setVisible(False)
if not self.is_live:
self.volume_slider.setEnabled(False)
# Signals
self.seek_slider.valueChanged.connect(self.send_to_plugins)
self.volume_slider.valueChanged.connect(self.send_to_plugins)
if self.is_live: if self.is_live:
# Build the Song Toolbar # Build the Song Toolbar
self.song_menu = QtWidgets.QToolButton(self.toolbar) self.song_menu = QtWidgets.QToolButton(self.toolbar)
@ -556,8 +647,6 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
# self.__add_actions_to_widget(self.display) # self.__add_actions_to_widget(self.display)
# The SlidePreview's ratio. # The SlidePreview's ratio.
# TODO: Need to basically update everything
def __add_actions_to_widget(self, widget): def __add_actions_to_widget(self, widget):
""" """
Add actions to the widget specified by `widget` Add actions to the widget specified by `widget`
@ -1398,10 +1487,10 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
:param item: The service item to be processed :param item: The service item to be processed
""" """
if self.is_live and self.hide_mode() == HideMode.Theme: if self.is_live and self.hide_mode() == HideMode.Theme:
self.media_controller.video(self.controller_type, item, HideMode.Blank) self.media_controller.load_video(self.controller_type, item, HideMode.Blank)
self.on_blank_display(True) self.on_blank_display(True)
else: else:
self.media_controller.video(self.controller_type, item, self.hide_mode()) self.media_controller.load_video(self.controller_type, item, self.hide_mode())
if not self.is_live: if not self.is_live:
self.preview_display.show() self.preview_display.show()
@ -1491,7 +1580,7 @@ class PreviewController(RegistryBase, SlideController):
self.type_prefix = 'preview' self.type_prefix = 'preview'
self.category = 'Preview Toolbar' self.category = 'Preview Toolbar'
def bootstrap_post_set_up(self): def bootstrap_initialise(self):
""" """
process the bootstrap post setup request process the bootstrap post setup request
""" """
@ -1523,7 +1612,7 @@ class LiveController(RegistryBase, SlideController):
self.category = UiStrings().LiveToolbar self.category = UiStrings().LiveToolbar
ActionList.get_instance().add_category(str(self.category), CategoryOrder.standard_toolbar) ActionList.get_instance().add_category(str(self.category), CategoryOrder.standard_toolbar)
def bootstrap_post_set_up(self): def bootstrap_initialise(self):
""" """
process the bootstrap post setup request process the bootstrap post setup request
""" """

View File

@ -330,6 +330,8 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
self.video_color_button.color = self.theme.background_border_color self.video_color_button.color = self.theme.background_border_color
self.video_path_edit.path = self.theme.background_filename self.video_path_edit.path = self.theme.background_filename
self.setField('background_type', 4) self.setField('background_type', 4)
elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Stream):
self.setField('background_type', 5)
elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Transparent): elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Transparent):
self.setField('background_type', 3) self.setField('background_type', 3)
if self.theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Horizontal): if self.theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Horizontal):

View File

@ -64,7 +64,7 @@ class Ui_ThemeWizard(object):
self.background_label = QtWidgets.QLabel(self.background_page) self.background_label = QtWidgets.QLabel(self.background_page)
self.background_label.setObjectName('background_label') self.background_label.setObjectName('background_label')
self.background_combo_box = QtWidgets.QComboBox(self.background_page) self.background_combo_box = QtWidgets.QComboBox(self.background_page)
self.background_combo_box.addItems(['', '', '', '', '']) self.background_combo_box.addItems(['', '', '', '', '', ''])
self.background_combo_box.setObjectName('background_combo_box') self.background_combo_box.setObjectName('background_combo_box')
self.background_type_layout.addRow(self.background_label, self.background_combo_box) self.background_type_layout.addRow(self.background_label, self.background_combo_box)
self.background_type_layout.setItem(1, QtWidgets.QFormLayout.LabelRole, self.spacer) self.background_type_layout.setItem(1, QtWidgets.QFormLayout.LabelRole, self.spacer)
@ -410,6 +410,8 @@ class Ui_ThemeWizard(object):
self.background_combo_box.setItemText(BackgroundType.Video, UiStrings().Video) self.background_combo_box.setItemText(BackgroundType.Video, UiStrings().Video)
self.background_combo_box.setItemText(BackgroundType.Transparent, self.background_combo_box.setItemText(BackgroundType.Transparent,
translate('OpenLP.ThemeWizard', 'Transparent')) translate('OpenLP.ThemeWizard', 'Transparent'))
self.background_combo_box.setItemText(BackgroundType.Stream,
translate('OpenLP.ThemeWizard', 'Live Stream'))
self.color_label.setText(translate('OpenLP.ThemeWizard', 'color:')) self.color_label.setText(translate('OpenLP.ThemeWizard', 'color:'))
self.gradient_start_label.setText(translate('OpenLP.ThemeWizard', 'Starting color:')) self.gradient_start_label.setText(translate('OpenLP.ThemeWizard', 'Starting color:'))
self.gradient_end_label.setText(translate('OpenLP.ThemeWizard', 'Ending color:')) self.gradient_end_label.setText(translate('OpenLP.ThemeWizard', 'Ending color:'))

View File

@ -202,6 +202,9 @@ class ListPreviewWidget(QtWidgets.QTableWidget, RegistryProperties):
if self.service_item.is_command(): if self.service_item.is_command():
if self.service_item.is_capable(ItemCapabilities.HasThumbnails): if self.service_item.is_capable(ItemCapabilities.HasThumbnails):
pixmap = QtGui.QPixmap(remove_url_prefix(slide['thumbnail'])) pixmap = QtGui.QPixmap(remove_url_prefix(slide['thumbnail']))
else:
if isinstance(slide['image'], QtGui.QIcon):
pixmap = slide['image'].pixmap(QtCore.QSize(32, 32))
else: else:
pixmap = QtGui.QPixmap(remove_url_prefix(slide['image'])) pixmap = QtGui.QPixmap(remove_url_prefix(slide['image']))
else: else:

View File

@ -116,6 +116,8 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
self.can_preview = False self.can_preview = False
self.can_make_live = False self.can_make_live = False
self.can_add_to_service = False self.can_add_to_service = False
if State().check_preconditions('media_live'):
self.can_make_live = False
def add_list_view_to_toolbar(self): def add_list_view_to_toolbar(self):
""" """
@ -264,7 +266,8 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
:param media: The media :param media: The media
:param target_group: :param target_group:
""" """
media.sort(key=lambda file_path: get_natural_key(file_path.name)) # TODO needs to be fixed as no idea why this fails
# media.sort(key=lambda file_path: get_natural_key(file_path.name))
for track in media: for track in media:
track_info = QtCore.QFileInfo(track) track_info = QtCore.QFileInfo(track)
item_name = None item_name = None

View File

@ -1,73 +0,0 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2019 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; version 2 of the License. #
# #
# 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, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
from PyQt5 import QtWidgets
from openlp.core.common.i18n import UiStrings, translate
from openlp.core.common.settings import Settings
from openlp.core.lib.settingstab import SettingsTab
class MediaTab(SettingsTab):
"""
MediaTab is the Media settings tab in the settings dialog.
"""
def __init__(self, parent, title, visible_title, icon_path):
self.parent = parent
super(MediaTab, self).__init__(parent, title, visible_title, icon_path)
def setup_ui(self):
self.setObjectName('MediaTab')
super(MediaTab, self).setup_ui()
self.advanced_group_box = QtWidgets.QGroupBox(self.left_column)
self.advanced_group_box.setObjectName('advanced_group_box')
self.advanced_layout = QtWidgets.QVBoxLayout(self.advanced_group_box)
self.advanced_layout.setObjectName('advanced_layout')
self.override_player_check_box = QtWidgets.QCheckBox(self.advanced_group_box)
self.override_player_check_box.setObjectName('override_player_check_box')
self.advanced_layout.addWidget(self.override_player_check_box)
self.auto_start_check_box = QtWidgets.QCheckBox(self.advanced_group_box)
self.auto_start_check_box.setObjectName('auto_start_check_box')
self.advanced_layout.addWidget(self.auto_start_check_box)
self.left_layout.addWidget(self.advanced_group_box)
self.left_layout.addStretch()
self.right_layout.addStretch()
def retranslate_ui(self):
self.advanced_group_box.setTitle(UiStrings().Advanced)
self.override_player_check_box.setText(translate('MediaPlugin.MediaTab', 'Allow media player to be overridden'))
self.auto_start_check_box.setText(translate('MediaPlugin.MediaTab', 'Start new Live media automatically'))
def load(self):
self.override_player_check_box.setChecked(Settings().value(self.settings_section + '/override player'))
self.auto_start_check_box.setChecked(Settings().value(self.settings_section + '/media auto start'))
def save(self):
setting_key = self.settings_section + '/override player'
if Settings().value(setting_key) != self.override_player_check_box.checkState():
Settings().setValue(setting_key, self.override_player_check_box.checkState())
self.settings_form.register_post_process('mediaitem_suffix_reset')
self.settings_form.register_post_process('mediaitem_media_rebuild')
self.settings_form.register_post_process('mediaitem_suffixes')
setting_key = self.settings_section + '/media auto start'
if Settings().value(setting_key) != self.auto_start_check_box.checkState():
Settings().setValue(setting_key, self.auto_start_check_box.checkState())

View File

@ -24,8 +24,6 @@ The Media plugin
""" """
import logging import logging
from PyQt5 import QtCore
from openlp.core.state import State from openlp.core.state import State
from openlp.core.api.http import register_endpoint from openlp.core.api.http import register_endpoint
from openlp.core.common.i18n import translate from openlp.core.common.i18n import translate
@ -34,7 +32,6 @@ from openlp.core.lib import build_icon
from openlp.core.lib.plugin import Plugin, StringContent from openlp.core.lib.plugin import Plugin, StringContent
from openlp.plugins.media.endpoint import api_media_endpoint, media_endpoint from openlp.plugins.media.endpoint import api_media_endpoint, media_endpoint
from openlp.plugins.media.lib.mediaitem import MediaMediaItem from openlp.plugins.media.lib.mediaitem import MediaMediaItem
from openlp.plugins.media.lib.mediatab import MediaTab
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -42,7 +39,6 @@ log = logging.getLogger(__name__)
# Some settings starting with "media" are in core, because they are needed for core functionality. # Some settings starting with "media" are in core, because they are needed for core functionality.
__default_settings__ = { __default_settings__ = {
'media/media auto start': QtCore.Qt.Unchecked,
'media/media files': [], 'media/media files': [],
'media/last directory': None 'media/last directory': None
} }
@ -78,15 +74,6 @@ class MediaPlugin(Plugin):
""" """
pass pass
def create_settings_tab(self, parent):
"""
Create the settings Tab
:param parent:
"""
visible_name = self.get_string(StringContent.VisibleName)
self.settings_tab = MediaTab(parent, self.name, visible_name['title'], self.icon_path)
@staticmethod @staticmethod
def about(): def about():
""" """

View File

@ -139,13 +139,13 @@ class TestCommonFunctions(TestCase):
Test `path_to_module` when supplied with a `Path` object Test `path_to_module` when supplied with a `Path` object
""" """
# GIVEN: A `Path` object # GIVEN: A `Path` object
path = Path('core', 'ui', 'media', 'webkitplayer.py') path = Path('core', 'ui', 'media', 'vlcplayer.py')
# WHEN: Calling path_to_module with the `Path` object # WHEN: Calling path_to_module with the `Path` object
result = path_to_module(path) result = path_to_module(path)
# THEN: path_to_module should return the module name # THEN: path_to_module should return the module name
assert result == 'openlp.core.ui.media.webkitplayer' assert result == 'openlp.core.ui.media.vlcplayer'
def test_trace_error_handler(self): def test_trace_error_handler(self):
""" """

View File

@ -1,567 +0,0 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2019 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; version 2 of the License. #
# #
# 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, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
Package to test the openlp.core.ui.media.systemplayer package.
"""
from unittest import TestCase
from unittest.mock import MagicMock, call, patch
from PyQt5 import QtCore, QtMultimedia
from openlp.core.common.registry import Registry
from openlp.core.ui.media import MediaState
from openlp.core.ui.media.systemplayer import ADDITIONAL_EXT, CheckMediaWorker, SystemPlayer
class TestSystemPlayer(TestCase):
"""
Test the system media player
"""
@patch('openlp.core.ui.media.systemplayer.mimetypes')
@patch('openlp.core.ui.media.systemplayer.QtMultimedia.QMediaPlayer')
def test_constructor(self, MockQMediaPlayer, mocked_mimetypes):
"""
Test the SystemPlayer constructor
"""
# GIVEN: The SystemPlayer class and a mockedQMediaPlayer
mocked_media_player = MagicMock()
mocked_media_player.supportedMimeTypes.return_value = [
'application/postscript',
'audio/aiff',
'audio/x-aiff',
'text/html',
'video/animaflex',
'video/x-ms-asf'
]
mocked_mimetypes.guess_all_extensions.side_effect = [
['.aiff'],
['.aiff'],
['.afl'],
['.asf']
]
MockQMediaPlayer.return_value = mocked_media_player
# WHEN: An object is created from it
player = SystemPlayer(self)
# THEN: The correct initial values should be set up
assert 'system' == player.name
assert 'System' == player.original_name
assert '&System' == player.display_name
assert self == player.parent
assert ADDITIONAL_EXT == player.additional_extensions
MockQMediaPlayer.assert_called_once_with(None, QtMultimedia.QMediaPlayer.VideoSurface)
mocked_mimetypes.init.assert_called_once_with()
mocked_media_player.service.assert_called_once_with()
mocked_media_player.supportedMimeTypes.assert_called_once_with()
assert ['*.aiff'] == player.audio_extensions_list
assert ['*.afl', '*.asf'] == player.video_extensions_list
@patch('openlp.core.ui.media.systemplayer.QtMultimediaWidgets.QVideoWidget')
@patch('openlp.core.ui.media.systemplayer.QtMultimedia.QMediaPlayer')
def test_setup(self, MockQMediaPlayer, MockQVideoWidget):
"""
Test the setup() method of SystemPlayer
"""
# GIVEN: A SystemPlayer instance and a mock display
player = SystemPlayer(self)
mocked_display = MagicMock()
mocked_display.size.return_value = [1, 2, 3, 4]
mocked_video_widget = MagicMock()
mocked_media_player = MagicMock()
MockQVideoWidget.return_value = mocked_video_widget
MockQMediaPlayer.return_value = mocked_media_player
# WHEN: setup() is run
player.setup(mocked_display)
# THEN: The player should have a display widget
MockQVideoWidget.assert_called_once_with(mocked_display)
assert mocked_video_widget == mocked_display.video_widget
mocked_display.size.assert_called_once_with()
mocked_video_widget.resize.assert_called_once_with([1, 2, 3, 4])
MockQMediaPlayer.assert_called_with(mocked_display)
assert mocked_media_player == mocked_display.media_player
mocked_media_player.setVideoOutput.assert_called_once_with(mocked_video_widget)
mocked_video_widget.raise_.assert_called_once_with()
mocked_video_widget.hide.assert_called_once_with()
assert player.has_own_widget is True
def test_disconnect_slots(self):
"""
Test that we the disconnect slots method catches the TypeError
"""
# GIVEN: A SystemPlayer class and a signal that throws a TypeError
player = SystemPlayer(self)
mocked_signal = MagicMock()
mocked_signal.disconnect.side_effect = \
TypeError('disconnect() failed between \'durationChanged\' and all its connections')
# WHEN: disconnect_slots() is called
player.disconnect_slots(mocked_signal)
# THEN: disconnect should have been called and the exception should have been ignored
mocked_signal.disconnect.assert_called_once_with()
def test_check_available(self):
"""
Test the check_available() method on SystemPlayer
"""
# GIVEN: A SystemPlayer instance
player = SystemPlayer(self)
# WHEN: check_available is run
result = player.check_available()
# THEN: it should be available
assert result is True
def test_load_valid_media(self):
"""
Test the load() method of SystemPlayer with a valid media file
"""
# GIVEN: A SystemPlayer instance and a mocked display
player = SystemPlayer(self)
mocked_display = MagicMock()
mocked_display.controller.media_info.volume = 1
mocked_display.controller.media_info.file_info.absoluteFilePath.return_value = '/path/to/file'
# WHEN: The load() method is run
with patch.object(player, 'check_media') as mocked_check_media, \
patch.object(player, 'volume') as mocked_volume:
mocked_check_media.return_value = True
result = player.load(mocked_display)
# THEN: the file is sent to the video widget
mocked_display.controller.media_info.file_info.absoluteFilePath.assert_called_once_with()
mocked_check_media.assert_called_once_with('/path/to/file')
mocked_display.media_player.setMedia.assert_called_once_with(
QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile('/path/to/file')))
mocked_volume.assert_called_once_with(mocked_display, 1)
assert result is True
def test_load_invalid_media(self):
"""
Test the load() method of SystemPlayer with an invalid media file
"""
# GIVEN: A SystemPlayer instance and a mocked display
player = SystemPlayer(self)
mocked_display = MagicMock()
mocked_display.controller.media_info.volume = 1
mocked_display.controller.media_info.file_info.absoluteFilePath.return_value = '/path/to/file'
# WHEN: The load() method is run
with patch.object(player, 'check_media') as mocked_check_media, \
patch.object(player, 'volume'):
mocked_check_media.return_value = False
result = player.load(mocked_display)
# THEN: stuff
mocked_display.controller.media_info.file_info.absoluteFilePath.assert_called_once_with()
mocked_check_media.assert_called_once_with('/path/to/file')
assert result is False
def test_resize(self):
"""
Test the resize() method of the SystemPlayer
"""
# GIVEN: A SystemPlayer instance and a mocked display
player = SystemPlayer(self)
mocked_display = MagicMock()
mocked_display.size.return_value = [1, 2, 3, 4]
# WHEN: The resize() method is called
player.resize(mocked_display)
# THEN: The player is resized
mocked_display.size.assert_called_once_with()
mocked_display.video_widget.resize.assert_called_once_with([1, 2, 3, 4])
@patch('openlp.core.ui.media.systemplayer.functools')
def test_play_is_live(self, mocked_functools):
"""
Test the play() method of the SystemPlayer on the live display
"""
# GIVEN: A SystemPlayer instance and a mocked display
mocked_functools.partial.return_value = 'function'
player = SystemPlayer(self)
mocked_display = MagicMock()
mocked_display.controller.is_live = True
mocked_display.controller.media_info.start_time = 1
mocked_display.controller.media_info.volume = 1
# WHEN: play() is called
with patch.object(player, 'get_live_state') as mocked_get_live_state, \
patch.object(player, 'seek') as mocked_seek, \
patch.object(player, 'volume') as mocked_volume, \
patch.object(player, 'set_state') as mocked_set_state, \
patch.object(player, 'disconnect_slots') as mocked_disconnect_slots:
mocked_get_live_state.return_value = QtMultimedia.QMediaPlayer.PlayingState
result = player.play(mocked_display)
# THEN: the media file is played
mocked_get_live_state.assert_called_once_with()
mocked_display.media_player.play.assert_called_once_with()
mocked_seek.assert_called_once_with(mocked_display, 1000)
mocked_volume.assert_called_once_with(mocked_display, 1)
mocked_disconnect_slots.assert_called_once_with(mocked_display.media_player.durationChanged)
mocked_display.media_player.durationChanged.connect.assert_called_once_with('function')
mocked_set_state.assert_called_once_with(MediaState.Playing, mocked_display)
mocked_display.video_widget.raise_.assert_called_once_with()
assert result is True
@patch('openlp.core.ui.media.systemplayer.functools')
def test_play_is_preview(self, mocked_functools):
"""
Test the play() method of the SystemPlayer on the preview display
"""
# GIVEN: A SystemPlayer instance and a mocked display
mocked_functools.partial.return_value = 'function'
player = SystemPlayer(self)
mocked_display = MagicMock()
mocked_display.controller.is_live = False
mocked_display.controller.media_info.start_time = 1
mocked_display.controller.media_info.volume = 1
# WHEN: play() is called
with patch.object(player, 'get_preview_state') as mocked_get_preview_state, \
patch.object(player, 'seek') as mocked_seek, \
patch.object(player, 'volume') as mocked_volume, \
patch.object(player, 'set_state') as mocked_set_state:
mocked_get_preview_state.return_value = QtMultimedia.QMediaPlayer.PlayingState
result = player.play(mocked_display)
# THEN: the media file is played
mocked_get_preview_state.assert_called_once_with()
mocked_display.media_player.play.assert_called_once_with()
mocked_seek.assert_called_once_with(mocked_display, 1000)
mocked_volume.assert_called_once_with(mocked_display, 1)
mocked_display.media_player.durationChanged.connect.assert_called_once_with('function')
mocked_set_state.assert_called_once_with(MediaState.Playing, mocked_display)
mocked_display.video_widget.raise_.assert_called_once_with()
assert result is True
def test_pause_is_live(self):
"""
Test the pause() method of the SystemPlayer on the live display
"""
# GIVEN: A SystemPlayer instance
player = SystemPlayer(self)
mocked_display = MagicMock()
mocked_display.controller.is_live = True
# WHEN: The pause method is called
with patch.object(player, 'get_live_state') as mocked_get_live_state, \
patch.object(player, 'set_state') as mocked_set_state:
mocked_get_live_state.return_value = QtMultimedia.QMediaPlayer.PausedState
player.pause(mocked_display)
# THEN: The video is paused
mocked_display.media_player.pause.assert_called_once_with()
mocked_get_live_state.assert_called_once_with()
mocked_set_state.assert_called_once_with(MediaState.Paused, mocked_display)
def test_pause_is_preview(self):
"""
Test the pause() method of the SystemPlayer on the preview display
"""
# GIVEN: A SystemPlayer instance
player = SystemPlayer(self)
mocked_display = MagicMock()
mocked_display.controller.is_live = False
# WHEN: The pause method is called
with patch.object(player, 'get_preview_state') as mocked_get_preview_state, \
patch.object(player, 'set_state') as mocked_set_state:
mocked_get_preview_state.return_value = QtMultimedia.QMediaPlayer.PausedState
player.pause(mocked_display)
# THEN: The video is paused
mocked_display.media_player.pause.assert_called_once_with()
mocked_get_preview_state.assert_called_once_with()
mocked_set_state.assert_called_once_with(MediaState.Paused, mocked_display)
def test_stop(self):
"""
Test the stop() method of the SystemPlayer
"""
# GIVEN: A SystemPlayer instance
player = SystemPlayer(self)
mocked_display = MagicMock()
# WHEN: The stop method is called
with patch.object(player, 'set_visible') as mocked_set_visible, \
patch.object(player, 'set_state') as mocked_set_state:
player.stop(mocked_display)
# THEN: The video is stopped
mocked_display.media_player.stop.assert_called_once_with()
mocked_set_visible.assert_called_once_with(mocked_display, False)
mocked_set_state.assert_called_once_with(MediaState.Stopped, mocked_display)
def test_volume(self):
"""
Test the volume() method of the SystemPlayer
"""
# GIVEN: A SystemPlayer instance
player = SystemPlayer(self)
mocked_display = MagicMock()
mocked_display.has_audio = True
# WHEN: The stop method is called
player.volume(mocked_display, 2)
# THEN: The video is stopped
mocked_display.media_player.setVolume.assert_called_once_with(2)
def test_seek(self):
"""
Test the seek() method of the SystemPlayer
"""
# GIVEN: A SystemPlayer instance
player = SystemPlayer(self)
mocked_display = MagicMock()
# WHEN: The stop method is called
player.seek(mocked_display, 2)
# THEN: The video is stopped
mocked_display.media_player.setPosition.assert_called_once_with(2)
def test_reset(self):
"""
Test the reset() method of the SystemPlayer
"""
# GIVEN: A SystemPlayer instance
player = SystemPlayer(self)
mocked_display = MagicMock()
# WHEN: reset() is called
with patch.object(player, 'set_state') as mocked_set_state, \
patch.object(player, 'set_visible') as mocked_set_visible:
player.reset(mocked_display)
# THEN: The media player is reset
mocked_display.media_player.stop()
mocked_display.media_player.setMedia.assert_called_once_with(QtMultimedia.QMediaContent())
mocked_set_visible.assert_called_once_with(mocked_display, False)
mocked_display.video_widget.setVisible.assert_called_once_with(False)
mocked_set_state.assert_called_once_with(MediaState.Off, mocked_display)
def test_set_visible(self):
"""
Test the set_visible() method on the SystemPlayer
"""
# GIVEN: A SystemPlayer instance and a mocked display
player = SystemPlayer(self)
player.has_own_widget = True
mocked_display = MagicMock()
# WHEN: set_visible() is called
player.set_visible(mocked_display, True)
# THEN: The widget should be visible
mocked_display.video_widget.setVisible.assert_called_once_with(True)
def test_set_duration(self):
"""
Test the set_duration() method of the SystemPlayer
"""
# GIVEN: a mocked controller
mocked_controller = MagicMock()
mocked_controller.media_info.length = 5
# WHEN: The set_duration() is called. NB: the 10 here is ignored by the code
SystemPlayer.set_duration(mocked_controller, 10)
# THEN: The maximum length of the slider should be set
mocked_controller.seek_slider.setMaximum.assert_called_once_with(5)
def test_update_ui(self):
"""
Test the update_ui() method on the SystemPlayer
"""
# GIVEN: A SystemPlayer instance
player = SystemPlayer(self)
player.state = [MediaState.Playing, MediaState.Playing]
mocked_display = MagicMock()
mocked_display.media_player.state.return_value = QtMultimedia.QMediaPlayer.PausedState
mocked_display.controller.media_info.end_time = 1
mocked_display.media_player.position.return_value = 2
mocked_display.controller.seek_slider.isSliderDown.return_value = False
# WHEN: update_ui() is called
with patch.object(player, 'stop') as mocked_stop, \
patch.object(player, 'set_visible') as mocked_set_visible:
player.update_ui(mocked_display)
# THEN: The UI is updated
expected_stop_calls = [call(mocked_display)]
expected_position_calls = [call(), call()]
expected_block_signals_calls = [call(True), call(False)]
mocked_display.media_player.state.assert_called_once_with()
assert 1 == mocked_stop.call_count
assert expected_stop_calls == mocked_stop.call_args_list
assert 2 == mocked_display.media_player.position.call_count
assert expected_position_calls == mocked_display.media_player.position.call_args_list
mocked_set_visible.assert_called_once_with(mocked_display, False)
mocked_display.controller.seek_slider.isSliderDown.assert_called_once_with()
assert expected_block_signals_calls == mocked_display.controller.seek_slider.blockSignals.call_args_list
mocked_display.controller.seek_slider.setSliderPosition.assert_called_once_with(2)
def test_get_media_display_css(self):
"""
Test the get_media_display_css() method of the SystemPlayer
"""
# GIVEN: A SystemPlayer instance
player = SystemPlayer(self)
# WHEN: get_media_display_css() is called
result = player.get_media_display_css()
# THEN: The css should be empty
assert '' == result
@patch('openlp.core.ui.media.systemplayer.QtMultimedia.QMediaPlayer')
def test_get_info(self, MockQMediaPlayer):
"""
Test the get_info() method of the SystemPlayer
"""
# GIVEN: A SystemPlayer instance
mocked_media_player = MagicMock()
mocked_media_player.supportedMimeTypes.return_value = []
MockQMediaPlayer.return_value = mocked_media_player
player = SystemPlayer(self)
# WHEN: get_info() is called
result = player.get_info()
# THEN: The info should be correct
expected_info = 'This media player uses your operating system to provide media capabilities.<br/> ' \
'<strong>Audio</strong><br/>[]<br/><strong>Video</strong><br/>[]<br/>'
assert expected_info == result
@patch('openlp.core.ui.media.systemplayer.CheckMediaWorker')
@patch('openlp.core.ui.media.systemplayer.run_thread')
@patch('openlp.core.ui.media.systemplayer.is_thread_finished')
def test_check_media(self, mocked_is_thread_finished, mocked_run_thread, MockCheckMediaWorker):
"""
Test the check_media() method of the SystemPlayer
"""
# GIVEN: A SystemPlayer instance and a mocked thread
valid_file = '/path/to/video.ogv'
mocked_application = MagicMock()
Registry().create()
Registry().register('application', mocked_application)
player = SystemPlayer(self)
mocked_is_thread_finished.side_effect = [False, True]
mocked_check_media_worker = MagicMock()
mocked_check_media_worker.result = True
MockCheckMediaWorker.return_value = mocked_check_media_worker
# WHEN: check_media() is called with a valid media file
result = player.check_media(valid_file)
# THEN: It should return True
MockCheckMediaWorker.assert_called_once_with(valid_file)
mocked_check_media_worker.setVolume.assert_called_once_with(0)
mocked_run_thread.assert_called_once_with(mocked_check_media_worker, 'check_media')
mocked_is_thread_finished.assert_called_with('check_media')
assert mocked_is_thread_finished.call_count == 2, 'is_thread_finished() should have been called twice'
mocked_application.processEvents.assert_called_once_with()
assert result is True
class TestCheckMediaWorker(TestCase):
"""
Test the CheckMediaWorker class
"""
def test_constructor(self):
"""
Test the constructor of the CheckMediaWorker class
"""
# GIVEN: A file path
path = 'file.ogv'
# WHEN: The CheckMediaWorker object is instantiated
worker = CheckMediaWorker(path)
# THEN: The correct values should be set up
assert worker is not None
@patch('openlp.core.ui.media.systemplayer.functools.partial')
@patch('openlp.core.ui.media.systemplayer.QtMultimedia.QMediaContent')
def test_start(self, MockQMediaContent, mocked_partial):
"""
Test the start method
"""
# GIVEN: A CheckMediaWorker instance
worker = CheckMediaWorker('file.ogv')
MockQMediaContent.side_effect = lambda x: x
mocked_partial.side_effect = lambda x, y: y
# WHEN: start() is called
with patch.object(worker, 'error') as mocked_error, \
patch.object(worker, 'mediaStatusChanged') as mocked_status_change, \
patch.object(worker, 'setMedia') as mocked_set_media, \
patch.object(worker, 'play') as mocked_play:
worker.start()
# THEN: The correct methods should be called
mocked_error.connect.assert_called_once_with('error')
mocked_status_change.connect.assert_called_once_with('media')
mocked_set_media.assert_called_once_with(QtCore.QUrl('file:file.ogv'))
mocked_play.assert_called_once_with()
def test_signals_media(self):
"""
Test the signals() signal of the CheckMediaWorker class with a "media" origin
"""
# GIVEN: A CheckMediaWorker instance
worker = CheckMediaWorker('file.ogv')
# WHEN: signals() is called with media and BufferedMedia
with patch.object(worker, 'stop') as mocked_stop, \
patch.object(worker, 'quit') as mocked_quit:
worker.signals('media', worker.BufferedMedia)
# THEN: The worker should exit and the result should be True
mocked_stop.assert_called_once_with()
mocked_quit.emit.assert_called_once_with()
assert worker.result is True
def test_signals_error(self):
"""
Test the signals() signal of the CheckMediaWorker class with a "error" origin
"""
# GIVEN: A CheckMediaWorker instance
worker = CheckMediaWorker('file.ogv')
# WHEN: signals() is called with error and BufferedMedia
with patch.object(worker, 'stop') as mocked_stop, \
patch.object(worker, 'quit') as mocked_quit:
worker.signals('error', None)
# THEN: The worker should exit and the result should be True
mocked_stop.assert_called_once_with()
mocked_quit.emit.assert_called_once_with()
assert worker.result is False

View File

@ -138,25 +138,24 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_vlc = MagicMock() mocked_vlc = MagicMock()
mocked_vlc.Instance.return_value = mocked_instance mocked_vlc.Instance.return_value = mocked_instance
mocked_get_vlc.return_value = mocked_vlc mocked_get_vlc.return_value = mocked_vlc
mocked_display = MagicMock() mocked_output_display = MagicMock()
mocked_display.has_audio = False mocked_controller = MagicMock()
mocked_display.controller.is_live = True mocked_controller.is_live = True
mocked_display.size.return_value = (10, 10) mocked_output_display.size.return_value = (10, 10)
vlc_player = VlcPlayer(None) vlc_player = VlcPlayer(None)
# WHEN: setup() is run # WHEN: setup() is run
vlc_player.setup(mocked_display) vlc_player.setup(mocked_output_display, mocked_controller)
# THEN: The VLC widget should be set up correctly # THEN: The VLC widget should be set up correctly
assert mocked_display.vlc_widget == mocked_qframe assert mocked_output_display.vlc_widget == mocked_qframe
mocked_qframe.setFrameStyle.assert_called_with(1) mocked_qframe.setFrameStyle.assert_called_with(1)
mocked_settings.value.assert_called_with('advanced/hide mouse') mocked_settings.value.assert_called_with('advanced/hide mouse')
mocked_vlc.Instance.assert_called_with('--no-video-title-show --no-audio --no-video-title-show ' mocked_vlc.Instance.assert_called_with('--no-video-title-show --mouse-hide-timeout=0')
'--mouse-hide-timeout=0') assert mocked_output_display.vlc_instance == mocked_instance
assert mocked_display.vlc_instance == mocked_instance
mocked_instance.media_player_new.assert_called_with() mocked_instance.media_player_new.assert_called_with()
assert mocked_display.vlc_media_player == mocked_media_player_new assert mocked_output_display.vlc_media_player == mocked_media_player_new
mocked_display.size.assert_called_with() mocked_output_display.size.assert_called_with()
mocked_qframe.resize.assert_called_with((10, 10)) mocked_qframe.resize.assert_called_with((10, 10))
mocked_qframe.raise_.assert_called_with() mocked_qframe.raise_.assert_called_with()
mocked_qframe.hide.assert_called_with() mocked_qframe.hide.assert_called_with()
@ -188,14 +187,14 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_vlc = MagicMock() mocked_vlc = MagicMock()
mocked_vlc.Instance.return_value = mocked_instance mocked_vlc.Instance.return_value = mocked_instance
mocked_get_vlc.return_value = mocked_vlc mocked_get_vlc.return_value = mocked_vlc
mocked_display = MagicMock() mocked_output_display = MagicMock()
mocked_display.has_audio = True mocked_controller = MagicMock()
mocked_display.controller.is_live = True mocked_controller.is_live = True
mocked_display.size.return_value = (10, 10) mocked_output_display.size.return_value = (10, 10)
vlc_player = VlcPlayer(None) vlc_player = VlcPlayer(None)
# WHEN: setup() is run # WHEN: setup() is run
vlc_player.setup(mocked_display) vlc_player.setup(mocked_output_display, mocked_controller)
# THEN: The VLC instance should be created with the correct options # THEN: The VLC instance should be created with the correct options
mocked_vlc.Instance.assert_called_with('--no-video-title-show --mouse-hide-timeout=0') mocked_vlc.Instance.assert_called_with('--no-video-title-show --mouse-hide-timeout=0')
@ -226,17 +225,17 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_vlc = MagicMock() mocked_vlc = MagicMock()
mocked_vlc.Instance.return_value = mocked_instance mocked_vlc.Instance.return_value = mocked_instance
mocked_get_vlc.return_value = mocked_vlc mocked_get_vlc.return_value = mocked_vlc
mocked_display = MagicMock() mocked_output_display = MagicMock()
mocked_display.has_audio = False mocked_controller = MagicMock()
mocked_display.controller.is_live = True mocked_controller.is_live = True
mocked_display.size.return_value = (10, 10) mocked_output_display.size.return_value = (10, 10)
vlc_player = VlcPlayer(None) vlc_player = VlcPlayer(None)
# WHEN: setup() is run # WHEN: setup() is run
vlc_player.setup(mocked_display) vlc_player.setup(mocked_output_display, mocked_controller)
# THEN: The VLC instance should be created with the correct options # THEN: The VLC instance should be created with the correct options
mocked_vlc.Instance.assert_called_with('--no-video-title-show --no-audio --no-video-title-show') mocked_vlc.Instance.assert_called_with('--no-video-title-show')
@patch('openlp.core.ui.media.vlcplayer.is_win') @patch('openlp.core.ui.media.vlcplayer.is_win')
@patch('openlp.core.ui.media.vlcplayer.is_macosx') @patch('openlp.core.ui.media.vlcplayer.is_macosx')
@ -263,14 +262,14 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_vlc = MagicMock() mocked_vlc = MagicMock()
mocked_vlc.Instance.return_value = mocked_instance mocked_vlc.Instance.return_value = mocked_instance
mocked_get_vlc.return_value = mocked_vlc mocked_get_vlc.return_value = mocked_vlc
mocked_display = MagicMock() mocked_output_display = MagicMock()
mocked_display.has_audio = False mocked_controller = MagicMock()
mocked_display.controller.is_live = True mocked_controller.is_live = True
mocked_display.size.return_value = (10, 10) mocked_output_display.size.return_value = (10, 10)
vlc_player = VlcPlayer(None) vlc_player = VlcPlayer(None)
# WHEN: setup() is run # WHEN: setup() is run
vlc_player.setup(mocked_display) vlc_player.setup(mocked_output_display, mocked_controller)
# THEN: set_hwnd should be called # THEN: set_hwnd should be called
mocked_media_player_new.set_hwnd.assert_called_with(2) mocked_media_player_new.set_hwnd.assert_called_with(2)
@ -300,14 +299,14 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_vlc = MagicMock() mocked_vlc = MagicMock()
mocked_vlc.Instance.return_value = mocked_instance mocked_vlc.Instance.return_value = mocked_instance
mocked_get_vlc.return_value = mocked_vlc mocked_get_vlc.return_value = mocked_vlc
mocked_display = MagicMock() mocked_output_display = MagicMock()
mocked_display.has_audio = False mocked_controller = MagicMock()
mocked_display.controller.is_live = True mocked_controller.is_live = True
mocked_display.size.return_value = (10, 10) mocked_output_display.size.return_value = (10, 10)
vlc_player = VlcPlayer(None) vlc_player = VlcPlayer(None)
# WHEN: setup() is run # WHEN: setup() is run
vlc_player.setup(mocked_display) vlc_player.setup(mocked_output_display, mocked_controller)
# THEN: set_nsobject should be called # THEN: set_nsobject should be called
mocked_media_player_new.set_nsobject.assert_called_with(2) mocked_media_player_new.set_nsobject.assert_called_with(2)
@ -353,15 +352,13 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_normcase.side_effect = lambda x: x mocked_normcase.side_effect = lambda x: x
mocked_vlc = MagicMock() mocked_vlc = MagicMock()
mocked_get_vlc.return_value = mocked_vlc mocked_get_vlc.return_value = mocked_vlc
mocked_controller = MagicMock() mocked_display = MagicMock()
mocked_controller.media_info.volume = 100 mocked_display.media_info.volume = 100
mocked_controller.media_info.media_type = MediaType.Video mocked_display.media_info.media_type = MediaType.Video
mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path mocked_display.media_info.file_info.absoluteFilePath.return_value = media_path
mocked_vlc_media = MagicMock() mocked_vlc_media = MagicMock()
mocked_media = MagicMock() mocked_media = MagicMock()
mocked_media.get_duration.return_value = 10000 mocked_media.get_duration.return_value = 10000
mocked_display = MagicMock()
mocked_display.controller = mocked_controller
mocked_display.vlc_instance.media_new_path.return_value = mocked_vlc_media mocked_display.vlc_instance.media_new_path.return_value = mocked_vlc_media
mocked_display.vlc_media_player.get_media.return_value = mocked_media mocked_display.vlc_media_player.get_media.return_value = mocked_media
vlc_player = VlcPlayer(None) vlc_player = VlcPlayer(None)
@ -392,16 +389,13 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_normcase.side_effect = lambda x: x mocked_normcase.side_effect = lambda x: x
mocked_vlc = MagicMock() mocked_vlc = MagicMock()
mocked_get_vlc.return_value = mocked_vlc mocked_get_vlc.return_value = mocked_vlc
mocked_controller = MagicMock() mocked_display = MagicMock()
mocked_controller.media_info.volume = 100 mocked_display.media_info.volume = 100
mocked_controller.media_info.media_type = MediaType.CD mocked_display.media_info.media_type = MediaType.CD
mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path mocked_display.media_info.title_track = 1
mocked_controller.media_info.title_track = 1
mocked_vlc_media = MagicMock() mocked_vlc_media = MagicMock()
mocked_media = MagicMock() mocked_media = MagicMock()
mocked_media.get_duration.return_value = 10000 mocked_media.get_duration.return_value = 10000
mocked_display = MagicMock()
mocked_display.controller = mocked_controller
mocked_display.vlc_instance.media_new_location.return_value = mocked_vlc_media mocked_display.vlc_instance.media_new_location.return_value = mocked_vlc_media
mocked_display.vlc_media_player.get_media.return_value = mocked_media mocked_display.vlc_media_player.get_media.return_value = mocked_media
mocked_subitems = MagicMock() mocked_subitems = MagicMock()
@ -437,16 +431,14 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_normcase.side_effect = lambda x: x mocked_normcase.side_effect = lambda x: x
mocked_vlc = MagicMock() mocked_vlc = MagicMock()
mocked_get_vlc.return_value = mocked_vlc mocked_get_vlc.return_value = mocked_vlc
mocked_controller = MagicMock() mocked_display = MagicMock()
mocked_controller.media_info.volume = 100 mocked_display.media_info.volume = 100
mocked_controller.media_info.media_type = MediaType.CD mocked_display.media_info.media_type = MediaType.CD
mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path mocked_display.media_info.file_info.absoluteFilePath.return_value = media_path
mocked_controller.media_info.title_track = 1 mocked_display.media_info.title_track = 1
mocked_vlc_media = MagicMock() mocked_vlc_media = MagicMock()
mocked_media = MagicMock() mocked_media = MagicMock()
mocked_media.get_duration.return_value = 10000 mocked_media.get_duration.return_value = 10000
mocked_display = MagicMock()
mocked_display.controller = mocked_controller
mocked_display.vlc_instance.media_new_location.return_value = mocked_vlc_media mocked_display.vlc_instance.media_new_location.return_value = mocked_vlc_media
mocked_display.vlc_media_player.get_media.return_value = mocked_media mocked_display.vlc_media_player.get_media.return_value = mocked_media
mocked_subitems = MagicMock() mocked_subitems = MagicMock()
@ -482,16 +474,14 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_normcase.side_effect = lambda x: x mocked_normcase.side_effect = lambda x: x
mocked_vlc = MagicMock() mocked_vlc = MagicMock()
mocked_get_vlc.return_value = mocked_vlc mocked_get_vlc.return_value = mocked_vlc
mocked_controller = MagicMock() mocked_display = MagicMock()
mocked_controller.media_info.volume = 100 mocked_display.media_info.volume = 100
mocked_controller.media_info.media_type = MediaType.CD mocked_display.media_info.media_type = MediaType.CD
mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path mocked_display.media_info.file_info.absoluteFilePath.return_value = media_path
mocked_controller.media_info.title_track = 1 mocked_display.media_info.title_track = 1
mocked_vlc_media = MagicMock() mocked_vlc_media = MagicMock()
mocked_media = MagicMock() mocked_media = MagicMock()
mocked_media.get_duration.return_value = 10000 mocked_media.get_duration.return_value = 10000
mocked_display = MagicMock()
mocked_display.controller = mocked_controller
mocked_display.vlc_instance.media_new_location.return_value = mocked_vlc_media mocked_display.vlc_instance.media_new_location.return_value = mocked_vlc_media
mocked_display.vlc_media_player.get_media.return_value = mocked_media mocked_display.vlc_media_player.get_media.return_value = mocked_media
mocked_subitems = MagicMock() mocked_subitems = MagicMock()
@ -611,29 +601,28 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_threading.Thread.return_value = mocked_thread mocked_threading.Thread.return_value = mocked_thread
mocked_vlc = MagicMock() mocked_vlc = MagicMock()
mocked_get_vlc.return_value = mocked_vlc mocked_get_vlc.return_value = mocked_vlc
mocked_controller = MagicMock() mocked_display = MagicMock()
mocked_controller.media_info.start_time = 0
mocked_controller.media_info.media_type = MediaType.Video
mocked_controller.media_info.volume = 100
mocked_media = MagicMock() mocked_media = MagicMock()
mocked_media.get_duration.return_value = 50000 mocked_media.get_duration.return_value = 50000
mocked_display = MagicMock() mocked_output_display = MagicMock()
mocked_display.controller = mocked_controller mocked_output_display.media_info.start_time = 0
mocked_display.vlc_media_player.get_media.return_value = mocked_media mocked_output_display.media_info.media_type = MediaType.Video
mocked_output_display.media_info.volume = 100
mocked_output_display.vlc_media_player.get_media.return_value = mocked_media
vlc_player = VlcPlayer(None) vlc_player = VlcPlayer(None)
vlc_player.set_state(MediaState.Paused, mocked_display) vlc_player.set_state(MediaState.Paused, mocked_output_display)
# WHEN: play() is called # WHEN: play() is called
with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \ with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \
patch.object(vlc_player, 'volume') as mocked_volume: patch.object(vlc_player, 'volume') as mocked_volume:
mocked_media_state_wait.return_value = True mocked_media_state_wait.return_value = True
result = vlc_player.play(mocked_display) result = vlc_player.play(mocked_display, mocked_output_display)
# THEN: A bunch of things should happen to play the media # THEN: A bunch of things should happen to play the media
mocked_thread.start.assert_called_with() mocked_thread.start.assert_called_with()
mocked_volume.assert_called_with(mocked_display, 100) mocked_volume.assert_called_with(mocked_output_display, 100)
assert MediaState.Playing == vlc_player.get_live_state() assert MediaState.Playing == vlc_player.get_live_state()
mocked_display.vlc_widget.raise_.assert_called_with() mocked_output_display.vlc_widget.raise_.assert_called_with()
assert result is True, 'The value returned from play() should be True' assert result is True, 'The value returned from play() should be True'
@patch('openlp.core.ui.media.vlcplayer.threading') @patch('openlp.core.ui.media.vlcplayer.threading')
@ -649,16 +638,15 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_get_vlc.return_value = mocked_vlc mocked_get_vlc.return_value = mocked_vlc
mocked_controller = MagicMock() mocked_controller = MagicMock()
mocked_controller.media_info.start_time = 0 mocked_controller.media_info.start_time = 0
mocked_display = MagicMock() mocked_output_display = MagicMock()
mocked_display.controller = mocked_controller
vlc_player = VlcPlayer(None) vlc_player = VlcPlayer(None)
vlc_player.set_state(MediaState.Paused, mocked_display) vlc_player.set_state(MediaState.Paused, mocked_output_display)
# WHEN: play() is called # WHEN: play() is called
with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \ with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \
patch.object(vlc_player, 'volume'): patch.object(vlc_player, 'volume'):
mocked_media_state_wait.return_value = False mocked_media_state_wait.return_value = False
result = vlc_player.play(mocked_display) result = vlc_player.play(mocked_controller, mocked_output_display)
# THEN: A thread should be started, but the method should return False # THEN: A thread should be started, but the method should return False
mocked_thread.start.assert_called_with() mocked_thread.start.assert_called_with()
@ -676,33 +664,32 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_vlc = MagicMock() mocked_vlc = MagicMock()
mocked_get_vlc.return_value = mocked_vlc mocked_get_vlc.return_value = mocked_vlc
mocked_controller = MagicMock() mocked_controller = MagicMock()
mocked_controller.media_info.start_time = 0 mocked_output_display = MagicMock()
mocked_controller.media_info.end_time = 50 mocked_output_display.media_info.start_time = 0
mocked_controller.media_info.media_type = MediaType.DVD mocked_output_display.media_info.end_time = 50
mocked_controller.media_info.volume = 100 mocked_output_display.media_info.media_type = MediaType.DVD
mocked_controller.media_info.title_track = 1 mocked_output_display.media_info.volume = 100
mocked_controller.media_info.audio_track = 1 mocked_output_display.media_info.title_track = 1
mocked_controller.media_info.subtitle_track = 1 mocked_output_display.media_info.audio_track = 1
mocked_display = MagicMock() mocked_output_display.media_info.subtitle_track = 1
mocked_display.controller = mocked_controller
vlc_player = VlcPlayer(None) vlc_player = VlcPlayer(None)
vlc_player.set_state(MediaState.Paused, mocked_display) vlc_player.set_state(MediaState.Paused, mocked_output_display)
# WHEN: play() is called # WHEN: play() is called
with patch.object(vlc_player, 'media_state_wait', return_value=True), \ with patch.object(vlc_player, 'media_state_wait', return_value=True), \
patch.object(vlc_player, 'volume') as mocked_volume, \ patch.object(vlc_player, 'volume') as mocked_volume, \
patch.object(vlc_player, 'get_live_state', return_value=MediaState.Loaded): patch.object(vlc_player, 'get_live_state', return_value=MediaState.Loaded):
result = vlc_player.play(mocked_display) result = vlc_player.play(mocked_controller, mocked_output_display)
# THEN: A bunch of things should happen to play the media # THEN: A bunch of things should happen to play the media
mocked_thread.start.assert_called_with() mocked_thread.start.assert_called_with()
mocked_display.vlc_media_player.set_title.assert_called_with(1) mocked_output_display.vlc_media_player.set_title.assert_called_with(1)
mocked_display.vlc_media_player.play.assert_called_with() mocked_output_display.vlc_media_player.play.assert_called_with()
mocked_display.vlc_media_player.audio_set_track.assert_called_with(1) mocked_output_display.vlc_media_player.audio_set_track.assert_called_with(1)
mocked_display.vlc_media_player.video_set_spu.assert_called_with(1) mocked_output_display.vlc_media_player.video_set_spu.assert_called_with(1)
mocked_volume.assert_called_with(mocked_display, 100) mocked_volume.assert_called_with(mocked_output_display, 100)
assert MediaState.Playing == vlc_player.get_live_state() assert MediaState.Playing == vlc_player.get_live_state()
mocked_display.vlc_widget.raise_.assert_called_with() mocked_output_display.vlc_widget.raise_.assert_called_with()
assert result is True, 'The value returned from play() should be True' assert result is True, 'The value returned from play() should be True'
@patch('openlp.core.ui.media.vlcplayer.get_vlc') @patch('openlp.core.ui.media.vlcplayer.get_vlc')
@ -937,7 +924,6 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_controller.media_info.end_time = 300 mocked_controller.media_info.end_time = 300
mocked_controller.seek_slider.isSliderDown.return_value = False mocked_controller.seek_slider.isSliderDown.return_value = False
mocked_display = MagicMock() mocked_display = MagicMock()
mocked_display.controller = mocked_controller
mocked_display.vlc_media.get_state.return_value = 1 mocked_display.vlc_media.get_state.return_value = 1
mocked_display.vlc_media_player.get_time.return_value = 400000 mocked_display.vlc_media_player.get_time.return_value = 400000
vlc_player = VlcPlayer(None) vlc_player = VlcPlayer(None)
@ -945,7 +931,7 @@ class TestVLCPlayer(TestCase, TestMixin):
# WHEN: update_ui() is called # WHEN: update_ui() is called
with patch.object(vlc_player, 'stop') as mocked_stop, \ with patch.object(vlc_player, 'stop') as mocked_stop, \
patch.object(vlc_player, 'set_visible') as mocked_set_visible: patch.object(vlc_player, 'set_visible') as mocked_set_visible:
vlc_player.update_ui(mocked_display) vlc_player.update_ui(mocked_controller, mocked_display)
# THEN: Certain methods should be called # THEN: Certain methods should be called
mocked_stop.assert_called_with(mocked_display) mocked_stop.assert_called_with(mocked_display)
@ -970,22 +956,19 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_controller.media_info.end_time = 300 mocked_controller.media_info.end_time = 300
mocked_controller.seek_slider.isSliderDown.return_value = False mocked_controller.seek_slider.isSliderDown.return_value = False
mocked_display = MagicMock() mocked_display = MagicMock()
mocked_display.controller = mocked_controller
mocked_display.vlc_media.get_state.return_value = 1 mocked_display.vlc_media.get_state.return_value = 1
mocked_display.vlc_media_player.get_time.return_value = 400 mocked_display.vlc_media_player.get_time.return_value = 300
mocked_display.controller.media_info.media_type = MediaType.DVD mocked_display.controller.media_info.media_type = MediaType.DVD
vlc_player = VlcPlayer(None) vlc_player = VlcPlayer(None)
# WHEN: update_ui() is called # WHEN: update_ui() is called
with patch.object(vlc_player, 'stop') as mocked_stop, \ with patch.object(vlc_player, 'stop') as mocked_stop:
patch.object(vlc_player, 'set_visible') as mocked_set_visible: vlc_player.update_ui(mocked_controller, mocked_display)
vlc_player.update_ui(mocked_display)
# THEN: Certain methods should be called # THEN: Certain methods should be called
mocked_stop.assert_called_with(mocked_display) mocked_stop.assert_called_with(mocked_display)
assert 2 == mocked_stop.call_count assert 1 == mocked_stop.call_count
mocked_display.vlc_media_player.get_time.assert_called_with() mocked_display.vlc_media_player.get_time.assert_called_with()
mocked_set_visible.assert_called_with(mocked_display, False)
mocked_controller.seek_slider.setSliderPosition.assert_called_with(300) mocked_controller.seek_slider.setSliderPosition.assert_called_with(300)
expected_calls = [call(True), call(False)] expected_calls = [call(True), call(False)]
assert expected_calls == mocked_controller.seek_slider.blockSignals.call_args_list assert expected_calls == mocked_controller.seek_slider.blockSignals.call_args_list