clean ups and settings

This commit is contained in:
Tim Bentley 2019-01-20 17:58:41 +00:00
parent 168efffb60
commit 469a888563
10 changed files with 265 additions and 1279 deletions

View File

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

View File

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

View File

@ -0,0 +1,259 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2018 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
LINUX_STREAM = 'v4l2:// :v4l-vdev="/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 setupUi(self):
"""
Set up the UI
"""
self.setObjectName('MediaTab')
super(MediaTab, self).setupUi()
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.media_layout = QtWidgets.QVBoxLayout(self.stream_media_group_box)
self.media_layout.setObjectName('media_layout')
self.left_layout.addWidget(self.stream_media_group_box)
self.left_layout.addStretch()
self.right_layout.addStretch()
# 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 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'))
self.auto_start_check_box.setText(translate('MediaPlugin.MediaTab', 'Start automatically'))
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
"""
self.auto_start_check_box.setChecked(Settings().value(self.settings_section + '/media auto start'))
# 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
"""
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()

View File

@ -1,331 +0,0 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2018 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.ui.media import MediaState
from openlp.core.ui.media.mediaplayer import MediaPlayer
from openlp.core.threading import ThreadWorker, run_thread, is_thread_finished
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

@ -223,6 +223,8 @@ class VlcPlayer(MediaPlayer):
if not audio_cd_tracks or audio_cd_tracks.count() < 1: if not audio_cd_tracks or audio_cd_tracks.count() < 1:
return False return False
display.vlc_media = audio_cd_tracks.item_at_index(controller.media_info.title_track) display.vlc_media = audio_cd_tracks.item_at_index(controller.media_info.title_track)
elif controller.media_info.media_type == MediaType.Stream:
display.vlc_media = display.vlc_instance.media_new_location("XXXXXXXXXXXXX")
else: else:
display.vlc_media = display.vlc_instance.media_new_path(path) display.vlc_media = display.vlc_instance.media_new_path(path)
# put the media in the media player # put the media in the media player

View File

@ -1,312 +0,0 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2018 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.webkit` module contains our WebKit video player
"""
import logging
from PyQt5 import QtGui, QtWebKitWidgets
from openlp.core.common.i18n import translate
from openlp.core.common.settings import Settings
from openlp.core.ui.media import MediaState
from openlp.core.ui.media.mediaplayer import MediaPlayer
log = logging.getLogger(__name__)
VIDEO_CSS = """
#videobackboard {
z-index:3;
background-color: %(bgcolor)s;
}
#video {
background-color: %(bgcolor)s;
z-index:4;
}
"""
VIDEO_JS = """
function show_video(state, path, volume, variable_value){
// Sometimes video.currentTime stops slightly short of video.duration and video.ended is intermittent!
var video = document.getElementById('video');
if(volume != null){
video.volume = volume;
}
switch(state){
case 'load':
video.src = 'file:///' + path;
video.load();
break;
case 'play':
video.play();
break;
case 'pause':
video.pause();
break;
case 'stop':
show_video('pause');
video.currentTime = 0;
break;
case 'close':
show_video('stop');
video.src = '';
break;
case 'length':
return video.duration;
case 'current_time':
return video.currentTime;
case 'seek':
video.currentTime = variable_value;
break;
case 'isEnded':
return video.ended;
case 'setVisible':
video.style.visibility = variable_value;
break;
case 'setBackBoard':
var back = document.getElementById('videobackboard');
back.style.visibility = variable_value;
break;
}
}
"""
VIDEO_HTML = """
<div id="videobackboard" class="size" style="visibility:hidden"></div>
<video id="video" class="size" style="visibility:hidden" autobuffer preload></video>
"""
VIDEO_EXT = ['*.3gp', '*.3gpp', '*.3g2', '*.3gpp2', '*.aac', '*.flv', '*.f4a', '*.f4b', '*.f4p', '*.f4v', '*.mov',
'*.m4a', '*.m4b', '*.m4p', '*.m4v', '*.mkv', '*.mp4', '*.ogv', '*.webm', '*.mpg', '*.wmv', '*.mpeg',
'*.avi', '*.swf']
AUDIO_EXT = ['*.mp3', '*.ogg']
class WebkitPlayer(MediaPlayer):
"""
A specialised version of the MediaPlayer class, which provides a QtWebKit
display.
"""
def __init__(self, parent):
"""
Constructor
"""
super(WebkitPlayer, self).__init__(parent, 'webkit')
self.original_name = 'WebKit'
self.display_name = '&WebKit'
self.parent = parent
self.can_background = True
self.audio_extensions_list = AUDIO_EXT
self.video_extensions_list = VIDEO_EXT
def get_media_display_css(self):
"""
Add css style sheets to htmlbuilder
"""
background = QtGui.QColor(Settings().value('players/background color')).name()
css = VIDEO_CSS % {'bgcolor': background}
return css
def get_media_display_javascript(self):
"""
Add javascript functions to htmlbuilder
"""
return VIDEO_JS
def get_media_display_html(self):
"""
Add html code to htmlbuilder
"""
return VIDEO_HTML
def setup(self, display):
"""
Set up the player
:param display: The display to be updated.
"""
display.web_view.resize(display.size())
display.web_view.raise_()
self.has_own_widget = False
def check_available(self):
"""
Check the availability of the media player.
:return: boolean. True if available
"""
web = QtWebKitWidgets.QWebPage()
# This script should return '[object HTMLVideoElement]' if the html5 video is available in webkit. Otherwise it
# should return '[object HTMLUnknownElement]'
return web.mainFrame().evaluateJavaScript(
"Object.prototype.toString.call(document.createElement('video'));") == '[object HTMLVideoElement]'
def load(self, display):
"""
Load a video
:param display: The display to be updated.
"""
log.debug('load vid in Webkit Controller')
controller = display.controller
if display.has_audio and not controller.media_info.is_background:
volume = controller.media_info.volume
vol = float(volume) / float(100)
else:
vol = 0
path = controller.media_info.file_info.absoluteFilePath()
display.web_view.setVisible(True)
js = 'show_video("load", "{path}", {vol});'.format(path=path.replace('\\', '\\\\'), vol=str(vol))
display.frame.evaluateJavaScript(js)
return True
def resize(self, display):
"""
Resize the player
:param display: The display to be updated.
"""
display.web_view.resize(display.size())
def play(self, display):
"""
Play a video
:param display: The display to be updated.
"""
controller = display.controller
display.web_loaded = True
start_time = 0
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
else:
if self.get_preview_state() != MediaState.Paused and controller.media_info.start_time > 0:
start_time = controller.media_info.start_time
self.set_visible(display, True)
display.frame.evaluateJavaScript('show_video("play");')
if start_time > 0:
self.seek(display, controller.media_info.start_time * 1000)
self.set_state(MediaState.Playing, display)
display.web_view.raise_()
return True
def pause(self, display):
"""
Pause a video
:param display: The display to be updated.
"""
display.frame.evaluateJavaScript('show_video("pause");')
self.set_state(MediaState.Paused, display)
def stop(self, display):
"""
Stop a video
:param display: The display to be updated.
"""
display.frame.evaluateJavaScript('show_video("stop");')
self.set_state(MediaState.Stopped, display)
def volume(self, display, volume):
"""
Set the volume
:param display: The display to be updated.
:param volume: The volume to be set.
"""
# 1.0 is the highest value
if display.has_audio:
vol = float(volume) / float(100)
display.frame.evaluateJavaScript('show_video(null, null, %s);' % str(vol))
def seek(self, display, seek_value):
"""
Go to a position in the video
:param display: The display to be updated.
:param seek_value: The value to be set.
"""
seek = float(seek_value) / 1000
display.frame.evaluateJavaScript('show_video("seek", null, null, null, "%f");' % seek)
def reset(self, display):
"""
Reset the player
:param display: The display to be updated.
"""
display.frame.evaluateJavaScript('show_video("close");')
self.set_state(MediaState.Off, display)
def set_visible(self, display, visibility):
"""
Set the visibility
:param display: The display to be updated.
:param visibility: The visibility to be set.
"""
if visibility:
is_visible = "visible"
else:
is_visible = "hidden"
display.frame.evaluateJavaScript('show_video("setVisible", null, null, null, "%s");' % is_visible)
def update_ui(self, display):
"""
Update the UI
:param display: The display to be updated.
"""
controller = display.controller
if display.frame.evaluateJavaScript('show_video("isEnded");'):
self.stop(display)
current_time = display.frame.evaluateJavaScript('show_video("current_time");')
# check if conversion was ok and value is not 'NaN'
if current_time and current_time != float('inf'):
current_time = int(current_time * 1000)
length = display.frame.evaluateJavaScript('show_video("length");')
# check if conversion was ok and value is not 'NaN'
if length and length != float('inf'):
length = int(length * 1000)
if current_time and length:
controller.media_info.length = length
controller.seek_slider.setMaximum(length)
if not controller.seek_slider.isSliderDown():
controller.seek_slider.blockSignals(True)
controller.seek_slider.setSliderPosition(current_time)
controller.seek_slider.blockSignals(False)
def get_info(self):
"""
Return some information about this player
"""
part1 = translate('Media.player', 'Webkit is a media player which runs inside a web browser. This player '
'allows text over video to be rendered.')
part2 = translate('Media.player', 'Audio')
part3 = translate('Media.player', 'Video')
return part1 + '<br/> <strong>' + part2 + '</strong><br/>' + str(AUDIO_EXT) + '<br/><strong>' + part3 + \
'</strong><br/>' + str(VIDEO_EXT) + '<br/>'

View File

@ -33,7 +33,6 @@ from openlp.core.common.registry import Registry
from openlp.core.lib.theme import BackgroundType, BackgroundGradientType from openlp.core.lib.theme import BackgroundType, BackgroundGradientType
from openlp.core.lib.ui import critical_error_message_box from openlp.core.lib.ui import critical_error_message_box
from openlp.core.ui.themelayoutform import ThemeLayoutForm from openlp.core.ui.themelayoutform import ThemeLayoutForm
from openlp.core.ui.media.webkitplayer import VIDEO_EXT
from .themewizard import Ui_ThemeWizard from .themewizard import Ui_ThemeWizard
log = logging.getLogger(__name__) log = logging.getLogger(__name__)

View File

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

View File

@ -1,567 +0,0 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2018 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 SystemPlayer, CheckMediaWorker, ADDITIONAL_EXT
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

@ -1,66 +0,0 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2018 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.webkitplayer package.
"""
from unittest import TestCase
from unittest.mock import MagicMock, patch
from openlp.core.ui.media.webkitplayer import WebkitPlayer
class TestWebkitPlayer(TestCase):
"""
Test the functions in the :mod:`webkitplayer` module.
"""
def test_check_available_video_disabled(self):
"""
Test of webkit video unavailability
"""
# GIVEN: A WebkitPlayer instance and a mocked QWebPage
mocked_qwebpage = MagicMock()
mocked_qwebpage.mainFrame().evaluateJavaScript.return_value = '[object HTMLUnknownElement]'
with patch('openlp.core.ui.media.webkitplayer.QtWebKitWidgets.QWebPage', **{'return_value': mocked_qwebpage}):
webkit_player = WebkitPlayer(None)
# WHEN: An checking if the player is available
available = webkit_player.check_available()
# THEN: The player should not be available when '[object HTMLUnknownElement]' is returned
assert available is False, 'The WebkitPlayer should not be available when video feature detection fails'
def test_check_available_video_enabled(self):
"""
Test of webkit video availability
"""
# GIVEN: A WebkitPlayer instance and a mocked QWebPage
mocked_qwebpage = MagicMock()
mocked_qwebpage.mainFrame().evaluateJavaScript.return_value = '[object HTMLVideoElement]'
with patch('openlp.core.ui.media.webkitplayer.QtWebKitWidgets.QWebPage', **{'return_value': mocked_qwebpage}):
webkit_player = WebkitPlayer(None)
# WHEN: An checking if the player is available
available = webkit_player.check_available()
# THEN: The player should be available when '[object HTMLVideoElement]' is returned
assert available is True, 'The WebkitPlayer should be available when video feature detection passes'