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*
*eric[1-9]project
.git
env
# Git files
.gitignore
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!
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__ = {
'settings/version': 0,
'advanced/add page break': False,
@ -185,6 +188,7 @@ class Settings(QtCore.QSettings):
'core/click live slide to unblank': False,
'core/blank warning': False,
'core/ccli number': '',
'core/experimental': False,
'core/has run wizard': False,
'core/language': '[en]',
'core/last version test': '',
@ -202,13 +206,13 @@ class Settings(QtCore.QSettings):
'core/view mode': 'default',
# The other display settings (display position and dimensions) are defined in the ScreenList class due to a
# circular dependency.
'core/display on monitor': True,
'core/display on monitor': on_monitor_default,
'core/override position': False,
'core/monitor': {},
'core/application version': '0.0',
'images/background color': '#000000',
'media/players': 'system,webkit',
'media/override player': QtCore.Qt.Unchecked,
'media/media auto start': QtCore.Qt.Unchecked,
'media/stream command': '',
'remotes/download version': '0.0',
'players/background color': '#000000',
'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 address', '', []),
('bibles/proxy username', '', []),
('bibles/proxy password', '', [])
('bibles/proxy password', '', []),
('media/players', '', []),
('media/override player', '', [])
]
@staticmethod

View File

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

View File

@ -48,6 +48,7 @@ class MediaType(object):
CD = 3
DVD = 4
Folder = 5
Stream = 6
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
related to playing media, such as sliders.
"""
import datetime
import logging
try:
@ -33,7 +32,7 @@ except ImportError:
pymediainfo_available = False
from subprocess import check_output
from PyQt5 import QtCore, QtWidgets
from PyQt5 import QtCore
from openlp.core.state import State
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.ui import critical_error_message_box
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.endpoint import media_endpoint
from openlp.core.ui.media.vlcplayer import VlcPlayer, get_vlc
from openlp.core.widgets.toolbar import OpenLPToolbar
log = logging.getLogger(__name__)
@ -56,45 +53,6 @@ log = logging.getLogger(__name__)
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):
"""
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):
self.vlc_player = None
self.display_controllers = {}
self.current_media_players = {}
# Timer for video state
self.live_timer = QtCore.QTimer()
@ -168,117 +125,71 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
self.setup()
self.vlc_player = VlcPlayer(self)
State().add_service("mediacontroller", 0)
State().add_service("media_live", 0, requires="mediacontroller")
if get_vlc() and pymediainfo_available:
State().update_pre_conditions("mediacontroller", True)
State().update_pre_conditions('media_live', True)
else:
State().missing_text("mediacontroller", translate('OpenLP.SlideController',
"VLC or pymediainfo are missing, so you are unable to play any media"))
self._generate_extensions_lists()
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):
"""
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:
self.current_media_players[DisplayControllerType.Live].resize(display)
self.current_media_players[DisplayControllerType.Live].update_ui(display)
self.tick(self.display_controllers[DisplayControllerType.Live])
self.current_media_players[DisplayControllerType.Live].update_ui(self.live_controller, display)
self.tick(self.display_controllers(DisplayControllerType.Live))
if self.current_media_players[DisplayControllerType.Live].get_live_state() is not MediaState.Playing:
self.live_timer.stop()
else:
self.live_timer.stop()
self.media_stop(self.display_controllers[DisplayControllerType.Live])
if self.display_controllers[DisplayControllerType.Live].media_info.can_loop_playback:
self.media_play(self.display_controllers[DisplayControllerType.Live], True)
self.media_stop(self.display_controllers(DisplayControllerType.Live))
if self.display_controllers(DisplayControllerType.Live).media_info.can_loop_playback:
self.media_play(self.display_controllers(DisplayControllerType.Live), True)
def media_state_preview(self):
"""
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:
self.current_media_players[DisplayControllerType.Preview].resize(display)
self.current_media_players[DisplayControllerType.Preview].update_ui(display)
self.tick(self.display_controllers[DisplayControllerType.Preview])
self.current_media_players[DisplayControllerType.Preview].update_ui(self.preview_controller, display)
self.tick(self.display_controllers(DisplayControllerType.Preview))
if self.current_media_players[DisplayControllerType.Preview].get_preview_state() is not MediaState.Playing:
self.preview_timer.stop()
else:
self.preview_timer.stop()
self.media_stop(self.display_controllers[DisplayControllerType.Preview])
if self.display_controllers[DisplayControllerType.Preview].media_info.can_loop_playback:
self.media_play(self.display_controllers[DisplayControllerType.Preview], True)
def 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)
self.media_stop(self.display_controllers(DisplayControllerType.Preview))
if self.display_controllers(DisplayControllerType.Preview).media_info.can_loop_playback:
self.media_play(self.display_controllers(DisplayControllerType.Preview), True)
def setup_display(self, display, preview):
"""
@ -287,14 +198,13 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
:param display: Display on which the output is to be played
:param preview: Whether the display is a main or preview display
"""
# clean up possible running old media files
self.finalise()
display.media_info = ItemMediaInfo()
display.has_audio = True
if display.is_live and preview:
return
# if display.is_live and preview:
# return
if preview:
display.has_audio = False
self.vlc_player.setup(display)
self.vlc_player.setup(display, preview)
def set_controls_visible(self, controller, value):
"""
@ -305,9 +215,9 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
"""
# Generic controls
controller.mediabar.setVisible(value)
if controller.is_live and controller.display:
if self.current_media_players and value:
controller.display.set_transparency(False)
# if controller.is_live and controller.display:
# if self.current_media_players and value:
# controller.display.set_transparency(False)
@staticmethod
def resize(display, player):
@ -319,7 +229,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
"""
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
@ -329,7 +239,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
:param video_behind_text: Is the video to be played behind text.
"""
is_valid = True
controller = self.display_controllers[source]
controller = self.display_controllers(source)
# stop running videos
self.media_reset(controller)
controller.media_info = ItemMediaInfo()
@ -354,8 +264,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
log.debug('video is not optical and live')
controller.media_info.length = service_item.media_length
is_valid = self._check_file_type(controller, display)
display.override['theme'] = ''
display.override['video'] = True
# display.override['theme'] = ''
# display.override['video'] = True
if controller.media_info.is_background:
# ignore start/end time
controller.media_info.start_time = 0
@ -379,10 +289,10 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'),
translate('MediaPlugin.MediaItem', 'Unsupported File'))
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
if controller.is_live and not controller.media_info.is_background:
display.frame.runJavaScript('show_video("setBackBoard", null, null,"visible");')
# if controller.is_live and not controller.media_info.is_background:
# display.frame.runJavaScript('show_video("setBackBoard", null, null,"visible");')
# now start playing - Preview is autoplay!
autoplay = False
# Preview requested
@ -471,28 +381,26 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
for file in controller.media_info.file_info:
if file.is_file:
suffix = '*%s' % file.suffix.lower()
player = self.vlc_player
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 \
player.can_background:
self.resize(display, player)
if player.load(display, file):
self.current_media_players[controller.controller_type] = player
self.vlc_player.can_background:
self.resize(display, self.vlc_player)
if self.vlc_player.load(display, file):
self.current_media_players[controller.controller_type] = self.vlc_player
controller.media_info.media_type = MediaType.Video
return True
if suffix in player.audio_extensions_list:
if player.load(display, file):
self.current_media_players[controller.controller_type] = player
if suffix in self.vlc_player.audio_extensions_list:
if self.vlc_player.load(display, file):
self.current_media_players[controller.controller_type] = self.vlc_player
controller.media_info.media_type = MediaType.Audio
return True
else:
player = self.vlc_player
file = str(file)
if player.can_folder:
self.resize(display, player)
if player.load(display, file):
self.current_media_players[controller.controller_type] = player
if self.vlc_player.can_folder:
self.resize(display, self.vlc_player)
if self.vlc_player.load(display, file):
self.current_media_players[controller.controller_type] = self.vlc_player
controller.media_info.media_type = MediaType.Video
return True
return False
@ -509,8 +417,6 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
def on_media_play(self):
"""
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)
@ -524,7 +430,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
controller.seek_slider.blockSignals(True)
controller.volume_slider.blockSignals(True)
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.volume_slider.blockSignals(False)
return False
@ -533,8 +439,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
else:
self.media_volume(controller, controller.media_info.volume)
if first_time:
if not controller.media_info.is_background:
display.frame.runJavaScript('show_blank("desktop");')
# if not controller.media_info.is_background:
# display.frame.runJavaScript('show_blank("desktop");')
self.current_media_players[controller.controller_type].set_visible(display, True)
controller.mediabar.actions['playbackPlay'].setVisible(False)
controller.mediabar.actions['playbackPause'].setVisible(True)
@ -591,8 +497,6 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
def on_media_pause(self):
"""
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'))
@ -639,8 +543,6 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
def on_media_stop(self):
"""
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'))
@ -653,8 +555,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
"""
display = self._define_display(controller)
if controller.controller_type in self.current_media_players:
if not looping_background:
display.frame.runJavaScript('show_blank("black");')
# if not looping_background:
# display.frame.runJavaScript('show_blank("black");')
self.current_media_players[controller.controller_type].stop(display)
self.current_media_players[controller.controller_type].set_visible(display, False)
controller.seek_slider.setSliderPosition(0)
@ -725,7 +627,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
display.override = {}
self.current_media_players[controller.controller_type].reset(display)
self.current_media_players[controller.controller_type].set_visible(display, False)
display.frame.runJavaScript('show_video("setBackBoard", null, null, "hidden");')
# display.frame.runJavaScript('show_video("setBackBoard", null, null, "hidden");')
del self.current_media_players[controller.controller_type]
def media_hide(self, msg):
@ -788,8 +690,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
"""
self.live_timer.stop()
self.preview_timer.stop()
for controller in self.display_controllers:
self.media_reset(self.display_controllers[controller])
self.media_reset(self.display_controllers(DisplayControllerType.Live))
self.media_reset(self.display_controllers(DisplayControllerType.Preview))
@staticmethod
def _define_display(controller):

View File

@ -52,11 +52,12 @@ class MediaPlayer(RegistryProperties):
"""
return False
def setup(self, display):
def setup(self, display, live_display):
"""
Create the related widgets for the current display
:param display: The display to be updated.
:param live_display: Is the display a live one.
"""
pass
@ -78,10 +79,11 @@ class MediaPlayer(RegistryProperties):
"""
pass
def play(self, display):
def play(self, controller, display):
"""
Starts playing of current Media File
:param controller: Which Controller is running the show.
:param display: The display to be updated.
"""
pass
@ -206,7 +208,7 @@ class MediaPlayer(RegistryProperties):
:param display: Identify the Display type
:return: None
"""
if display.controller.is_live:
if display.is_display:
self.set_live_state(state)
else:
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.video_extensions_list = VIDEO_EXT
def setup(self, display):
def setup(self, output_display, live_display):
"""
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:
"""
vlc = get_vlc()
display.vlc_widget = QtWidgets.QFrame(display)
display.vlc_widget.setFrameStyle(QtWidgets.QFrame.NoFrame)
output_display.vlc_widget = QtWidgets.QFrame(output_display)
output_display.vlc_widget.setFrameStyle(QtWidgets.QFrame.NoFrame)
# creating a basic vlc instance
command_line_options = '--no-video-title-show'
if not display.has_audio:
command_line_options += ' --no-audio --no-video-title-show'
if Settings().value('advanced/hide mouse') and display.controller.is_live:
if Settings().value('advanced/hide mouse') and live_display:
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
display.vlc_media_player = display.vlc_instance.media_player_new()
display.vlc_widget.resize(display.size())
display.vlc_widget.raise_()
display.vlc_widget.hide()
output_display.vlc_media_player = output_display.vlc_instance.media_player_new()
output_display.vlc_widget.resize(output_display.size())
output_display.vlc_widget.raise_()
output_display.vlc_widget.hide()
# The media player has to be 'connected' to the QFrame.
# (otherwise a video would be displayed in it's own window)
# This is platform specific!
# You have to give the id of the QFrame (or similar object)
# to vlc, different platforms have different functions for this.
win_id = int(display.vlc_widget.winId())
win_id = int(output_display.vlc_widget.winId())
if is_win():
display.vlc_media_player.set_hwnd(win_id)
output_display.vlc_media_player.set_hwnd(win_id)
elif is_macosx():
# We have to use 'set_nsobject' since Qt5 on OSX uses Cocoa
# framework and not the old Carbon.
display.vlc_media_player.set_nsobject(win_id)
output_display.vlc_media_player.set_nsobject(win_id)
else:
# 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
def check_available(self):
@ -197,43 +196,45 @@ class VlcPlayer(MediaPlayer):
"""
return get_vlc() is not None
def load(self, display, file):
def load(self, output_display, file):
"""
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
:return:
"""
vlc = get_vlc()
log.debug('load vid in Vlc Controller')
controller = display.controller
controller = output_display
volume = controller.media_info.volume
path = os.path.normcase(file)
# create the media
if controller.media_info.media_type == MediaType.CD:
if is_win():
path = '/' + path
display.vlc_media = display.vlc_instance.media_new_location('cdda://' + path)
display.vlc_media_player.set_media(display.vlc_media)
display.vlc_media_player.play()
output_display.vlc_media = output_display.vlc_instance.media_new_location('cdda://' + path)
output_display.vlc_media_player.set_media(output_display.vlc_media)
output_display.vlc_media_player.play()
# 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
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:
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:
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
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
display.vlc_media.parse()
self.volume(display, volume)
output_display.vlc_media.parse()
self.volume(output_display, volume)
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 no longer than 60 seconds. (loading an iso file needs a long time)
@ -244,171 +245,171 @@ class VlcPlayer(MediaPlayer):
"""
vlc = get_vlc()
start = datetime.now()
while media_state != display.vlc_media.get_state():
if display.vlc_media.get_state() == vlc.State.Error:
while media_state != output_display.vlc_media.get_state():
if output_display.vlc_media.get_state() == vlc.State.Error:
return False
self.application.process_events()
if (datetime.now() - start).seconds > 60:
return False
return True
def resize(self, display):
def resize(self, output_display):
"""
Resize the player
:param display: The display where the media is
:param output_display: The display where the media is
: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
: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:
"""
vlc = get_vlc()
controller = display.controller
start_time = 0
log.debug('vlc play')
if display.controller.is_live:
if self.get_live_state() != MediaState.Paused and controller.media_info.start_time > 0:
start_time = controller.media_info.start_time
if output_display.is_display:
if self.get_live_state() != MediaState.Paused and output_display.media_info.start_time > 0:
start_time = output_display.media_info.start_time
else:
if self.get_preview_state() != MediaState.Paused and controller.media_info.start_time > 0:
start_time = controller.media_info.start_time
threading.Thread(target=display.vlc_media_player.play).start()
if not self.media_state_wait(display, vlc.State.Playing):
if self.get_preview_state() != MediaState.Paused and output_display.media_info.start_time > 0:
start_time = output_display.media_info.start_time
threading.Thread(target=output_display.vlc_media_player.play).start()
if not self.media_state_wait(output_display, vlc.State.Playing):
return False
if display.controller.is_live:
if self.get_live_state() != MediaState.Paused and controller.media_info.start_time > 0:
if output_display.is_display:
if self.get_live_state() != MediaState.Paused and output_display.media_info.start_time > 0:
log.debug('vlc play, start time set')
start_time = controller.media_info.start_time
start_time = output_display.media_info.start_time
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')
start_time = controller.media_info.start_time
log.debug('mediatype: ' + str(controller.media_info.media_type))
start_time = output_display.media_info.start_time
log.debug('mediatype: ' + str(output_display.media_info.media_type))
# 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:
log.debug('vlc play, playing started')
if controller.media_info.title_track > 0:
log.debug('vlc play, title_track set: ' + str(controller.media_info.title_track))
display.vlc_media_player.set_title(controller.media_info.title_track)
display.vlc_media_player.play()
if not self.media_state_wait(display, vlc.State.Playing):
if output_display.media_info.title_track > 0:
log.debug('vlc play, title_track set: ' + str(output_display.media_info.title_track))
output_display.vlc_media_player.set_title(output_display.media_info.title_track)
output_display.vlc_media_player.play()
if not self.media_state_wait(output_display, vlc.State.Playing):
return False
if controller.media_info.audio_track > 0:
display.vlc_media_player.audio_set_track(controller.media_info.audio_track)
log.debug('vlc play, audio_track set: ' + str(controller.media_info.audio_track))
if controller.media_info.subtitle_track > 0:
display.vlc_media_player.video_set_spu(controller.media_info.subtitle_track)
log.debug('vlc play, subtitle_track set: ' + str(controller.media_info.subtitle_track))
if controller.media_info.start_time > 0:
log.debug('vlc play, starttime set: ' + str(controller.media_info.start_time))
start_time = controller.media_info.start_time
controller.media_info.length = controller.media_info.end_time - controller.media_info.start_time
self.volume(display, controller.media_info.volume)
if start_time > 0 and display.vlc_media_player.is_seekable():
display.vlc_media_player.set_time(int(start_time))
controller.seek_slider.setMaximum(controller.media_info.length)
self.set_state(MediaState.Playing, display)
display.vlc_widget.raise_()
if output_display.media_info.audio_track > 0:
output_display.vlc_media_player.audio_set_track(output_display.media_info.audio_track)
log.debug('vlc play, audio_track set: ' + str(output_display.media_info.audio_track))
if output_display.media_info.subtitle_track > 0:
output_display.vlc_media_player.video_set_spu(output_display.media_info.subtitle_track)
log.debug('vlc play, subtitle_track set: ' + str(output_display.media_info.subtitle_track))
if output_display.media_info.start_time > 0:
log.debug('vlc play, starttime set: ' + str(output_display.media_info.start_time))
start_time = output_display.media_info.start_time
output_display.media_info.length = output_display.media_info.end_time - output_display.media_info.start_time
self.volume(output_display, output_display.media_info.volume)
if start_time > 0 and output_display.vlc_media_player.is_seekable():
output_display.vlc_media_player.set_time(int(start_time))
controller.seek_slider.setMaximum(output_display.media_info.length)
self.set_state(MediaState.Playing, output_display)
output_display.vlc_widget.raise_()
return True
def pause(self, display):
def pause(self, output_display):
"""
Pause the current item
:param display: The display where the media is
:param output_display: The display where the media is
:return:
"""
vlc = get_vlc()
if display.vlc_media.get_state() != vlc.State.Playing:
if output_display.vlc_media.get_state() != vlc.State.Playing:
return
display.vlc_media_player.pause()
if self.media_state_wait(display, vlc.State.Paused):
self.set_state(MediaState.Paused, display)
output_display.vlc_media_player.pause()
if self.media_state_wait(output_display, vlc.State.Paused):
self.set_state(MediaState.Paused, output_display)
def stop(self, display):
def stop(self, output_display):
"""
Stop the current item
:param display: The display where the media is
:param output_display: The display where the media is
:return:
"""
threading.Thread(target=display.vlc_media_player.stop).start()
self.set_state(MediaState.Stopped, display)
threading.Thread(target=output_display.vlc_media_player.stop).start()
self.set_state(MediaState.Stopped, output_display)
def volume(self, display, vol):
def volume(self, output_display, vol):
"""
Set the volume
: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:
"""
if display.has_audio:
display.vlc_media_player.audio_set_volume(vol)
if output_display.has_audio:
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
: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 \
or display.controller.media_info.media_type == MediaType.DVD:
seek_value += int(display.controller.media_info.start_time)
if display.vlc_media_player.is_seekable():
display.vlc_media_player.set_time(seek_value)
if output_display.controller.media_info.media_type == MediaType.CD \
or output_display.controller.media_info.media_type == MediaType.DVD:
seek_value += int(output_display.controller.media_info.start_time)
if output_display.vlc_media_player.is_seekable():
output_display.vlc_media_player.set_time(seek_value)
def reset(self, display):
def reset(self, output_display):
"""
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()
display.vlc_widget.setVisible(False)
self.set_state(MediaState.Off, display)
output_display.vlc_media_player.stop()
output_display.vlc_widget.setVisible(False)
self.set_state(MediaState.Off, output_display)
def set_visible(self, display, status):
def set_visible(self, output_display, status):
"""
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
"""
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
: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()
# Stop video if playback is finished.
if display.vlc_media.get_state() == vlc.State.Ended:
self.stop(display)
controller = display.controller
if output_display.vlc_media.get_state() == vlc.State.Ended:
self.stop(output_display)
if controller.media_info.end_time > 0:
if display.vlc_media_player.get_time() > controller.media_info.end_time:
self.stop(display)
self.set_visible(display, False)
if output_display.vlc_media_player.get_time() > controller.media_info.end_time:
self.stop(output_display)
self.set_visible(output_display, False)
if not controller.seek_slider.isSliderDown():
controller.seek_slider.blockSignals(True)
if display.controller.media_info.media_type == MediaType.CD \
or display.controller.media_info.media_type == MediaType.DVD:
if controller.media_info.media_type == MediaType.CD \
or controller.media_info.media_type == MediaType.DVD:
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:
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)
def get_info(self):

View File

@ -41,7 +41,7 @@ class ScreensTab(SettingsTab):
"""
Initialise the screen settings tab
"""
self.icon_path = UiIcons().settings
self.icon_path = UiIcons().desktop
screens_translated = translate('OpenLP.ScreensTab', 'Screens')
super(ScreensTab, self).__init__(parent, 'Screens', screens_translated)
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.common.mixins import RegistryProperties
from openlp.core.common.registry import Registry
from openlp.core.common.settings import Settings
from openlp.core.lib import build_icon
from openlp.core.projectors.tab import ProjectorTab
from openlp.core.ui.advancedtab import AdvancedTab
from openlp.core.ui.generaltab import GeneralTab
from openlp.core.ui.screenstab import ScreensTab
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
@ -78,8 +79,8 @@ class SettingsForm(QtWidgets.QDialog, Ui_SettingsDialog, RegistryProperties):
self.insert_tab(self.advanced_tab)
self.insert_tab(self.screens_tab)
self.insert_tab(self.themes_tab)
self.insert_tab(self.advanced_tab)
# self.insert_tab(self.player_tab)
if Settings().value('core/experimental'):
self.insert_tab(self.player_tab)
self.insert_tab(self.projector_tab)
self.insert_tab(self.api_tab)
for plugin in State().list_plugins():
@ -160,15 +161,17 @@ class SettingsForm(QtWidgets.QDialog, Ui_SettingsDialog, RegistryProperties):
self.themes_tab = ThemesTab(self)
self.projector_tab = ProjectorTab(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.screens_tab = ScreensTab(self)
except Exception as e:
print(e)
# Post setup
self.general_tab.post_set_up()
self.themes_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()
for plugin in State().list_plugins():
if plugin.settings_tab:

View File

@ -23,6 +23,7 @@
The :mod:`slidecontroller` module contains the most important part of OpenLP - the slide controller
"""
import copy
import datetime
from collections import deque
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):
"""
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'),
triggers=self.on_clear)
self.controller_layout.addWidget(self.toolbar)
# Build the Media Toolbar
self.media_controller.register_controller(self)
# Build a Media ToolBar
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:
# Build the Song 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)
# The SlidePreview's ratio.
# TODO: Need to basically update everything
def __add_actions_to_widget(self, 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
"""
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)
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:
self.preview_display.show()
@ -1491,7 +1580,7 @@ class PreviewController(RegistryBase, SlideController):
self.type_prefix = 'preview'
self.category = 'Preview Toolbar'
def bootstrap_post_set_up(self):
def bootstrap_initialise(self):
"""
process the bootstrap post setup request
"""
@ -1523,7 +1612,7 @@ class LiveController(RegistryBase, SlideController):
self.category = UiStrings().LiveToolbar
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
"""

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_path_edit.path = self.theme.background_filename
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):
self.setField('background_type', 3)
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.setObjectName('background_label')
self.background_combo_box = QtWidgets.QComboBox(self.background_page)
self.background_combo_box.addItems(['', '', '', '', ''])
self.background_combo_box.addItems(['', '', '', '', '', ''])
self.background_combo_box.setObjectName('background_combo_box')
self.background_type_layout.addRow(self.background_label, self.background_combo_box)
self.background_type_layout.setItem(1, QtWidgets.QFormLayout.LabelRole, self.spacer)
@ -410,6 +410,8 @@ class Ui_ThemeWizard(object):
self.background_combo_box.setItemText(BackgroundType.Video, UiStrings().Video)
self.background_combo_box.setItemText(BackgroundType.Transparent,
translate('OpenLP.ThemeWizard', 'Transparent'))
self.background_combo_box.setItemText(BackgroundType.Stream,
translate('OpenLP.ThemeWizard', 'Live Stream'))
self.color_label.setText(translate('OpenLP.ThemeWizard', 'color:'))
self.gradient_start_label.setText(translate('OpenLP.ThemeWizard', 'Starting color:'))
self.gradient_end_label.setText(translate('OpenLP.ThemeWizard', 'Ending color:'))

View File

@ -203,7 +203,10 @@ class ListPreviewWidget(QtWidgets.QTableWidget, RegistryProperties):
if self.service_item.is_capable(ItemCapabilities.HasThumbnails):
pixmap = QtGui.QPixmap(remove_url_prefix(slide['thumbnail']))
else:
pixmap = QtGui.QPixmap(remove_url_prefix(slide['image']))
if isinstance(slide['image'], QtGui.QIcon):
pixmap = slide['image'].pixmap(QtCore.QSize(32, 32))
else:
pixmap = QtGui.QPixmap(remove_url_prefix(slide['image']))
else:
pixmap = QtGui.QPixmap(remove_url_prefix(slide['path']))
label.setPixmap(pixmap)

View File

@ -116,6 +116,8 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
self.can_preview = False
self.can_make_live = 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):
"""
@ -264,7 +266,8 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
:param media: The media
: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:
track_info = QtCore.QFileInfo(track)
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
from PyQt5 import QtCore
from openlp.core.state import State
from openlp.core.api.http import register_endpoint
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.plugins.media.endpoint import api_media_endpoint, media_endpoint
from openlp.plugins.media.lib.mediaitem import MediaMediaItem
from openlp.plugins.media.lib.mediatab import MediaTab
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.
__default_settings__ = {
'media/media auto start': QtCore.Qt.Unchecked,
'media/media files': [],
'media/last directory': None
}
@ -78,15 +74,6 @@ class MediaPlugin(Plugin):
"""
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
def about():
"""

View File

@ -139,13 +139,13 @@ class TestCommonFunctions(TestCase):
Test `path_to_module` when supplied with 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
result = path_to_module(path)
# 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):
"""

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.Instance.return_value = mocked_instance
mocked_get_vlc.return_value = mocked_vlc
mocked_display = MagicMock()
mocked_display.has_audio = False
mocked_display.controller.is_live = True
mocked_display.size.return_value = (10, 10)
mocked_output_display = MagicMock()
mocked_controller = MagicMock()
mocked_controller.is_live = True
mocked_output_display.size.return_value = (10, 10)
vlc_player = VlcPlayer(None)
# 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
assert mocked_display.vlc_widget == mocked_qframe
assert mocked_output_display.vlc_widget == mocked_qframe
mocked_qframe.setFrameStyle.assert_called_with(1)
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 '
'--mouse-hide-timeout=0')
assert mocked_display.vlc_instance == mocked_instance
mocked_vlc.Instance.assert_called_with('--no-video-title-show --mouse-hide-timeout=0')
assert mocked_output_display.vlc_instance == mocked_instance
mocked_instance.media_player_new.assert_called_with()
assert mocked_display.vlc_media_player == mocked_media_player_new
mocked_display.size.assert_called_with()
assert mocked_output_display.vlc_media_player == mocked_media_player_new
mocked_output_display.size.assert_called_with()
mocked_qframe.resize.assert_called_with((10, 10))
mocked_qframe.raise_.assert_called_with()
mocked_qframe.hide.assert_called_with()
@ -188,14 +187,14 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_vlc = MagicMock()
mocked_vlc.Instance.return_value = mocked_instance
mocked_get_vlc.return_value = mocked_vlc
mocked_display = MagicMock()
mocked_display.has_audio = True
mocked_display.controller.is_live = True
mocked_display.size.return_value = (10, 10)
mocked_output_display = MagicMock()
mocked_controller = MagicMock()
mocked_controller.is_live = True
mocked_output_display.size.return_value = (10, 10)
vlc_player = VlcPlayer(None)
# 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
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.Instance.return_value = mocked_instance
mocked_get_vlc.return_value = mocked_vlc
mocked_display = MagicMock()
mocked_display.has_audio = False
mocked_display.controller.is_live = True
mocked_display.size.return_value = (10, 10)
mocked_output_display = MagicMock()
mocked_controller = MagicMock()
mocked_controller.is_live = True
mocked_output_display.size.return_value = (10, 10)
vlc_player = VlcPlayer(None)
# 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
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_macosx')
@ -263,14 +262,14 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_vlc = MagicMock()
mocked_vlc.Instance.return_value = mocked_instance
mocked_get_vlc.return_value = mocked_vlc
mocked_display = MagicMock()
mocked_display.has_audio = False
mocked_display.controller.is_live = True
mocked_display.size.return_value = (10, 10)
mocked_output_display = MagicMock()
mocked_controller = MagicMock()
mocked_controller.is_live = True
mocked_output_display.size.return_value = (10, 10)
vlc_player = VlcPlayer(None)
# WHEN: setup() is run
vlc_player.setup(mocked_display)
vlc_player.setup(mocked_output_display, mocked_controller)
# THEN: set_hwnd should be called
mocked_media_player_new.set_hwnd.assert_called_with(2)
@ -300,14 +299,14 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_vlc = MagicMock()
mocked_vlc.Instance.return_value = mocked_instance
mocked_get_vlc.return_value = mocked_vlc
mocked_display = MagicMock()
mocked_display.has_audio = False
mocked_display.controller.is_live = True
mocked_display.size.return_value = (10, 10)
mocked_output_display = MagicMock()
mocked_controller = MagicMock()
mocked_controller.is_live = True
mocked_output_display.size.return_value = (10, 10)
vlc_player = VlcPlayer(None)
# WHEN: setup() is run
vlc_player.setup(mocked_display)
vlc_player.setup(mocked_output_display, mocked_controller)
# THEN: set_nsobject should be called
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_vlc = MagicMock()
mocked_get_vlc.return_value = mocked_vlc
mocked_controller = MagicMock()
mocked_controller.media_info.volume = 100
mocked_controller.media_info.media_type = MediaType.Video
mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path
mocked_display = MagicMock()
mocked_display.media_info.volume = 100
mocked_display.media_info.media_type = MediaType.Video
mocked_display.media_info.file_info.absoluteFilePath.return_value = media_path
mocked_vlc_media = MagicMock()
mocked_media = MagicMock()
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_media_player.get_media.return_value = mocked_media
vlc_player = VlcPlayer(None)
@ -392,16 +389,13 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_normcase.side_effect = lambda x: x
mocked_vlc = MagicMock()
mocked_get_vlc.return_value = mocked_vlc
mocked_controller = MagicMock()
mocked_controller.media_info.volume = 100
mocked_controller.media_info.media_type = MediaType.CD
mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path
mocked_controller.media_info.title_track = 1
mocked_display = MagicMock()
mocked_display.media_info.volume = 100
mocked_display.media_info.media_type = MediaType.CD
mocked_display.media_info.title_track = 1
mocked_vlc_media = MagicMock()
mocked_media = MagicMock()
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_media_player.get_media.return_value = mocked_media
mocked_subitems = MagicMock()
@ -437,16 +431,14 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_normcase.side_effect = lambda x: x
mocked_vlc = MagicMock()
mocked_get_vlc.return_value = mocked_vlc
mocked_controller = MagicMock()
mocked_controller.media_info.volume = 100
mocked_controller.media_info.media_type = MediaType.CD
mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path
mocked_controller.media_info.title_track = 1
mocked_display = MagicMock()
mocked_display.media_info.volume = 100
mocked_display.media_info.media_type = MediaType.CD
mocked_display.media_info.file_info.absoluteFilePath.return_value = media_path
mocked_display.media_info.title_track = 1
mocked_vlc_media = MagicMock()
mocked_media = MagicMock()
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_media_player.get_media.return_value = mocked_media
mocked_subitems = MagicMock()
@ -482,16 +474,14 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_normcase.side_effect = lambda x: x
mocked_vlc = MagicMock()
mocked_get_vlc.return_value = mocked_vlc
mocked_controller = MagicMock()
mocked_controller.media_info.volume = 100
mocked_controller.media_info.media_type = MediaType.CD
mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path
mocked_controller.media_info.title_track = 1
mocked_display = MagicMock()
mocked_display.media_info.volume = 100
mocked_display.media_info.media_type = MediaType.CD
mocked_display.media_info.file_info.absoluteFilePath.return_value = media_path
mocked_display.media_info.title_track = 1
mocked_vlc_media = MagicMock()
mocked_media = MagicMock()
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_media_player.get_media.return_value = mocked_media
mocked_subitems = MagicMock()
@ -611,29 +601,28 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_threading.Thread.return_value = mocked_thread
mocked_vlc = MagicMock()
mocked_get_vlc.return_value = mocked_vlc
mocked_controller = MagicMock()
mocked_controller.media_info.start_time = 0
mocked_controller.media_info.media_type = MediaType.Video
mocked_controller.media_info.volume = 100
mocked_display = MagicMock()
mocked_media = MagicMock()
mocked_media.get_duration.return_value = 50000
mocked_display = MagicMock()
mocked_display.controller = mocked_controller
mocked_display.vlc_media_player.get_media.return_value = mocked_media
mocked_output_display = MagicMock()
mocked_output_display.media_info.start_time = 0
mocked_output_display.media_info.media_type = MediaType.Video
mocked_output_display.media_info.volume = 100
mocked_output_display.vlc_media_player.get_media.return_value = mocked_media
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
with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \
patch.object(vlc_player, 'volume') as mocked_volume:
mocked_media_state_wait.return_value = True
result = vlc_player.play(mocked_display)
result = vlc_player.play(mocked_display, mocked_output_display)
# THEN: A bunch of things should happen to play the media
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()
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'
@patch('openlp.core.ui.media.vlcplayer.threading')
@ -649,16 +638,15 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_get_vlc.return_value = mocked_vlc
mocked_controller = MagicMock()
mocked_controller.media_info.start_time = 0
mocked_display = MagicMock()
mocked_display.controller = mocked_controller
mocked_output_display = MagicMock()
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
with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \
patch.object(vlc_player, 'volume'):
mocked_media_state_wait.return_value = False
result = vlc_player.play(mocked_display)
result = vlc_player.play(mocked_controller, mocked_output_display)
# THEN: A thread should be started, but the method should return False
mocked_thread.start.assert_called_with()
@ -676,33 +664,32 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_vlc = MagicMock()
mocked_get_vlc.return_value = mocked_vlc
mocked_controller = MagicMock()
mocked_controller.media_info.start_time = 0
mocked_controller.media_info.end_time = 50
mocked_controller.media_info.media_type = MediaType.DVD
mocked_controller.media_info.volume = 100
mocked_controller.media_info.title_track = 1
mocked_controller.media_info.audio_track = 1
mocked_controller.media_info.subtitle_track = 1
mocked_display = MagicMock()
mocked_display.controller = mocked_controller
mocked_output_display = MagicMock()
mocked_output_display.media_info.start_time = 0
mocked_output_display.media_info.end_time = 50
mocked_output_display.media_info.media_type = MediaType.DVD
mocked_output_display.media_info.volume = 100
mocked_output_display.media_info.title_track = 1
mocked_output_display.media_info.audio_track = 1
mocked_output_display.media_info.subtitle_track = 1
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
with patch.object(vlc_player, 'media_state_wait', return_value=True), \
patch.object(vlc_player, 'volume') as mocked_volume, \
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
mocked_thread.start.assert_called_with()
mocked_display.vlc_media_player.set_title.assert_called_with(1)
mocked_display.vlc_media_player.play.assert_called_with()
mocked_display.vlc_media_player.audio_set_track.assert_called_with(1)
mocked_display.vlc_media_player.video_set_spu.assert_called_with(1)
mocked_volume.assert_called_with(mocked_display, 100)
mocked_output_display.vlc_media_player.set_title.assert_called_with(1)
mocked_output_display.vlc_media_player.play.assert_called_with()
mocked_output_display.vlc_media_player.audio_set_track.assert_called_with(1)
mocked_output_display.vlc_media_player.video_set_spu.assert_called_with(1)
mocked_volume.assert_called_with(mocked_output_display, 100)
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'
@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.seek_slider.isSliderDown.return_value = False
mocked_display = MagicMock()
mocked_display.controller = mocked_controller
mocked_display.vlc_media.get_state.return_value = 1
mocked_display.vlc_media_player.get_time.return_value = 400000
vlc_player = VlcPlayer(None)
@ -945,7 +931,7 @@ class TestVLCPlayer(TestCase, TestMixin):
# WHEN: update_ui() is called
with patch.object(vlc_player, 'stop') as mocked_stop, \
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
mocked_stop.assert_called_with(mocked_display)
@ -970,22 +956,19 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_controller.media_info.end_time = 300
mocked_controller.seek_slider.isSliderDown.return_value = False
mocked_display = MagicMock()
mocked_display.controller = mocked_controller
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
vlc_player = VlcPlayer(None)
# WHEN: update_ui() is called
with patch.object(vlc_player, 'stop') as mocked_stop, \
patch.object(vlc_player, 'set_visible') as mocked_set_visible:
vlc_player.update_ui(mocked_display)
with patch.object(vlc_player, 'stop') as mocked_stop:
vlc_player.update_ui(mocked_controller, mocked_display)
# THEN: Certain methods should be called
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_set_visible.assert_called_with(mocked_display, False)
mocked_controller.seek_slider.setSliderPosition.assert_called_with(300)
expected_calls = [call(True), call(False)]
assert expected_calls == mocked_controller.seek_slider.blockSignals.call_args_list