openlp/openlp/core/ui/media/mediamanager.py

443 lines
18 KiB
Python
Raw Normal View History

2011-06-05 21:10:49 +00:00
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2011 Raoul Snyman #
2011-06-14 16:10:20 +00:00
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
2011-06-05 21:10:49 +00:00
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
# --------------------------------------------------------------------------- #
# 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 #
###############################################################################
import logging
import sys, os,time
from PyQt4 import QtCore, QtGui, QtWebKit
from openlp.core.lib import OpenLPToolbar, Receiver, translate
from openlp.core.lib.ui import UiStrings, critical_error_message_box
2011-07-10 21:43:07 +00:00
from openlp.core.ui.media import MediaAPI, MediaState, MediaInfo
from openlp.core.utils import AppLocation
2011-06-05 21:10:49 +00:00
log = logging.getLogger(__name__)
class MediaManager(object):
"""
The implementation of a Media Manager
The idea is to separate the media related implementation
into the plugin files and unify the access from other parts of code
The media manager adds an own class for every API
Currently these are QtWebkit, Phonon and planed Vlc.
Manager
- different API classes with specialised Access functions
Controller
- have general and API specific control Elements
- have one or more displays (Preview, Live, ...) with different settings
Display
- have API-Specific Display Elements
- have media info for current media
"""
def __init__(self, parent):
self.parent = parent
self.APIs = {}
self.controller = []
self.curDisplayMediaAPI = {}
#Timer for video state
self.Timer = QtCore.QTimer()
self.Timer.setInterval(200)
self.withLivePreview = False
2011-07-10 21:43:07 +00:00
self.checkPreConditions()
2011-06-05 21:10:49 +00:00
#Signals
QtCore.QObject.connect(self.Timer,
QtCore.SIGNAL("timeout()"), self.video_state)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'Media Start'), self.video_play)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'Media Pause'), self.video_pause)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'Media Stop'), self.video_stop)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'seekSlider'), self.video_seek)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'volumeSlider'), self.video_volume)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'media_hide'), self.video_hide)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'media_blank'), self.video_blank)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'media_unblank'), self.video_unblank)
2011-07-10 21:43:07 +00:00
def registerControllers(self, controller):
"""
Register each media API controller (Webkit, Phonon, etc) and
store for later use
"""
if controller.check_available():
self.APIs[controller.name] = controller
def checkPreConditions(self):
"""
Check to see if we have any media API's available
If Not do not install the plugin.
"""
log.debug(u'checkPreConditions')
controller_dir = os.path.join(
AppLocation.get_directory(AppLocation.AppDir),
u'core', u'ui', u'media')
for filename in os.listdir(controller_dir):
if filename.endswith(u'api.py') and \
not filename == 'mediaapi.py':
path = os.path.join(controller_dir, filename)
if os.path.isfile(path):
modulename = u'openlp.core.ui.media.' + \
os.path.splitext(filename)[0]
log.debug(u'Importing controller %s', modulename)
try:
__import__(modulename, globals(), locals(), [])
except ImportError:
log.warn(u'Failed to import %s on path %s',
modulename, path)
controller_classes = MediaAPI.__subclasses__()
for controller_class in controller_classes:
controller = controller_class(self)
self.registerControllers(controller)
if self.APIs:
return True
else:
return False
2011-06-05 21:10:49 +00:00
def video_state(self):
"""
Check if there is an assigned media API and do some
updating stuff (e.g. update the UI)
"""
isAnyonePlaying = False
if len(self.curDisplayMediaAPI.keys()) == 0:
self.Timer.stop()
else:
for display in self.curDisplayMediaAPI.keys():
self.curDisplayMediaAPI[display].update_ui(display)
if self.curDisplayMediaAPI[display] \
.state == MediaState.Playing:
isAnyonePlaying = True
if not isAnyonePlaying:
self.Timer.stop()
2011-06-14 16:10:20 +00:00
def getDisplayCss(self):
2011-06-08 13:18:05 +00:00
"""
Add css style sheets to htmlbuilder
"""
css = u'';
for api in self.APIs.values():
2011-06-14 16:10:20 +00:00
css += api.getDisplayCss()
2011-06-08 13:18:05 +00:00
return css
2011-06-14 16:10:20 +00:00
def getDisplayJavascript(self):
2011-06-08 13:18:05 +00:00
"""
Add javascript functions to htmlbuilder
"""
js = u''
for api in self.APIs.values():
2011-06-14 16:10:20 +00:00
js += api.getDisplayJavascript()
2011-06-08 13:18:05 +00:00
return js
2011-06-14 16:10:20 +00:00
def getDisplayHtml(self):
2011-06-08 13:18:05 +00:00
"""
Add html code to htmlbuilder
"""
html = u''
for api in self.APIs.values():
2011-06-14 16:10:20 +00:00
html += api.getDisplayHtml()
2011-06-08 13:18:05 +00:00
return html
2011-06-05 21:10:49 +00:00
def addControllerItems(self, controller, control_panel):
self.controller.append(controller)
self.setup_generic_controls(controller, control_panel)
for api in self.APIs.values():
api.setup_controls(controller, control_panel)
def setup_generic_controls(self, controller, control_panel):
controller.media_info = MediaInfo()
# Build a Media ToolBar
controller.mediabar = OpenLPToolbar(controller)
controller.mediabar.addToolbarButton(
u'Media Start', u':/slides/media_playback_start.png',
translate('OpenLP.SlideController', 'Start playing media'),
controller.sendToPlugins)
controller.mediabar.addToolbarButton(
u'Media Pause', u':/slides/media_playback_pause.png',
2011-06-08 13:18:05 +00:00
translate('OpenLP.SlideController', 'Pause playing media'),
2011-06-05 21:10:49 +00:00
controller.sendToPlugins)
controller.mediabar.addToolbarButton(
u'Media Stop', u':/slides/media_playback_stop.png',
2011-06-08 13:18:05 +00:00
translate('OpenLP.SlideController', 'Stop playing media'),
2011-06-05 21:10:49 +00:00
controller.sendToPlugins)
# Build the seekSlider.
controller.seekSlider = QtGui.QSlider(QtCore.Qt.Horizontal)
controller.seekSlider.setMaximum(1000)
# Build the volumeSlider.
controller.volumeSlider = QtGui.QSlider(QtCore.Qt.Horizontal)
controller.volumeSlider.setTickInterval(10)
controller.volumeSlider.setTickPosition(QtGui.QSlider.TicksAbove)
controller.volumeSlider.setMinimum(0)
controller.volumeSlider.setMaximum(100)
controller.volumeSlider.setValue(controller.media_info.volume)
controller.seekSlider.setGeometry(QtCore.QRect(90, 260, 221, 24))
controller.seekSlider.setObjectName(u'seekSlider')
controller.mediabar.addToolbarWidget(u'Seek Slider', controller.seekSlider)
controller.volumeSlider.setGeometry(QtCore.QRect(90, 160, 221, 24))
controller.volumeSlider.setObjectName(u'volumeSlider')
controller.mediabar.addToolbarWidget(u'Audio Volume', controller.volumeSlider)
control_panel.addWidget(controller.mediabar)
controller.mediabar.setVisible(False)
#Signals
QtCore.QObject.connect(controller.seekSlider,
QtCore.SIGNAL(u'sliderMoved(int)'), controller.sendToPlugins)
QtCore.QObject.connect(controller.volumeSlider,
QtCore.SIGNAL(u'sliderMoved(int)'), controller.sendToPlugins)
def setup_display(self, display):
"""
After a new display is configured, all media related widget
will be created too
"""
display.hasAudio = True
if not self.withLivePreview and \
display == self.parent.liveController.previewDisplay:
return
if display == self.parent.previewController.previewDisplay or \
display == self.parent.liveController.previewDisplay:
2011-06-14 16:10:20 +00:00
display.resize(display.controller.slidePreview.size())
2011-06-05 21:10:49 +00:00
display.hasAudio = False
for api in self.APIs.values():
api.setup(display)
def set_controls_visible(self, controller, value):
# Generic controls
controller.mediabar.setVisible(value)
# Special controls
2011-06-14 16:10:20 +00:00
# TODO
2011-06-05 21:10:49 +00:00
# for api in self.APIs.values():
# api.setup_controls(controller, control_panel)
2011-06-06 19:12:14 +00:00
def resize(self, controller, display, api):
2011-06-05 21:10:49 +00:00
"""
After Mainwindow changes or Splitter moved all related media
widgets have to be resized
"""
if display == self.parent.previewController.previewDisplay or \
display == self.parent.liveController.previewDisplay:
2011-06-06 19:12:14 +00:00
display.resize(controller.slidePreview.size())
2011-06-05 21:10:49 +00:00
api.resize(display)
2011-07-18 21:25:10 +00:00
def video(self, controller, file, muted, isBackground):
2011-06-05 21:10:49 +00:00
"""
Loads and starts a video to run with the option of sound
"""
log.debug(u'video')
isValid = False
# stop running videos
self.video_reset(controller)
controller.media_info = MediaInfo()
2011-07-18 21:25:10 +00:00
if muted:
controller.media_info.volume = 0
else:
controller.media_info.volume = controller.volumeSlider.value()
controller.media_info.file_info = QtCore.QFileInfo(file)
controller.media_info.is_background = isBackground
2011-06-05 21:10:49 +00:00
if controller.isLive:
if self.withLivePreview:
display = controller.previewDisplay
2011-06-06 19:12:14 +00:00
isValid = self.check_file_type(controller, display)
2011-06-05 21:10:49 +00:00
display = controller.display
2011-06-06 19:12:14 +00:00
isValid = self.check_file_type(controller, display)
2011-06-05 21:10:49 +00:00
else:
display = controller.previewDisplay
2011-06-06 19:12:14 +00:00
isValid = self.check_file_type(controller, display)
2011-06-05 21:10:49 +00:00
if not isValid:
#Media could not be loaded correctly
critical_error_message_box(
translate('MediaPlugin.MediaItem', 'Unsupported File'),
unicode(translate('MediaPlugin.MediaItem',
'Unsupported File')))
2011-07-18 21:25:10 +00:00
return False
2011-06-05 21:10:49 +00:00
#now start playing
self.video_play([controller])
2011-06-06 19:12:14 +00:00
self.video_pause([controller])
2011-06-08 14:49:48 +00:00
self.video_seek([controller, [0]])
2011-06-06 19:12:14 +00:00
self.video_play([controller])
2011-06-05 21:10:49 +00:00
self.set_controls_visible(controller, True)
2011-06-14 16:10:20 +00:00
log.debug(u'use %s controller' % self.curDisplayMediaAPI[display])
2011-07-18 21:25:10 +00:00
return True
2011-06-05 21:10:49 +00:00
2011-06-06 19:12:14 +00:00
def check_file_type(self, controller, display):
2011-06-05 21:10:49 +00:00
"""
Used to choose the right media API type
from the prioritized API list
"""
apiSettings = str(QtCore.QSettings().value(u'media/apis',
QtCore.QVariant(u'Webkit')).toString())
usedAPIs = apiSettings.split(u',')
if controller.media_info.file_info.isFile():
suffix = u'*.%s' % controller.media_info.file_info.suffix()
for title in usedAPIs:
api = self.APIs[title]
if suffix in api.video_extensions_list:
if not controller.media_info.is_background or \
controller.media_info.is_background and api.canBackground:
2011-06-06 19:12:14 +00:00
self.resize(controller, display, api)
2011-06-05 21:10:49 +00:00
if api.load(display):
self.curDisplayMediaAPI[display] = api
return True
# no valid api found
return False
def video_play(self, msg):
"""
Responds to the request to play a loaded video
"""
log.debug(u'video_play')
controller = msg[0]
for display in self.curDisplayMediaAPI.keys():
2011-06-06 19:12:14 +00:00
if display.controller == controller:
2011-06-05 21:10:49 +00:00
self.curDisplayMediaAPI[display].play(display)
# Start Timer for ui updates
if not self.Timer.isActive():
self.Timer.start()
def video_pause(self, msg):
"""
Responds to the request to pause a loaded video
"""
log.debug(u'videoPause')
controller = msg[0]
for display in self.curDisplayMediaAPI.keys():
2011-06-06 19:12:14 +00:00
if display.controller == controller:
2011-06-05 21:10:49 +00:00
self.curDisplayMediaAPI[display].pause(display)
def video_stop(self, msg):
"""
Responds to the request to stop a loaded video
"""
log.debug(u'video_stop')
controller = msg[0]
for display in self.curDisplayMediaAPI.keys():
2011-06-06 19:12:14 +00:00
if display.controller == controller:
2011-06-05 21:10:49 +00:00
self.curDisplayMediaAPI[display].stop(display)
self.curDisplayMediaAPI[display].set_visible(display, False)
def video_volume(self, msg):
"""
Changes the volume of a running video
"""
controller = msg[0]
2011-06-08 14:49:48 +00:00
vol = msg[1][0]
2011-06-05 21:10:49 +00:00
log.debug(u'video_volume %d' % vol)
for display in self.curDisplayMediaAPI.keys():
2011-06-06 19:12:14 +00:00
if display.controller == controller:
2011-06-05 21:10:49 +00:00
self.curDisplayMediaAPI[display].volume(display, vol)
def video_seek(self, msg):
"""
Responds to the request to change the seek Slider of a loaded video
"""
log.debug(u'video_seek')
controller = msg[0]
2011-06-08 14:49:48 +00:00
seekVal = msg[1][0]
2011-06-05 21:10:49 +00:00
for display in self.curDisplayMediaAPI.keys():
2011-06-06 19:12:14 +00:00
if display.controller == controller:
2011-06-05 21:10:49 +00:00
self.curDisplayMediaAPI[display].seek(display, seekVal)
def video_reset(self, controller):
"""
Responds to the request to reset a loaded video
"""
log.debug(u'video_reset')
for display in self.curDisplayMediaAPI.keys():
2011-06-06 19:12:14 +00:00
if display.controller == controller:
2011-06-05 21:10:49 +00:00
self.curDisplayMediaAPI[display].reset(display)
del self.curDisplayMediaAPI[display]
self.set_controls_visible(controller, False)
def video_hide(self, msg):
"""
Hide the related video Widget
"""
isLive = msg[1]
if isLive:
controller = self.parent.liveController
for display in self.curDisplayMediaAPI.keys():
2011-06-06 19:12:14 +00:00
if display.controller == controller:
2011-06-05 21:10:49 +00:00
if self.curDisplayMediaAPI[display] \
.state == MediaState.Playing:
self.curDisplayMediaAPI[display].pause(display)
self.curDisplayMediaAPI[display] \
.set_visible(display, False)
def video_blank(self, msg):
"""
Blank the related video Widget
"""
isLive = msg[1]
if isLive:
controller = self.parent.liveController
for display in self.curDisplayMediaAPI.keys():
2011-06-06 19:12:14 +00:00
if display.controller == controller:
2011-06-05 21:10:49 +00:00
if self.curDisplayMediaAPI[display] \
.state == MediaState.Playing:
self.curDisplayMediaAPI[display].pause(display)
self.curDisplayMediaAPI[display] \
.set_visible(display, False)
def video_unblank(self, msg):
"""
Unblank the related video Widget
"""
Receiver.send_message(u'maindisplay_show')
isLive = msg[1]
if isLive:
controller = self.parent.liveController
for display in self.curDisplayMediaAPI.keys():
2011-06-06 19:12:14 +00:00
if display.controller == controller:
2011-06-05 21:10:49 +00:00
if self.curDisplayMediaAPI[display] \
.state == MediaState.Paused:
self.curDisplayMediaAPI[display].play(display)
self.curDisplayMediaAPI[display] \
.set_visible(display, True)
def get_audio_extensions_list(self):
audio_list = []
for api in self.APIs.values():
for item in api.audio_extensions_list:
if not item in audio_list:
audio_list.append(item)
return audio_list
def get_video_extensions_list(self):
video_list = []
for api in self.APIs.values():
for item in api.video_extensions_list:
if not item in video_list:
video_list.append(item)
return video_list