Head r1812

This commit is contained in:
Jon Tibble 2011-12-02 20:17:57 +00:00
commit bba6d6730f
25 changed files with 2012 additions and 727 deletions

View File

@ -53,8 +53,8 @@ body {
position: absolute;
left: 0px;
top: 0px;
width: %spx;
height: %spx;
width: 100%%;
height: 100%%;
}
#black {
z-index: 8;
@ -67,12 +67,6 @@ body {
#image {
z-index: 2;
}
#video1 {
z-index: 3;
}
#video2 {
z-index: 3;
}
%s
#footer {
position: absolute;
@ -90,90 +84,9 @@ sup {
</style>
<script>
var timer = null;
var video_timer = null;
var current_video = '1';
var transition = %s;
function show_video(state, path, volume, loop){
// Note, the preferred method for looping would be to use the
// video tag loop attribute.
// But QtWebKit doesn't support this. Neither does it support the
// onended event, hence the setInterval()
// In addition, setting the currentTime attribute to zero to restart
// the video raises an INDEX_SIZE_ERROR: DOM Exception 1
// To complicate it further, sometimes vid.currentTime stops
// slightly short of vid.duration and vid.ended is intermittent!
//
// Note, currently the background may go black between loops. Not
// desirable. Need to investigate using two <video>'s, and hiding/
// preloading one, and toggle between the two when looping.
if(current_video=='1'){
var vid = document.getElementById('video1');
var vid2 = document.getElementById('video2');
} else {
var vid = document.getElementById('video2');
var vid2 = document.getElementById('video1');
}
if(volume != null){
vid.volume = volume;
vid2.volume = volume;
}
switch(state){
case 'init':
vid.src = path;
vid2.src = path;
if(loop == null) loop = false;
vid.looping = loop;
vid2.looping = loop;
vid.load();
break;
case 'load':
vid2.style.visibility = 'hidden';
vid2.load();
break;
case 'play':
vid.play();
vid.style.visibility = 'visible';
if(vid.looping){
video_timer = setInterval(
function() {
show_video('poll');
}, 200);
}
break;
case 'pause':
if(video_timer!=null){
clearInterval(video_timer);
video_timer = null;
}
vid.pause();
break;
case 'stop':
show_video('pause');
vid.style.visibility = 'hidden';
break;
case 'poll':
if(vid.ended||vid.currentTime+0.2>vid.duration)
show_video('swap');
break;
case 'swap':
show_video('pause');
if(current_video=='1')
current_video = '2';
else
current_video = '1';
show_video('play');
show_video('load');
break;
case 'close':
show_video('stop');
vid.src = '';
vid2.src = '';
break;
}
}
%s
function show_image(src){
var img = document.getElementById('image');
img.src = src;
@ -186,18 +99,14 @@ sup {
function show_blank(state){
var black = 'none';
var lyrics = '';
var pause = false;
switch(state){
case 'theme':
lyrics = 'hidden';
pause = true;
break;
case 'black':
black = 'block';
pause = true;
break;
case 'desktop':
pause = true;
break;
}
document.getElementById('black').style.display = black;
@ -210,13 +119,6 @@ sup {
if(shadow!=null)
shadow.style.visibility = lyrics;
document.getElementById('footer').style.visibility = lyrics;
var vid = document.getElementById('video');
if(vid.src != ''){
if(pause)
vid.pause();
else
vid.play();
}
}
function show_footer(footertext){
@ -277,10 +179,6 @@ sup {
<body>
<img id="bgimage" class="size" %s />
<img id="image" class="size" %s />
<video id="video1" class="size" style="visibility:hidden" autobuffer preload>
</video>
<video id="video2" class="size" style="visibility:hidden" autobuffer preload>
</video>
%s
%s
<div id="footer" class="footer"></div>
@ -336,7 +234,6 @@ def build_html(item, screen, islive, background, image=None,
js_additions += plugin.getDisplayJavaScript()
html_additions += plugin.getDisplayHtml()
html = HTMLSRC % (build_background_css(item, width, height),
width, height,
css_additions,
build_footer_css(item, height),
build_lyrics_css(item, webkitvers),
@ -609,4 +506,3 @@ def build_footer_css(item, height):
item.footer.width(), theme.font_footer_name,
theme.font_footer_size, theme.font_footer_color)
return lyrics_html

View File

@ -0,0 +1,137 @@
# -*- 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 #
# 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, #
# 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 #
###############################################################################
from openlp.core.ui.media import MediaState
class MediaPlayer(object):
"""
This is the base class media Player class to provide OpenLP with a pluggable media display
framework.
"""
def __init__(self, parent, name=u'media_player'):
self.parent = parent
self.name = name
self.available = self.check_available()
self.isActive = False
self.canBackground = False
self.canFolder = False
self.state = MediaState.Off
self.hasOwnWidget = False
self.audio_extensions_list = []
self.video_extensions_list = []
def check_available(self):
"""
Player is available on this machine
"""
return False
def setup(self, display):
"""
Create the related widgets for the current display
"""
pass
def load(self, display):
"""
Load a new media file and check if it is valid
"""
return True
def resize(self, display):
"""
If the main display size or position is changed, the media widgets
should also resized
"""
pass
def play(self, display):
"""
Starts playing of current Media File
"""
pass
def pause(self, display):
"""
Pause of current Media File
"""
pass
def stop(self, display):
"""
Stop playing of current Media File
"""
pass
def volume(self, display, vol):
"""
Change volume of current Media File
"""
pass
def seek(self, display, seekVal):
"""
Change playing position of current Media File
"""
pass
def reset(self, display):
"""
Remove the current loaded video
"""
pass
def set_visible(self, display, status):
"""
Show/Hide the media widgets
"""
pass
def update_ui(self, display):
"""
Do some ui related stuff (e.g. update the seek slider)
"""
pass
def get_media_display_css(self):
"""
Add css style sheets to htmlbuilder
"""
return u''
def get_media_display_javascript(self):
"""
Add javascript functions to htmlbuilder
"""
return u''
def get_media_display_html(self):
"""
Add html code to htmlbuilder
"""
return u''

View File

@ -168,6 +168,7 @@ class Plugin(QtCore.QObject):
self.mediadock = plugin_helpers[u'toolbox']
self.pluginManager = plugin_helpers[u'pluginmanager']
self.formparent = plugin_helpers[u'formparent']
self.mediaController = plugin_helpers[u'mediacontroller']
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'%s_add_service_item' % self.name),
self.processAddServiceEvent)
@ -395,4 +396,3 @@ class Plugin(QtCore.QObject):
Add html code to htmlbuilder.
"""
return u''

View File

@ -76,7 +76,7 @@ class Renderer(object):
self.theme_data = None
self.bg_frame = None
self.force_page = False
self.display = MainDisplay(None, self.imageManager, False)
self.display = MainDisplay(None, self.imageManager, False, self)
self.display.setup()
def update_display(self):
@ -87,7 +87,7 @@ class Renderer(object):
self._calculate_default()
if self.display:
self.display.close()
self.display = MainDisplay(None, self.imageManager, False)
self.display = MainDisplay(None, self.imageManager, False, self)
self.display.setup()
self.bg_frame = None
self.theme_data = None

View File

@ -77,10 +77,10 @@ from themeform import ThemeForm
from filerenameform import FileRenameForm
from starttimeform import StartTimeForm
from screen import ScreenList
from maindisplay import MainDisplay
from maindisplay import MainDisplay, Display
from servicenoteform import ServiceNoteForm
from serviceitemeditform import ServiceItemEditForm
from slidecontroller import SlideController
from slidecontroller import SlideController, Controller
from splashscreen import SplashScreen
from generaltab import GeneralTab
from themestab import ThemesTab

View File

@ -30,7 +30,7 @@ and play multimedia within OpenLP.
"""
import logging
from PyQt4 import QtCore, QtGui, QtWebKit
from PyQt4 import QtCore, QtGui, QtWebKit, QtOpenGL
from PyQt4.phonon import Phonon
from openlp.core.lib import Receiver, build_html, ServiceItem, image_to_byte, \
@ -43,11 +43,13 @@ log = logging.getLogger(__name__)
#http://www.steveheffernan.com/html5-video-player/demo-video-player.html
#http://html5demos.com/two-videos
class MainDisplay(QtGui.QGraphicsView):
class Display(QtGui.QGraphicsView):
"""
This is the display screen.
This is a general display screen class. Here the general display settings
will done. It will be used as specialized classes by Main Display and
Preview display.
"""
def __init__(self, parent, imageManager, live):
def __init__(self, parent, live, controller):
if live:
QtGui.QGraphicsView.__init__(self)
# Overwrite the parent() method.
@ -55,12 +57,60 @@ class MainDisplay(QtGui.QGraphicsView):
else:
QtGui.QGraphicsView.__init__(self, parent)
self.isLive = live
self.controller = controller
self.screen = {}
self.plugins = PluginManager.get_instance().plugins
self.setViewport(QtOpenGL.QGLWidget())
def setup(self):
"""
Set up and build the screen base
"""
log.debug(u'Start Display base setup (live = %s)' % self.isLive)
self.setGeometry(self.screen[u'size'])
log.debug(u'Setup webView')
self.webView = QtWebKit.QWebView(self)
self.webView.setGeometry(0, 0,
self.screen[u'size'].width(), self.screen[u'size'].height())
self.webView.settings().setAttribute(
QtWebKit.QWebSettings.PluginsEnabled, True)
self.page = self.webView.page()
self.frame = self.page.mainFrame()
if self.isLive and log.getEffectiveLevel() == logging.DEBUG:
self.webView.settings().setAttribute(
QtWebKit.QWebSettings.DeveloperExtrasEnabled, True)
QtCore.QObject.connect(self.webView,
QtCore.SIGNAL(u'loadFinished(bool)'), self.isWebLoaded)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.frame.setScrollBarPolicy(QtCore.Qt.Vertical,
QtCore.Qt.ScrollBarAlwaysOff)
self.frame.setScrollBarPolicy(QtCore.Qt.Horizontal,
QtCore.Qt.ScrollBarAlwaysOff)
def resizeEvent(self, ev):
self.webView.setGeometry(0, 0,
self.width(), self.height())
def isWebLoaded(self):
"""
Called by webView event to show display is fully loaded
"""
log.debug(u'Webloaded')
self.webLoaded = True
class MainDisplay(Display):
"""
This is the display screen as a specialized class from the Display class
"""
def __init__(self, parent, imageManager, live, controller):
Display.__init__(self, parent, live, controller)
self.imageManager = imageManager
self.screens = ScreenList.get_instance()
self.plugins = PluginManager.get_instance().plugins
self.rebuildCSS = False
self.hideMode = None
self.videoHide = False
self.override = {}
self.retranslateUi()
self.mediaObject = None
@ -79,9 +129,6 @@ class MainDisplay(QtGui.QGraphicsView):
QtCore.SIGNAL(u'live_display_hide'), self.hideDisplay)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'live_display_show'), self.showDisplay)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'openlp_phonon_creation'),
self.createMediaObject)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'update_display_css'), self.cssChanged)
QtCore.QObject.connect(Receiver.get_receiver(),
@ -114,36 +161,9 @@ class MainDisplay(QtGui.QGraphicsView):
Set up and build the output screen
"""
log.debug(u'Start MainDisplay setup (live = %s)' % self.isLive)
self.usePhonon = QtCore.QSettings().value(
u'media/use phonon', QtCore.QVariant(True)).toBool()
self.phononActive = False
self.screen = self.screens.current
self.setVisible(False)
self.setGeometry(self.screen[u'size'])
self.videoWidget = Phonon.VideoWidget(self)
self.videoWidget.setVisible(False)
self.videoWidget.setGeometry(QtCore.QRect(0, 0,
self.screen[u'size'].width(), self.screen[u'size'].height()))
if self.isLive:
if not self.firstTime:
self.createMediaObject()
log.debug(u'Setup webView')
self.webView = QtWebKit.QWebView(self)
self.webView.setGeometry(0, 0,
self.screen[u'size'].width(), self.screen[u'size'].height())
self.page = self.webView.page()
self.frame = self.page.mainFrame()
if self.isLive and log.getEffectiveLevel() == logging.DEBUG:
self.webView.settings().setAttribute(
QtWebKit.QWebSettings.DeveloperExtrasEnabled, True)
QtCore.QObject.connect(self.webView,
QtCore.SIGNAL(u'loadFinished(bool)'), self.isWebLoaded)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.frame.setScrollBarPolicy(QtCore.Qt.Vertical,
QtCore.Qt.ScrollBarAlwaysOff)
self.frame.setScrollBarPolicy(QtCore.Qt.Horizontal,
QtCore.Qt.ScrollBarAlwaysOff)
Display.setup(self)
if self.isLive:
# Build the initial frame.
image_file = QtCore.QSettings().value(u'advanced/default image',
@ -179,24 +199,6 @@ class MainDisplay(QtGui.QGraphicsView):
self.primary = True
log.debug(u'Finished MainDisplay setup')
def createMediaObject(self):
self.firstTime = False
log.debug(u'Creating Phonon objects - Start for %s', self.isLive)
self.mediaObject = Phonon.MediaObject(self)
self.audio = Phonon.AudioOutput(Phonon.VideoCategory, self.mediaObject)
Phonon.createPath(self.mediaObject, self.videoWidget)
Phonon.createPath(self.mediaObject, self.audio)
QtCore.QObject.connect(self.mediaObject,
QtCore.SIGNAL(u'stateChanged(Phonon::State, Phonon::State)'),
self.videoState)
QtCore.QObject.connect(self.mediaObject,
QtCore.SIGNAL(u'finished()'),
self.videoFinished)
QtCore.QObject.connect(self.mediaObject,
QtCore.SIGNAL(u'tick(qint64)'),
self.videoTick)
log.debug(u'Creating Phonon objects - Finished for %s', self.isLive)
def text(self, slide):
"""
Add the slide text from slideController
@ -220,8 +222,8 @@ class MainDisplay(QtGui.QGraphicsView):
The text to be displayed.
"""
log.debug(u'alert to display')
if self.height() != self.screen[u'size'].height() or not \
self.isVisible() or self.videoWidget.isVisible():
if self.height() != self.screen[u'size'].height() or \
not self.isVisible():
shrink = True
js = u'show_alert("%s", "%s")' % (
text.replace(u'\\', u'\\\\').replace(u'\"', u'\\\"'),
@ -232,22 +234,18 @@ class MainDisplay(QtGui.QGraphicsView):
text.replace(u'\\', u'\\\\').replace(u'\"', u'\\\"'))
height = self.frame.evaluateJavaScript(js)
if shrink:
if self.phononActive:
shrinkItem = self.webView
else:
shrinkItem = self
if text:
alert_height = int(height.toString())
shrinkItem.resize(self.width(), alert_height)
shrinkItem.setVisible(True)
self.resize(self.width(), alert_height)
self.setVisible(True)
if location == AlertLocation.Middle:
shrinkItem.move(self.screen[u'size'].left(),
self.move(self.screen[u'size'].left(),
(self.screen[u'size'].height() - alert_height) / 2)
elif location == AlertLocation.Bottom:
shrinkItem.move(self.screen[u'size'].left(),
self.move(self.screen[u'size'].left(),
self.screen[u'size'].height() - alert_height)
else:
shrinkItem.setVisible(False)
self.setVisible(False)
self.setGeometry(self.screen[u'size'])
def directImage(self, name, path, background):
@ -275,7 +273,7 @@ class MainDisplay(QtGui.QGraphicsView):
"""
log.debug(u'image to display')
image = self.imageManager.get_image_bytes(name)
self.resetVideo()
self.controller.mediaController.video_reset(self.controller)
self.displayImage(image)
def displayImage(self, image):
@ -302,135 +300,6 @@ class MainDisplay(QtGui.QGraphicsView):
# clear the cache
self.override = {}
def resetVideo(self):
"""
Used after Video plugin has changed the background
"""
log.debug(u'resetVideo')
if self.phononActive:
self.mediaObject.stop()
self.mediaObject.clearQueue()
self.webView.setVisible(True)
self.videoWidget.setVisible(False)
self.phononActive = False
else:
self.frame.evaluateJavaScript(u'show_video("close");')
self.override = {}
def videoPlay(self):
"""
Responds to the request to play a loaded video
"""
log.debug(u'videoPlay')
if self.phononActive:
self.mediaObject.play()
else:
self.frame.evaluateJavaScript(u'show_video("play");')
# show screen
if self.isLive:
self.setVisible(True)
def videoPause(self):
"""
Responds to the request to pause a loaded video
"""
log.debug(u'videoPause')
if self.phononActive:
self.mediaObject.pause()
else:
self.frame.evaluateJavaScript(u'show_video("pause");')
def videoStop(self):
"""
Responds to the request to stop a loaded video
"""
log.debug(u'videoStop')
if self.phononActive:
self.mediaObject.stop()
else:
self.frame.evaluateJavaScript(u'show_video("stop");')
def videoVolume(self, volume):
"""
Changes the volume of a running video
"""
log.debug(u'videoVolume %d' % volume)
vol = float(volume) / float(10)
if self.phononActive:
self.audio.setVolume(vol)
else:
self.frame.evaluateJavaScript(u'show_video(null, null, %s);' %
str(vol))
def video(self, videoPath, volume, isBackground=False):
"""
Loads and starts a video to run with the option of sound
"""
# We request a background video but have no service Item
if isBackground and not hasattr(self, u'serviceItem'):
return False
if not self.mediaObject:
self.createMediaObject()
log.debug(u'video')
self.webLoaded = True
self.setGeometry(self.screen[u'size'])
# We are running a background theme
self.override[u'theme'] = u''
self.override[u'video'] = True
vol = float(volume) / float(10)
if isBackground or not self.usePhonon:
js = u'show_video("init", "%s", %s, true); show_video("play");' % \
(videoPath.replace(u'\\', u'\\\\'), str(vol))
self.frame.evaluateJavaScript(js)
else:
self.phononActive = True
self.mediaObject.stop()
self.mediaObject.clearQueue()
self.mediaObject.setCurrentSource(Phonon.MediaSource(videoPath))
# Need the timer to trigger set the trigger to 200ms
# Value taken from web documentation.
if self.serviceItem.end_time != 0:
self.mediaObject.setTickInterval(200)
self.mediaObject.play()
self.webView.setVisible(False)
self.videoWidget.setVisible(True)
self.audio.setVolume(vol)
return True
def videoState(self, newState, oldState):
"""
Start the video at a predetermined point.
"""
if newState == Phonon.PlayingState \
and oldState != Phonon.PausedState \
and self.serviceItem.start_time > 0:
# set start time in milliseconds
self.mediaObject.seek(self.serviceItem.start_time * 1000)
def videoFinished(self):
"""
Blank the Video when it has finished so the final frame is not left
hanging
"""
self.videoStop()
self.hideDisplay(HideMode.Blank)
self.phononActive = False
self.videoHide = True
def videoTick(self, tick):
"""
Triggered on video tick every 200 milli seconds
"""
if tick > self.serviceItem.end_time * 1000:
self.videoFinished()
def isWebLoaded(self):
"""
Called by webView event to show display is fully loaded
"""
log.debug(u'Webloaded')
self.webLoaded = True
def preview(self):
"""
Generates a preview of the image displayed.
@ -510,16 +379,12 @@ class MainDisplay(QtGui.QGraphicsView):
if serviceItem.foot_text:
self.footer(serviceItem.foot_text)
# if was hidden keep it hidden
if self.hideMode and self.isLive:
if self.hideMode and self.isLive and not serviceItem.is_media():
if QtCore.QSettings().value(u'general/auto unblank',
QtCore.QVariant(False)).toBool():
Receiver.send_message(u'slidecontroller_live_unblank')
else:
self.hideDisplay(self.hideMode)
# display hidden for video end we have a new item so must be shown
if self.videoHide and self.isLive:
self.videoHide = False
self.showDisplay()
self.__hideMouse()
def footer(self, text):
@ -537,8 +402,6 @@ class MainDisplay(QtGui.QGraphicsView):
Store the images so they can be replaced when required
"""
log.debug(u'hideDisplay mode = %d', mode)
if self.phononActive:
self.videoPause()
if mode == HideMode.Screen:
self.frame.evaluateJavaScript(u'show_blank("desktop");')
self.setVisible(False)
@ -549,7 +412,6 @@ class MainDisplay(QtGui.QGraphicsView):
if mode != HideMode.Screen:
if self.isHidden():
self.setVisible(True)
if self.phononActive:
self.webView.setVisible(True)
self.hideMode = mode
@ -563,9 +425,6 @@ class MainDisplay(QtGui.QGraphicsView):
self.frame.evaluateJavaScript('show_blank("show");')
if self.isHidden():
self.setVisible(True)
if self.phononActive:
self.webView.setVisible(False)
self.videoPlay()
self.hideMode = None
# Trigger actions when display is active again
if self.isLive:

View File

@ -41,6 +41,7 @@ from openlp.core.lib.ui import UiStrings, base_action, checkable_action, \
from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, \
ThemeManager, SlideController, PluginForm, MediaDockManager, \
ShortcutListForm, FormattingTagForm
from openlp.core.ui.media import MediaController
from openlp.core.utils import AppLocation, add_actions, LanguageManager, \
get_application_version
from openlp.core.utils.actions import ActionList, CategoryOrder
@ -557,6 +558,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
self.pluginManager = PluginManager(pluginpath)
self.pluginHelpers = {}
self.imageManager = ImageManager()
self.mediaController = MediaController(self)
# Set up the interface
self.setupUi(self)
# Load settings after setupUi so default UI sizes are overwritten
@ -644,6 +646,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
self.pluginHelpers[u'toolbox'] = self.mediaDockManager
self.pluginHelpers[u'pluginmanager'] = self.pluginManager
self.pluginHelpers[u'formparent'] = self
self.pluginHelpers[u'mediacontroller'] = self.mediaController
self.pluginManager.find_plugins(pluginpath, self.pluginHelpers)
# hook methods have to happen after find_plugins. Find plugins needs
# the controllers hence the hooks have moved from setupUI() to here

View File

@ -0,0 +1,65 @@
# -*- 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 #
# 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, #
# 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 #
###############################################################################
class MediaState(object):
"""
An enumeration for possible States of the Media Player (copied partially
from Phonon::State)
"""
Loading = 0
Stopped = 1
Playing = 2
Paused = 4
Off = 6
class MediaType(object):
"""
An enumeration of possibible Media Types
"""
Unused = 0
Audio = 1
Video = 2
CD = 3
DVD = 4
Folder = 5
class MediaInfo(object):
"""
This class hold the media related infos
"""
file_info = None
volume = 100
is_flash = False
is_background = False
length = 0
start_time = 0
end_time = 0
media_type = MediaType()
from mediacontroller import MediaController

View File

@ -0,0 +1,576 @@
# -*- 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 #
# 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, #
# 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.mediaplayer import MediaPlayer
from openlp.core.lib.ui import UiStrings, critical_error_message_box
from openlp.core.ui.media import MediaState, MediaInfo, MediaType
from openlp.core.utils import AppLocation
log = logging.getLogger(__name__)
class MediaController(object):
"""
The implementation of the Media Controller. The Media Controller adds an own
class for every Player. Currently these are QtWebkit, Phonon and planed Vlc.
"""
def __init__(self, parent):
self.parent = parent
self.mediaPlayers = {}
self.controller = []
self.overridenPlayer = ''
self.curDisplayMediaPlayer = {}
# Timer for video state
self.timer = QtCore.QTimer()
self.timer.setInterval(200)
self.withLivePreview = False
self.check_available_media_players()
# Signals
QtCore.QObject.connect(self.timer,
QtCore.SIGNAL("timeout()"), self.video_state)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'media_playback_play'), self.video_play)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'media_playback_pause'), self.video_pause)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'media_playback_stop'), self.video_stop)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'seek_slider'), self.video_seek)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'volume_slider'), 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)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'media_override_player'), self.override_player)
# Signals for background video
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'songs_hide'), self.video_hide)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'songs_unblank'), self.video_unblank)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'mediaitem_media_rebuild'), self.set_active_players)
def set_active_players(self):
playerSettings = str(QtCore.QSettings().value(u'media/players',
QtCore.QVariant(u'webkit')).toString())
if len(playerSettings) == 0:
playerSettings = u'webkit'
savedPlayers = playerSettings.split(u',')
for player in self.mediaPlayers.keys():
if player in savedPlayers:
self.mediaPlayers[player].isActive = True
else:
self.mediaPlayers[player].isActive = False
def register_controllers(self, controller):
"""
Register each media Player controller (Webkit, Phonon, etc) and store
for later use
"""
if controller.check_available():
self.mediaPlayers[controller.name] = controller
def check_available_media_players(self):
"""
Check to see if we have any media Player's available. If Not do not
install the plugin.
"""
log.debug(u'check_available_media_players')
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'player.py') and \
not filename == 'media_player.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 = MediaPlayer.__subclasses__()
for controller_class in controller_classes:
controller = controller_class(self)
self.register_controllers(controller)
if self.mediaPlayers:
playerSettings = str(QtCore.QSettings().value(u'media/players',
QtCore.QVariant(u'webkit')).toString())
savedPlayers = playerSettings.split(u',')
invalidMediaPlayers = [mediaPlayer for mediaPlayer in savedPlayers \
if not mediaPlayer in self.mediaPlayers]
if len(invalidMediaPlayers)>0:
[savedPlayers.remove(invalidPlayer) for invalidPlayer in invalidMediaPlayers]
newPlayerSetting = u','.join(savedPlayers)
QtCore.QSettings().setValue(u'media/players',
QtCore.QVariant(newPlayerSetting))
self.set_active_players()
return True
else:
return False
def video_state(self):
"""
Check if there is a running media Player and do updating stuff (e.g.
update the UI)
"""
if len(self.curDisplayMediaPlayer.keys()) == 0:
self.timer.stop()
else:
for display in self.curDisplayMediaPlayer.keys():
self.curDisplayMediaPlayer[display].resize(display)
self.curDisplayMediaPlayer[display].update_ui(display)
if self.curDisplayMediaPlayer[display] \
.state == MediaState.Playing:
return
self.timer.stop()
def get_media_display_css(self):
"""
Add css style sheets to htmlbuilder
"""
css = u''
for player in self.mediaPlayers.values():
if player.isActive:
css += player.get_media_display_css()
return css
def get_media_display_javascript(self):
"""
Add javascript functions to htmlbuilder
"""
js = u''
for player in self.mediaPlayers.values():
if player.isActive:
js += player.get_media_display_javascript()
return js
def get_media_display_html(self):
"""
Add html code to htmlbuilder
"""
html = u''
for player in self.mediaPlayers.values():
if player.isActive:
html += player.get_media_display_html()
return html
def add_controller_items(self, controller, control_panel):
self.controller.append(controller)
self.setup_generic_controls(controller, control_panel)
self.setup_special_controls(controller, control_panel)
def setup_generic_controls(self, controller, control_panel):
"""
Add generic media control items (valid for all types of medias)
"""
controller.media_info = MediaInfo()
# Build a Media ToolBar
controller.mediabar = OpenLPToolbar(controller)
controller.mediabar.addToolbarButton(
u'media_playback_play', u':/slides/media_playback_start.png',
translate('OpenLP.SlideController', 'Start playing media'),
controller.sendToPlugins)
controller.mediabar.addToolbarButton(
u'media_playback_pause', u':/slides/media_playback_pause.png',
translate('OpenLP.SlideController', 'Pause playing media'),
controller.sendToPlugins)
controller.mediabar.addToolbarButton(
u'media_playback_stop', u':/slides/media_playback_stop.png',
translate('OpenLP.SlideController', 'Stop playing media'),
controller.sendToPlugins)
# Build the seekSlider.
controller.seekSlider = QtGui.QSlider(QtCore.Qt.Horizontal)
controller.seekSlider.setMaximum(1000)
controller.seekSlider.setToolTip(translate(
'OpenLP.SlideController', 'Video position.'))
controller.seekSlider.setGeometry(QtCore.QRect(90, 260, 221, 24))
controller.seekSlider.setObjectName(u'seek_slider')
controller.mediabar.addToolbarWidget(u'Seek Slider',
controller.seekSlider)
# 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.setToolTip(translate(
'OpenLP.SlideController', 'Audio Volume.'))
controller.volumeSlider.setValue(controller.media_info.volume)
controller.volumeSlider.setGeometry(QtCore.QRect(90, 160, 221, 24))
controller.volumeSlider.setObjectName(u'volume_slider')
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_special_controls(self, controller, control_panel):
"""
Special media Toolbars will be created here (e.g. for DVD Playback)
"""
controller.media_info = MediaInfo()
# TODO: add Toolbar for DVD, ...
def setup_display(self, display):
"""
After a new display is configured, all media related widget will be
created too
"""
# clean up possible running old media files
self.finalise()
# update player status
self.set_active_players()
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:
display.hasAudio = False
for player in self.mediaPlayers.values():
if player.isActive:
player.setup(display)
def set_controls_visible(self, controller, value):
# Generic controls
controller.mediabar.setVisible(value)
# Special controls: Here media type specific Controls will be enabled
# (e.g. for DVD control, ...)
# TODO
def resize(self, controller, display, player):
"""
After Mainwindow changes or Splitter moved all related media widgets
have to be resized
"""
player.resize(display)
def video(self, controller, file, muted, isBackground):
"""
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()
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
display = None
if controller.isLive:
if self.withLivePreview and controller.previewDisplay:
display = controller.previewDisplay
isValid = self.check_file_type(controller, display)
display = controller.display
isValid = self.check_file_type(controller, display)
display.override[u'theme'] = u''
display.override[u'video'] = True
controller.media_info.start_time = display.serviceItem.start_time
controller.media_info.end_time = display.serviceItem.end_time
elif controller.previewDisplay:
display = controller.previewDisplay
isValid = self.check_file_type(controller, display)
if not isValid:
# Media could not be loaded correctly
critical_error_message_box(
translate('MediaPlugin.MediaItem', 'Unsupported File'),
unicode(translate('MediaPlugin.MediaItem',
'Unsupported File')))
return False
# dont care about actual theme, set a black background
if controller.isLive and ( \
controller.media_info.is_background == False):
display.frame.evaluateJavaScript(u'show_video( \
"setBackBoard", null, null, null,"visible");')
# now start playing
if controller.isLive and \
(QtCore.QSettings().value(u'general/auto unblank',
QtCore.QVariant(False)).toBool() or \
controller.media_info.is_background == True) or \
controller.isLive == False:
if not self.video_play([controller]):
critical_error_message_box(
translate('MediaPlugin.MediaItem', 'Unsupported File'),
unicode(translate('MediaPlugin.MediaItem',
'Unsupported File')))
return False
self.set_controls_visible(controller, True)
log.debug(u'use %s controller' % self.curDisplayMediaPlayer[display])
return True
def check_file_type(self, controller, display):
"""
Used to choose the right media Player type from the prioritized Player list
"""
playerSettings = str(QtCore.QSettings().value(u'media/players',
QtCore.QVariant(u'webkit')).toString())
usedPlayers = playerSettings.split(u',')
if QtCore.QSettings().value(u'media/override player',
QtCore.QVariant(QtCore.Qt.Unchecked)) == QtCore.Qt.Checked:
if self.overridenPlayer != '':
usedPlayers = [self.overridenPlayer]
if controller.media_info.file_info.isFile():
suffix = u'*.%s' % controller.media_info.file_info.suffix().toLower()
for title in usedPlayers:
player = self.mediaPlayers[title]
if suffix in player.video_extensions_list:
if not controller.media_info.is_background or \
controller.media_info.is_background and player.canBackground:
self.resize(controller, display, player)
if player.load(display):
self.curDisplayMediaPlayer[display] = player
controller.media_info.media_type = MediaType.Video
return True
if suffix in player.audio_extensions_list:
if player.load(display):
self.curDisplayMediaPlayer[display] = player
controller.media_info.media_type = MediaType.Audio
return True
else:
for title in usedPlayers:
player = self.mediaPlayers[title]
if player.canFolder:
self.resize(controller, display, player)
if player.load(display):
self.curDisplayMediaPlayer[display] = player
controller.media_info.media_type = MediaType.Video
return True
# no valid player found
return False
def video_play(self, msg, status=True):
"""
Responds to the request to play a loaded video
``msg``
First element is the controller which should be used
"""
log.debug(u'video_play')
controller = msg[0]
for display in self.curDisplayMediaPlayer.keys():
if display.controller == controller:
if not self.curDisplayMediaPlayer[display].play(display):
return False
if status:
display.frame.evaluateJavaScript(u'show_blank("desktop");')
self.curDisplayMediaPlayer[display].set_visible(display, True)
if controller.isLive:
if controller.hideMenu.defaultAction().isChecked():
controller.hideMenu.defaultAction().trigger()
# Start Timer for ui updates
if not self.timer.isActive():
self.timer.start()
return True
def video_pause(self, msg):
"""
Responds to the request to pause a loaded video
``msg``
First element is the controller which should be used
"""
log.debug(u'video_pause')
controller = msg[0]
for display in self.curDisplayMediaPlayer.keys():
if display.controller == controller:
self.curDisplayMediaPlayer[display].pause(display)
def video_stop(self, msg):
"""
Responds to the request to stop a loaded video
``msg``
First element is the controller which should be used
"""
log.debug(u'video_stop')
controller = msg[0]
for display in self.curDisplayMediaPlayer.keys():
if display.controller == controller:
display.frame.evaluateJavaScript(u'show_blank("black");')
self.curDisplayMediaPlayer[display].stop(display)
self.curDisplayMediaPlayer[display].set_visible(display, False)
def video_volume(self, msg):
"""
Changes the volume of a running video
``msg``
First element is the controller which should be used
"""
controller = msg[0]
vol = msg[1][0]
log.debug(u'video_volume %d' % vol)
for display in self.curDisplayMediaPlayer.keys():
if display.controller == controller:
self.curDisplayMediaPlayer[display].volume(display, vol)
def video_seek(self, msg):
"""
Responds to the request to change the seek Slider of a loaded video
``msg``
First element is the controller which should be used
Second element is a list with the seek Value as first element
"""
log.debug(u'video_seek')
controller = msg[0]
seekVal = msg[1][0]
for display in self.curDisplayMediaPlayer.keys():
if display.controller == controller:
self.curDisplayMediaPlayer[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.curDisplayMediaPlayer.keys():
if display.controller == controller:
display.override = {}
self.curDisplayMediaPlayer[display].reset(display)
self.curDisplayMediaPlayer[display].set_visible(display, False)
display.frame.evaluateJavaScript(u'show_video( \
"setBackBoard", null, null, null,"hidden");')
del self.curDisplayMediaPlayer[display]
self.set_controls_visible(controller, False)
def video_hide(self, msg):
"""
Hide the related video Widget
``msg``
First element is the boolean for Live indication
"""
isLive = msg[1]
if isLive:
controller = self.parent.liveController
for display in self.curDisplayMediaPlayer.keys():
if display.controller == controller:
if self.curDisplayMediaPlayer[display] \
.state == MediaState.Playing:
self.curDisplayMediaPlayer[display].pause(display)
self.curDisplayMediaPlayer[display] \
.set_visible(display, False)
def video_blank(self, msg):
"""
Blank the related video Widget
``msg``
First element is the boolean for Live indication
Second element is the hide mode
"""
isLive = msg[1]
hide_mode = msg[2]
if isLive:
Receiver.send_message(u'live_display_hide', hide_mode)
controller = self.parent.liveController
for display in self.curDisplayMediaPlayer.keys():
if display.controller == controller:
if self.curDisplayMediaPlayer[display] \
.state == MediaState.Playing:
self.curDisplayMediaPlayer[display].pause(display)
self.curDisplayMediaPlayer[display] \
.set_visible(display, False)
def video_unblank(self, msg):
"""
Unblank the related video Widget
``msg``
First element is not relevant in this context
Second element is the boolean for Live indication
"""
Receiver.send_message(u'live_display_show')
isLive = msg[1]
if isLive:
controller = self.parent.liveController
for display in self.curDisplayMediaPlayer.keys():
if display.controller == controller:
if self.curDisplayMediaPlayer[display] \
.state == MediaState.Paused:
if self.curDisplayMediaPlayer[display].play(display):
self.curDisplayMediaPlayer[display] \
.set_visible(display, True)
# Start Timer for ui updates
if not self.timer.isActive():
self.timer.start()
def get_audio_extensions_list(self):
audio_list = []
for player in self.mediaPlayers.values():
if player.isActive:
for item in player.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 player in self.mediaPlayers.values():
if player.isActive:
for item in player.video_extensions_list:
if not item in video_list:
video_list.append(item)
return video_list
def override_player(self, override_player):
playerSettings = str(QtCore.QSettings().value(u'media/players',
QtCore.QVariant(u'webkit')).toString())
usedPlayers = playerSettings.split(u',')
if override_player in usedPlayers:
self.overridenPlayer = override_player
else:
self.overridenPlayer = ''
def finalise(self):
self.timer.stop()
for controller in self.controller:
self.video_reset(controller)

View File

@ -0,0 +1,201 @@
# -*- 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 #
# 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, #
# 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 mimetypes
from datetime import datetime
from PyQt4 import QtCore, QtGui
from PyQt4.phonon import Phonon
from openlp.core.lib import Receiver
from openlp.core.lib.mediaplayer import MediaPlayer
from openlp.core.ui.media import MediaState
log = logging.getLogger(__name__)
ADDITIONAL_EXT = {
u'audio/ac3': [u'.ac3'],
u'audio/flac': [u'.flac'],
u'audio/x-m4a': [u'.m4a'],
u'audio/midi': [u'.mid', u'.midi'],
u'audio/x-mp3': [u'.mp3'],
u'audio/mpeg': [u'.mp3', u'.mp2', u'.mpga', u'.mpega', u'.m4a'],
u'audio/qcelp': [u'.qcp'],
u'audio/x-wma': [u'.wma'],
u'audio/x-ms-wma': [u'.wma'],
u'video/x-flv': [u'.flv'],
u'video/x-matroska': [u'.mpv', u'.mkv'],
u'video/x-wmv': [u'.wmv'],
u'video/x-mpg': [u'.mpg'],
u'video/x-ms-wmv': [u'.wmv']}
class PhononPlayer(MediaPlayer):
"""
A specialised version of the MediaPlayer class, which provides a Phonon
display.
"""
def __init__(self, parent):
MediaPlayer.__init__(self, parent, u'phonon')
self.parent = parent
self.additional_extensions = ADDITIONAL_EXT
mimetypes.init()
for mimetype in Phonon.BackendCapabilities.availableMimeTypes():
mimetype = unicode(mimetype)
if mimetype.startswith(u'audio/'):
self._addToList(self.audio_extensions_list, mimetype)
elif mimetype.startswith(u'video/'):
self._addToList(self.video_extensions_list, mimetype)
def _addToList(self, list, mimetype):
# Add all extensions which mimetypes provides us for supported types.
extensions = mimetypes.guess_all_extensions(unicode(mimetype))
for extension in extensions:
ext = u'*%s' % extension
if ext not in list:
list.append(ext)
log.info(u'MediaPlugin: %s extensions: %s' % (mimetype,
u' '.join(extensions)))
# Add extensions for this mimetype from self.additional_extensions.
# This hack clears mimetypes' and operating system's shortcomings
# by providing possibly missing extensions.
if mimetype in self.additional_extensions.keys():
for extension in self.additional_extensions[mimetype]:
ext = u'*%s' % extension
if ext not in list:
list.append(ext)
log.info(u'MediaPlugin: %s additional extensions: %s' % (mimetype,
u' '.join(self.additional_extensions[mimetype])))
def setup(self, display):
display.phononWidget = Phonon.VideoWidget(display)
display.phononWidget.resize(display.size())
display.mediaObject = Phonon.MediaObject(display)
Phonon.createPath(display.mediaObject, display.phononWidget)
if display.hasAudio:
display.audio = Phonon.AudioOutput( \
Phonon.VideoCategory, display.mediaObject)
Phonon.createPath(display.mediaObject, display.audio)
display.phononWidget.raise_()
display.phononWidget.hide()
self.hasOwnWidget = True
def check_available(self):
return True
def load(self, display):
log.debug(u'load vid in Phonon Controller')
controller = display.controller
volume = controller.media_info.volume
path = controller.media_info.file_info.absoluteFilePath()
display.mediaObject.setCurrentSource(Phonon.MediaSource(path))
if not self.media_state_wait(display, Phonon.StoppedState):
return False
self.volume(display, volume)
return True
def media_state_wait(self, display, mediaState):
"""
Wait for the video to change its state
Wait no longer than 5 seconds.
"""
start = datetime.now()
current_state = display.mediaObject.state()
while current_state != mediaState:
current_state = display.mediaObject.state()
if current_state == Phonon.ErrorState:
return False
Receiver.send_message(u'openlp_process_events')
if (datetime.now() - start).seconds > 5:
return False
return True
def resize(self, display):
display.phononWidget.resize(display.size())
def play(self, display):
controller = display.controller
start_time = 0
if display.mediaObject.state() != Phonon.PausedState and \
controller.media_info.start_time > 0:
start_time = controller.media_info.start_time
display.mediaObject.play()
if self.media_state_wait(display, Phonon.PlayingState):
if start_time > 0:
self.seek(display, controller.media_info.start_time*1000)
self.volume(display, controller.media_info.volume)
controller.media_info.length = \
int(display.mediaObject.totalTime()/1000)
controller.seekSlider.setMaximum(controller.media_info.length*1000)
self.state = MediaState.Playing
display.phononWidget.raise_()
return True
else:
return False
def pause(self, display):
display.mediaObject.pause()
if self.media_state_wait(display, Phonon.PausedState):
self.state = MediaState.Paused
def stop(self, display):
display.mediaObject.stop()
self.set_visible(display, False)
self.state = MediaState.Stopped
def volume(self, display, vol):
# 1.0 is the highest value
if display.hasAudio:
vol = float(vol) / float(100)
display.audio.setVolume(vol)
def seek(self, display, seekVal):
display.mediaObject.seek(seekVal)
def reset(self, display):
display.mediaObject.stop()
display.mediaObject.clearQueue()
self.set_visible(display, False)
display.phononWidget.setVisible(False)
self.state = MediaState.Off
def set_visible(self, display, status):
if self.hasOwnWidget:
display.phononWidget.setVisible(status)
def update_ui(self, display):
controller = display.controller
if controller.media_info.end_time > 0:
if display.mediaObject.currentTime() > \
controller.media_info.end_time*1000:
self.stop(display)
self.set_visible(display, False)
if not controller.seekSlider.isSliderDown():
controller.seekSlider.setSliderPosition( \
display.mediaObject.currentTime())

View File

@ -0,0 +1,426 @@
# -*- 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 #
# 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, #
# 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
from PyQt4 import QtCore, QtGui, QtWebKit
from openlp.core.lib import OpenLPToolbar, translate
from openlp.core.lib.mediaplayer import MediaPlayer
from openlp.core.ui.media import MediaState
log = logging.getLogger(__name__)
VIDEO_CSS = u"""
#videobackboard {
z-index:3;
background-color: black;
}
#video1 {
z-index:4;
}
#video2 {
z-index:4;
}
"""
VIDEO_JS = u"""
var video_timer = null;
var current_video = '1';
function show_video(state, path, volume, loop, varVal){
// Note, the preferred method for looping would be to use the
// video tag loop attribute.
// But QtWebKit doesn't support this. Neither does it support the
// onended event, hence the setInterval()
// In addition, setting the currentTime attribute to zero to restart
// the video raises an INDEX_SIZE_ERROR: DOM Exception 1
// To complicate it further, sometimes vid.currentTime stops
// slightly short of vid.duration and vid.ended is intermittent!
//
// Note, currently the background may go black between loops. Not
// desirable. Need to investigate using two <video>'s, and hiding/
// preloading one, and toggle between the two when looping.
if(current_video=='1'){
var vid = document.getElementById('video1');
var vid2 = document.getElementById('video2');
} else {
var vid = document.getElementById('video2');
var vid2 = document.getElementById('video1');
}
if(volume != null){
vid.volume = volume;
vid2.volume = volume;
}
switch(state){
case 'init':
vid.src = 'file:///' + path;
vid2.src = 'file:///' + path;
if(loop == null) loop = false;
vid.looping = loop;
vid2.looping = loop;
vid.load();
break;
case 'load':
vid2.style.visibility = 'hidden';
vid2.load();
break;
case 'play':
vid.play();
if(vid.looping){
video_timer = setInterval(
function() {
show_video('poll');
}, 200);
}
break;
case 'pause':
if(video_timer!=null){
clearInterval(video_timer);
video_timer = null;
}
vid.pause();
break;
case 'stop':
show_video('pause');
vid.currentTime = 0;
break;
case 'poll':
if(vid.ended||vid.currentTime+0.2>vid.duration)
show_video('swap');
break;
case 'swap':
show_video('pause');
if(current_video=='1')
current_video = '2';
else
current_video = '1';
show_video('load');
show_video('play');
show_video('setVisible',null,null,null,'visible');
break;
case 'close':
show_video('stop');
vid.src = '';
vid2.src = '';
break;
case 'length':
return vid.duration;
case 'currentTime':
return vid.currentTime;
case 'seek':
// doesnt work currently
vid.currentTime = varVal;
break;
case 'setVisible':
vid.style.visibility = varVal;
break;
case 'setBackBoard':
var back = document.getElementById('videobackboard');
back.style.visibility = varVal;
break;
}
}
"""
VIDEO_HTML = u"""
<div id="videobackboard" class="size" style="visibility:hidden"></div>
<video id="video1" class="size" style="visibility:hidden" autobuffer preload>
</video>
<video id="video2" class="size" style="visibility:hidden" autobuffer preload>
</video>
"""
FLASH_CSS = u"""
#flash {
z-index:5;
}
"""
FLASH_JS = u"""
function getFlashMovieObject(movieName)
{
if (window.document[movieName])
{
return window.document[movieName];
}
if (document.embeds && document.embeds[movieName])
return document.embeds[movieName];
}
function show_flash(state, path, volume, varVal){
var text = document.getElementById('flash');
var flashMovie = getFlashMovieObject("OpenLPFlashMovie");
var src = "src = 'file:///" + path + "'";
var view_parm = " wmode='opaque'" +
" width='100%%'" +
" height='100%%'";
var swf_parm = " name='OpenLPFlashMovie'" +
" autostart='true' loop='false' play='true'" +
" hidden='false' swliveconnect='true' allowscriptaccess='always'" +
" volume='" + volume + "'";
switch(state){
case 'load':
text.innerHTML = "<embed " + src + view_parm + swf_parm + "/>";
flashMovie = getFlashMovieObject("OpenLPFlashMovie");
flashMovie.Play();
break;
case 'play':
flashMovie.Play();
break;
case 'pause':
flashMovie.StopPlay();
break;
case 'stop':
flashMovie.StopPlay();
tempHtml = text.innerHTML;
text.innerHTML = '';
text.innerHTML = tempHtml;
break;
case 'close':
flashMovie.StopPlay();
text.innerHTML = '';
break;
case 'length':
return flashMovie.TotalFrames();
case 'currentTime':
return flashMovie.CurrentFrame();
case 'seek':
// flashMovie.GotoFrame(varVal);
break;
case 'setVisible':
text.style.visibility = varVal;
break;
}
}
"""
FLASH_HTML = u"""
<div id="flash" class="size" style="visibility:hidden"></div>
"""
VIDEO_EXT = [
u'*.3gp'
, u'*.3gpp'
, u'*.3g2'
, u'*.3gpp2'
, u'*.aac'
, u'*.flv'
, u'*.f4a'
, u'*.f4b'
, u'*.f4p'
, u'*.f4v'
, u'*.mov'
, u'*.m4a'
, u'*.m4b'
, u'*.m4p'
, u'*.m4v'
, u'*.mkv'
, u'*.mp4'
, u'*.ogv'
, u'*.webm'
, u'*.mpg', u'*.wmv', u'*.mpeg', u'*.avi'
, u'*.swf'
]
AUDIO_EXT = [
u'*.mp3'
, u'*.ogg'
]
class WebkitPlayer(MediaPlayer):
"""
A specialised version of the MediaPlayer class, which provides a QtWebKit
display.
"""
def __init__(self, parent):
MediaPlayer.__init__(self, parent, u'webkit')
self.parent = parent
self.canBackground = 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
"""
return VIDEO_CSS + FLASH_CSS
def get_media_display_javascript(self):
"""
Add javascript functions to htmlbuilder
"""
return VIDEO_JS + FLASH_JS
def get_media_display_html(self):
"""
Add html code to htmlbuilder
"""
return VIDEO_HTML + FLASH_HTML
def setup(self, display):
display.webView.resize(display.size())
display.webView.raise_()
self.hasOwnWidget = False
def check_available(self):
return True
def load(self, display):
log.debug(u'load vid in Webkit Controller')
controller = display.controller
if display.hasAudio:
volume = controller.media_info.volume
vol = float(volume) / float(100)
else:
vol = 0
path = controller.media_info.file_info.absoluteFilePath()
if controller.media_info.is_background:
loop = u'true'
else:
loop = u'false'
display.webView.setVisible(True)
if controller.media_info.file_info.suffix() == u'swf':
controller.media_info.is_flash = True
js = u'show_flash("load","%s");' % \
(path.replace(u'\\', u'\\\\'))
else:
js = u'show_video("init", "%s", %s, %s);' % \
(path.replace(u'\\', u'\\\\'), str(vol), loop)
display.frame.evaluateJavaScript(js)
return True
def resize(self, display):
controller = display.controller
display.webView.resize(display.size())
def play(self, display):
controller = display.controller
display.webLoaded = True
length = 0
start_time = 0
if self.state != MediaState.Paused and \
controller.media_info.start_time > 0:
start_time = controller.media_info.start_time
self.set_visible(display, True)
if controller.media_info.is_flash:
display.frame.evaluateJavaScript(u'show_flash("play");')
else:
display.frame.evaluateJavaScript(u'show_video("play");')
if start_time > 0:
self.seek(display, controller.media_info.start_time*1000)
# TODO add playing check and get the correct media length
controller.media_info.length = length
self.state = MediaState.Playing
display.webView.raise_()
return True
def pause(self, display):
controller = display.controller
if controller.media_info.is_flash:
display.frame.evaluateJavaScript(u'show_flash("pause");')
else:
display.frame.evaluateJavaScript(u'show_video("pause");')
self.state = MediaState.Paused
def stop(self, display):
controller = display.controller
if controller.media_info.is_flash:
display.frame.evaluateJavaScript(u'show_flash("stop");')
else:
display.frame.evaluateJavaScript(u'show_video("stop");')
controller.seekSlider.setSliderPosition(0)
self.state = MediaState.Stopped
def volume(self, display, vol):
controller = display.controller
# 1.0 is the highest value
if display.hasAudio:
vol = float(vol) / float(100)
if not controller.media_info.is_flash:
display.frame.evaluateJavaScript(
u'show_video(null, null, %s);' % str(vol))
def seek(self, display, seekVal):
controller = display.controller
if controller.media_info.is_flash:
seek = seekVal
display.frame.evaluateJavaScript( \
u'show_flash("seek", null, null, "%s");' % (seek))
else:
seek = float(seekVal)/1000
display.frame.evaluateJavaScript( \
u'show_video("seek", null, null, null, "%f");' % (seek))
def reset(self, display):
controller = display.controller
if controller.media_info.is_flash:
display.frame.evaluateJavaScript(u'show_flash("close");')
else:
display.frame.evaluateJavaScript(u'show_video("close");')
self.state = MediaState.Off
def set_visible(self, display, status):
controller = display.controller
if status:
is_visible = "visible"
else:
is_visible = "hidden"
if controller.media_info.is_flash:
display.frame.evaluateJavaScript(u'show_flash( \
"setVisible", null, null, "%s");' % (is_visible))
else:
display.frame.evaluateJavaScript(u'show_video( \
"setVisible", null, null, null, "%s");' % (is_visible))
def update_ui(self, display):
controller = display.controller
if controller.media_info.is_flash:
currentTime = display.frame.evaluateJavaScript( \
u'show_flash("currentTime");').toInt()[0]
length = display.frame.evaluateJavaScript( \
u'show_flash("length");').toInt()[0]
else:
(currentTime, ok) = display.frame.evaluateJavaScript( \
u'show_video("currentTime");').toFloat()
# check if conversion was ok and value is not 'NaN'
if ok and currentTime != float('inf'):
currentTime = int(currentTime*1000)
(length, ok) = display.frame.evaluateJavaScript( \
u'show_video("length");').toFloat()
# check if conversion was ok and value is not 'NaN'
if ok and length != float('inf'):
length = int(length*1000)
if currentTime > 0:
controller.media_info.length = length
controller.seekSlider.setMaximum(length)
if not controller.seekSlider.isSliderDown():
controller.seekSlider.setSliderPosition(currentTime)

View File

@ -25,18 +25,19 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import logging
import os
import logging
import time
import copy
from collections import deque
from PyQt4 import QtCore, QtGui
from PyQt4.phonon import Phonon
from openlp.core.lib import OpenLPToolbar, Receiver, ItemCapabilities, \
translate, build_icon
translate, build_icon, ServiceItem, build_html, PluginManager, ServiceItem
from openlp.core.lib.ui import UiStrings, shortcut_action
from openlp.core.ui import HideMode, MainDisplay, ScreenList
from openlp.core.ui import HideMode, MainDisplay, Display, ScreenList
from openlp.core.utils.actions import ActionList, CategoryOrder
log = logging.getLogger(__name__)
@ -49,8 +50,29 @@ class SlideList(QtGui.QTableWidget):
def __init__(self, parent=None, name=None):
QtGui.QTableWidget.__init__(self, parent.controller)
class Controller(QtGui.QWidget):
"""
Controller is a general controller widget.
"""
def __init__(self, parent, isLive=False):
"""
Set up the general Controller.
"""
QtGui.QWidget.__init__(self, parent)
self.isLive = isLive
self.display = None
class SlideController(QtGui.QWidget):
def sendToPlugins(self, *args):
"""
This is the generic function to send signal for control widgets,
created from within other plugins
This function is needed to catch the current controller
"""
sender = self.sender().objectName() if self.sender().objectName() else self.sender().text()
controller = self
Receiver.send_message('%s' % sender, [controller, args])
class SlideController(Controller):
"""
SlideController is the slide controller widget. This widget is what the
user uses to control the displaying of verses/slides/etc on the screen.
@ -59,13 +81,12 @@ class SlideController(QtGui.QWidget):
"""
Set up the Slide Controller.
"""
QtGui.QWidget.__init__(self, parent)
self.isLive = isLive
self.display = None
Controller.__init__(self, parent, isLive)
self.screens = ScreenList.get_instance()
self.ratio = float(self.screens.current[u'size'].width()) / \
float(self.screens.current[u'size'].height())
self.imageManager = self.parent().imageManager
self.mediaController = self.parent().mediaController
self.loopList = [
u'Play Slides Menu',
u'Loop Separator',
@ -74,7 +95,10 @@ class SlideController(QtGui.QWidget):
self.songEditList = [
u'Edit Song',
]
self.volume = 10
self.nextPreviousList = [
u'Previous Slide',
u'Next Slide'
]
self.timer_id = 0
self.songEdit = False
self.selectedRow = 0
@ -91,6 +115,8 @@ class SlideController(QtGui.QWidget):
self.typeLabel.setText(UiStrings().Live)
self.split = 1
self.typePrefix = u'live'
self.keypress_queue = deque()
self.keypress_loop = False
else:
self.typeLabel.setText(UiStrings().Preview)
self.split = 0
@ -138,14 +164,14 @@ class SlideController(QtGui.QWidget):
self.toolbar.sizePolicy().hasHeightForWidth())
self.toolbar.setSizePolicy(sizeToolbarPolicy)
self.previousItem = self.toolbar.addToolbarButton(
translate('OpenLP.SlideController', 'Previous Slide'),
u'Previous Slide',
u':/slides/slide_previous.png',
translate('OpenLP.SlideController', 'Move to previous.'),
self.onSlideSelectedPrevious,
shortcuts=[QtCore.Qt.Key_Up, QtCore.Qt.Key_PageUp],
context=QtCore.Qt.WidgetWithChildrenShortcut)
self.nextItem = self.toolbar.addToolbarButton(
translate('OpenLP.SlideController', 'Next Slide'),
u'Next Slide',
u':/slides/slide_next.png',
translate('OpenLP.SlideController', 'Move to next.'),
self.onSlideSelectedNext,
@ -232,20 +258,8 @@ class SlideController(QtGui.QWidget):
'Edit and reload song preview.'),
self.onEditSong)
self.controllerLayout.addWidget(self.toolbar)
# Build a Media ToolBar
self.mediabar = OpenLPToolbar(self)
self.mediabar.addToolbarButton(
u'Media Start', u':/slides/media_playback_start.png',
translate('OpenLP.SlideController', 'Start playing media.'),
self.onMediaPlay)
self.mediabar.addToolbarButton(
u'Media Pause', u':/slides/media_playback_pause.png',
translate('OpenLP.SlideController', 'Start playing media.'),
self.onMediaPause)
self.mediabar.addToolbarButton(
u'Media Stop', u':/slides/media_playback_stop.png',
translate('OpenLP.SlideController', 'Start playing media.'),
self.onMediaStop)
# Build the Media Toolbar
self.mediaController.add_controller_items(self, self.controllerLayout)
if self.isLive:
# Build the Song Toolbar
self.songMenu = QtGui.QToolButton(self.toolbar)
@ -261,23 +275,6 @@ class SlideController(QtGui.QWidget):
translate('OpenLP.SlideController', 'Pause audio.'),
self.onAudioPauseClicked, True)
self.audioPauseItem.setVisible(False)
# Build the volumeSlider.
self.volumeSlider = QtGui.QSlider(QtCore.Qt.Horizontal)
self.volumeSlider.setTickInterval(1)
self.volumeSlider.setTickPosition(QtGui.QSlider.TicksAbove)
self.volumeSlider.setMinimum(0)
self.volumeSlider.setMaximum(10)
else:
# Build the seekSlider.
self.seekSlider = Phonon.SeekSlider()
self.seekSlider.setGeometry(QtCore.QRect(90, 260, 221, 24))
self.seekSlider.setObjectName(u'seekSlider')
self.mediabar.addToolbarWidget(u'Seek Slider', self.seekSlider)
self.volumeSlider = Phonon.VolumeSlider()
self.volumeSlider.setGeometry(QtCore.QRect(90, 260, 221, 24))
self.volumeSlider.setObjectName(u'volumeSlider')
self.mediabar.addToolbarWidget(u'Audio Volume', self.volumeSlider)
self.controllerLayout.addWidget(self.mediabar)
# Screen preview area
self.previewFrame = QtGui.QFrame(self.splitter)
self.previewFrame.setGeometry(QtCore.QRect(0, 0, 300, 300 * self.ratio))
@ -294,17 +291,13 @@ class SlideController(QtGui.QWidget):
self.slideLayout = QtGui.QVBoxLayout()
self.slideLayout.setSpacing(0)
self.slideLayout.setMargin(0)
self.slideLayout.setObjectName(u'slideLayout')
if not self.isLive:
self.mediaObject = Phonon.MediaObject(self)
self.video = Phonon.VideoWidget()
self.video.setVisible(False)
self.audio = Phonon.AudioOutput(Phonon.VideoCategory,
self.mediaObject)
Phonon.createPath(self.mediaObject, self.video)
Phonon.createPath(self.mediaObject, self.audio)
self.video.setGeometry(QtCore.QRect(0, 0, 300, 225))
self.slideLayout.insertWidget(0, self.video)
self.slideLayout.setObjectName(u'SlideLayout')
self.previewDisplay = Display(self, self.isLive, self)
self.previewDisplay.setGeometry(QtCore.QRect(0, 0, 300, 300))
self.previewDisplay.screen = {u'size':self.previewDisplay.geometry()}
self.previewDisplay.setup()
self.slideLayout.insertWidget(0, self.previewDisplay)
self.previewDisplay.hide()
# Actual preview screen
self.slidePreview = QtGui.QLabel(self)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed,
@ -413,8 +406,6 @@ class SlideController(QtGui.QWidget):
QtCore.QObject.connect(self.previewListWidget,
QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onSlideSelected)
if self.isLive:
QtCore.QObject.connect(self.volumeSlider,
QtCore.SIGNAL(u'sliderReleased()'), self.mediaVolume)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'slidecontroller_live_spin_delay'),
self.receiveSpinDelay)
@ -424,7 +415,6 @@ class SlideController(QtGui.QWidget):
QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
self.onGoLiveClick)
self.toolbar.makeWidgetsInvisible(self.songEditList)
self.mediabar.setVisible(False)
if self.isLive:
self.setLiveHotkeys(self)
self.__addActionsToWidget(self.previewListWidget)
@ -575,15 +565,37 @@ class SlideController(QtGui.QWidget):
def liveEscape(self):
self.display.setVisible(False)
self.display.videoStop()
self.mediaController.video_stop([self])
def servicePrevious(self):
time.sleep(0.1)
Receiver.send_message('servicemanager_previous_item')
"""
Live event to select the previous service item from the service manager.
"""
self.keypress_queue.append(u'previous')
self._process_queue()
def serviceNext(self):
time.sleep(0.1)
Receiver.send_message('servicemanager_next_item')
"""
Live event to select the next service item from the service manager.
"""
self.keypress_queue.append(u'next')
self._process_queue()
def _process_queue(self):
"""
Process the service item request queue. The key presses can arrive
faster than the processing so implement a FIFO queue.
"""
if len(self.keypress_queue):
while len(self.keypress_queue) and not self.keypress_loop:
self.keypress_loop = True
if self.keypress_queue.popleft() == u'previous':
Receiver.send_message('servicemanager_previous_item')
else:
Receiver.send_message('servicemanager_next_item')
self.keypress_loop = False
def screenSizeChanged(self):
"""
@ -593,14 +605,22 @@ class SlideController(QtGui.QWidget):
# rebuild display as screen size changed
if self.display:
self.display.close()
self.display = MainDisplay(self, self.imageManager, self.isLive)
self.display = MainDisplay(self, self.imageManager, self.isLive,
self)
self.display.setup()
if self.isLive:
self.__addActionsToWidget(self.display)
# The SlidePreview's ratio.
self.ratio = float(self.screens.current[u'size'].width()) / \
float(self.screens.current[u'size'].height())
self.mediaController.setup_display(self.display)
self.previewSizeChanged()
self.previewDisplay.setup()
serviceItem = ServiceItem()
self.previewDisplay.webView.setHtml(build_html(serviceItem,
self.previewDisplay.screen, None, self.isLive, None,
plugins=PluginManager.get_instance().plugins))
self.mediaController.setup_display(self.previewDisplay)
if self.serviceItem:
self.refreshServiceItem()
@ -622,11 +642,17 @@ class SlideController(QtGui.QWidget):
max_height = self.previewFrame.height() - self.grid.margin() * 2
self.slidePreview.setFixedSize(QtCore.QSize(max_height * self.ratio,
max_height))
self.previewDisplay.setFixedSize(QtCore.QSize(max_height * self.ratio,
max_height))
self.previewDisplay.screen = {u'size':self.previewDisplay.geometry()}
else:
# We have to take the width as limit.
max_width = self.previewFrame.width() - self.grid.margin() * 2
self.slidePreview.setFixedSize(QtCore.QSize(max_width,
max_width / self.ratio))
self.previewDisplay.setFixedSize(QtCore.QSize(max_width,
max_width / self.ratio))
self.previewDisplay.screen = {u'size':self.previewDisplay.geometry()}
# Make sure that the frames have the correct size.
self.previewListWidget.setColumnWidth(0,
self.previewListWidget.viewport().size().width())
@ -687,12 +713,13 @@ class SlideController(QtGui.QWidget):
len(item.get_frames()) > 1:
self.toolbar.makeWidgetsVisible(self.loopList)
if item.is_media():
self.toolbar.setVisible(False)
self.mediabar.setVisible(True)
self.toolbar.makeWidgetsInvisible(self.nextPreviousList)
else:
# Work-around for OS X, hide and then show the toolbar
# See bug #791050
self.toolbar.show()
self.toolbar.makeWidgetsVisible(self.nextPreviousList)
self.toolbar.show()
def enablePreviewToolBar(self, item):
"""
@ -706,13 +733,13 @@ class SlideController(QtGui.QWidget):
if item.is_capable(ItemCapabilities.CanEdit) and item.from_plugin:
self.toolbar.makeWidgetsVisible(self.songEditList)
elif item.is_media():
self.toolbar.setVisible(False)
self.mediabar.setVisible(True)
self.volumeSlider.setAudioOutput(self.audio)
self.toolbar.makeWidgetsInvisible(self.nextPreviousList)
if not item.is_media():
# Work-around for OS X, hide and then show the toolbar
# See bug #791050
self.toolbar.show()
self.toolbar.makeWidgetsVisible(self.nextPreviousList)
self.toolbar.show()
def refreshServiceItem(self):
"""
@ -771,7 +798,7 @@ class SlideController(QtGui.QWidget):
log.debug(u'processManagerItem live = %s' % self.isLive)
self.onStopLoop()
old_item = self.serviceItem
# take a copy not a link to the servicemeanager copy.
# take a copy not a link to the servicemanager copy.
self.serviceItem = copy.copy(serviceItem)
if old_item and self.isLive and old_item.is_capable(
ItemCapabilities.ProvidesOwnDisplay):
@ -1301,72 +1328,18 @@ class SlideController(QtGui.QWidget):
"""
log.debug(u'SlideController onMediaStart')
file = os.path.join(item.get_frame_path(), item.get_frame_title())
if self.isLive:
self.display.video(file, self.volume)
self.volumeSlider.setValue(self.volume)
else:
self.mediaObject.stop()
self.mediaObject.clearQueue()
self.mediaObject.setCurrentSource(Phonon.MediaSource(file))
self.seekSlider.setMediaObject(self.mediaObject)
self.seekSlider.show()
self.onMediaPlay()
def mediaVolume(self):
"""
Respond to the release of Volume Slider
"""
log.debug(u'SlideController mediaVolume')
self.volume = self.volumeSlider.value()
self.display.videoVolume(self.volume)
def onMediaPause(self):
"""
Respond to the Pause from the media Toolbar
"""
log.debug(u'SlideController onMediaPause')
if self.isLive:
self.display.videoPause()
else:
self.mediaObject.pause()
def onMediaPlay(self):
"""
Respond to the Play from the media Toolbar
"""
log.debug(u'SlideController onMediaPlay')
if self.isLive:
self.display.videoPlay()
else:
self.mediaController.video(self, file, False, False)
if not self.isLive or self.mediaController.withLivePreview:
self.previewDisplay.show()
self.slidePreview.hide()
self.video.show()
self.mediaObject.play()
def onMediaStop(self):
"""
Respond to the Stop from the media Toolbar
"""
log.debug(u'SlideController onMediaStop')
if self.isLive:
self.display.videoStop()
else:
self.mediaObject.stop()
self.video.hide()
self.slidePreview.clear()
self.slidePreview.show()
def onMediaClose(self):
"""
Respond to a request to close the Video
"""
log.debug(u'SlideController onMediaStop')
if self.isLive:
self.display.resetVideo()
else:
self.mediaObject.stop()
self.mediaObject.clearQueue()
self.video.hide()
self.slidePreview.clear()
log.debug(u'SlideController onMediaClose')
self.mediaController.video_reset(self)
self.previewDisplay.hide()
self.slidePreview.show()
def _resetBlank(self):

View File

@ -25,7 +25,6 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
from datetime import datetime
import logging
import os
import locale
@ -34,12 +33,17 @@ from PyQt4 import QtCore, QtGui
from PyQt4.phonon import Phonon
from openlp.core.lib import MediaManagerItem, build_icon, ItemCapabilities, \
SettingsManager, translate, check_item_selected, Receiver, MediaType
from openlp.core.lib.ui import UiStrings, critical_error_message_box
SettingsManager, translate, check_item_selected, Receiver, MediaType, \
ServiceItem, build_html
from openlp.core.lib.ui import UiStrings, critical_error_message_box, \
media_item_combo_box
from openlp.core.ui import Controller, Display
log = logging.getLogger(__name__)
CLAPPERBOARD = QtGui.QImage(u':/media/media_video.png')
#TODO: Add an appropriate Icon for DVDs, CDs, ...
DVD_ICON = QtGui.QImage(u':/media/media_video.png')
class MediaMediaItem(MediaManagerItem):
"""
@ -51,16 +55,40 @@ class MediaMediaItem(MediaManagerItem):
self.iconPath = u'images/image'
self.background = False
self.previewFunction = CLAPPERBOARD
self.Automatic = u''
MediaManagerItem.__init__(self, parent, plugin, icon)
self.singleServiceItem = False
self.hasSearch = True
self.mediaObject = None
self.mediaController = Controller(parent)
self.mediaController.controllerLayout = QtGui.QVBoxLayout()
self.plugin.mediaController.add_controller_items(self.mediaController, \
self.mediaController.controllerLayout)
self.plugin.mediaController.set_controls_visible(self.mediaController, \
False)
self.mediaController.previewDisplay = Display(self.mediaController, \
False, self.mediaController)
self.mediaController.previewDisplay.setGeometry(
QtCore.QRect(0, 0, 300, 300))
self.mediaController.previewDisplay.screen = \
{u'size':self.mediaController.previewDisplay.geometry()}
self.mediaController.previewDisplay.setup()
serviceItem = ServiceItem()
self.mediaController.previewDisplay.webView.setHtml(build_html( \
serviceItem, self.mediaController.previewDisplay.screen, None, \
False, None))
self.mediaController.previewDisplay.setup()
self.plugin.mediaController.setup_display( \
self.mediaController.previewDisplay)
self.mediaController.previewDisplay.hide()
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'video_background_replaced'),
self.videobackgroundReplaced)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'openlp_phonon_creation'),
self.createPhonon)
QtCore.SIGNAL(u'mediaitem_media_rebuild'), self.rebuild)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'config_screen_changed'), self.displaySetup)
# Allow DnD from the desktop
self.listView.activateDnD()
@ -74,6 +102,10 @@ class MediaMediaItem(MediaManagerItem):
self.replaceAction.setToolTip(UiStrings().ReplaceLiveBG)
self.resetAction.setText(UiStrings().ResetBG)
self.resetAction.setToolTip(UiStrings().ResetLiveBG)
self.Automatic = translate('MediaPlugin.MediaItem',
'Automatic')
self.displayTypeLabel.setText(
translate('MediaPlugin.MediaItem', 'Use Player:'))
def requiredIcons(self):
MediaManagerItem.requiredIcons(self)
@ -92,13 +124,34 @@ class MediaMediaItem(MediaManagerItem):
self.resetAction = self.addToolbarButton(u'', u'',
u':/system/system_close.png', self.onResetClick, False)
self.resetAction.setVisible(False)
self.mediaWidget = QtGui.QWidget(self)
self.mediaWidget.setObjectName(u'mediaWidget')
self.displayLayout = QtGui.QFormLayout(self.mediaWidget)
self.displayLayout.setMargin(self.displayLayout.spacing())
self.displayLayout.setObjectName(u'displayLayout')
self.displayTypeLabel = QtGui.QLabel(self.mediaWidget)
self.displayTypeLabel.setObjectName(u'displayTypeLabel')
self.displayTypeComboBox = media_item_combo_box(
self.mediaWidget, u'displayTypeComboBox')
self.displayTypeLabel.setBuddy(self.displayTypeComboBox)
self.displayLayout.addRow(self.displayTypeLabel,
self.displayTypeComboBox)
# Add the Media widget to the page layout
self.pageLayout.addWidget(self.mediaWidget)
QtCore.QObject.connect(self.displayTypeComboBox,
QtCore.SIGNAL(u'currentIndexChanged (int)'), self.overridePlayerChanged)
def overridePlayerChanged(self, index):
Receiver.send_message(u'media_override_player', \
u'%s' % self.displayTypeComboBox.currentText())
def onResetClick(self):
"""
Called to reset the Live backgound with the media selected.
Called to reset the Live background with the media selected,
"""
self.plugin.liveController.mediaController.video_reset( \
self.plugin.liveController)
self.resetAction.setVisible(False)
self.plugin.liveController.display.resetVideo()
def videobackgroundReplaced(self):
"""
@ -108,7 +161,7 @@ class MediaMediaItem(MediaManagerItem):
def onReplaceClick(self):
"""
Called to replace Live backgound with the media selected.
Called to replace Live background with the media selected.
"""
if check_item_selected(self.listView,
translate('MediaPlugin.MediaItem',
@ -116,8 +169,8 @@ class MediaMediaItem(MediaManagerItem):
item = self.listView.currentItem()
filename = unicode(item.data(QtCore.Qt.UserRole).toString())
if os.path.exists(filename):
(path, name) = os.path.split(filename)
if self.plugin.liveController.display.video(filename, 0, True):
if self.plugin.liveController.mediaController.video( \
self.plugin.liveController, filename, True, True):
self.resetAction.setVisible(True)
else:
critical_error_message_box(UiStrings().LiveBGError,
@ -144,30 +197,18 @@ class MediaMediaItem(MediaManagerItem):
unicode(translate('MediaPlugin.MediaItem',
'The file %s no longer exists.')) % filename)
return False
self.mediaObject.stop()
self.mediaObject.clearQueue()
self.mediaObject.setCurrentSource(Phonon.MediaSource(filename))
if not self.mediaStateWait(Phonon.StoppedState):
critical_error_message_box(UiStrings().UnsupportedFile,
UiStrings().UnsupportedFile)
self.mediaLength = 0
if self.plugin.mediaController.video( \
self.mediaController, filename, False, False):
self.mediaLength = self.mediaController.media_info.length
service_item.media_length = self.mediaLength
self.plugin.mediaController.video_reset(self.mediaController)
if self.mediaLength > 0:
service_item.add_capability(
ItemCapabilities.HasVariableStartTime)
else:
return False
# File too big for processing
if os.path.getsize(filename) <= 52428800: # 50MiB
self.mediaObject.play()
if not self.mediaStateWait(Phonon.PlayingState) \
or self.mediaObject.currentSource().type() \
== Phonon.MediaSource.Invalid:
self.mediaObject.stop()
critical_error_message_box(
translate('MediaPlugin.MediaItem', 'File Too Big'),
translate('MediaPlugin.MediaItem', 'The file you are '
'trying to load is too big. Please reduce it to less '
'than 50MiB.'))
return False
self.mediaObject.stop()
service_item.media_length = self.mediaObject.totalTime() / 1000
service_item.add_capability(
ItemCapabilities.HasVariableStartTime)
service_item.media_length = self.mediaLength
service_item.title = unicode(self.plugin.nameStrings[u'singular'])
service_item.add_capability(ItemCapabilities.RequiresMedia)
# force a non-existent theme
@ -177,23 +218,49 @@ class MediaMediaItem(MediaManagerItem):
service_item.add_from_command(path, name, frame)
return True
def mediaStateWait(self, mediaState):
"""
Wait for the video to change its state. Wait no longer than 5 seconds.
"""
start = datetime.now()
while self.mediaObject.state() != mediaState:
if self.mediaObject.state() == Phonon.ErrorState:
return False
Receiver.send_message(u'openlp_process_events')
if (datetime.now() - start).seconds > 5:
return False
return True
def initialise(self):
self.listView.clear()
self.listView.setIconSize(QtCore.QSize(88, 50))
self.loadList(SettingsManager.load_list(self.settingsSection, u'media'))
self.populateDisplayTypes()
def rebuild(self):
"""
Rebuild the tab in the media manager when changes are made in
the settings
"""
self.populateDisplayTypes()
self.onNewFileMasks = unicode(translate('MediaPlugin.MediaItem',
'Videos (%s);;Audio (%s);;%s (*)')) % (
u' '.join(self.plugin.video_extensions_list),
u' '.join(self.plugin.audio_extensions_list), UiStrings().AllFiles)
def displaySetup(self):
self.plugin.mediaController.setup_display( \
self.mediaController.previewDisplay)
def populateDisplayTypes(self):
"""
Load the combobox with the enabled media players,
allowing user to select a specific player if settings allow
"""
self.displayTypeComboBox.clear()
playerSettings = str(QtCore.QSettings().value(u'media/players',
QtCore.QVariant(u'webkit')).toString())
usedPlayers = playerSettings.split(u',')
for title in usedPlayers:
# load the drop down selection
self.displayTypeComboBox.addItem(title)
if self.displayTypeComboBox.count() > 1:
self.displayTypeComboBox.insertItem(0, self.Automatic)
self.displayTypeComboBox.setCurrentIndex(0)
if QtCore.QSettings().value(self.settingsSection + u'/override player',
QtCore.QVariant(QtCore.Qt.Unchecked)) == QtCore.Qt.Checked:
self.mediaWidget.show()
else:
self.mediaWidget.hide()
def onDeleteClick(self):
"""
@ -214,10 +281,18 @@ class MediaMediaItem(MediaManagerItem):
media.sort(cmp=locale.strcoll,
key=lambda filename: os.path.split(unicode(filename))[1].lower())
for track in media:
filename = os.path.split(unicode(track))[1]
item_name = QtGui.QListWidgetItem(filename)
item_name.setIcon(build_icon(CLAPPERBOARD))
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(track))
track_info = QtCore.QFileInfo(track)
if not track_info.isFile():
filename = os.path.split(unicode(track))[1]
item_name = QtGui.QListWidgetItem(filename)
item_name.setIcon(build_icon(CLAPPERBOARD))
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(track))
else:
filename = os.path.split(unicode(track))[1]
item_name = QtGui.QListWidgetItem(filename)
#TODO: add the appropriate Icon
#item_name.setIcon(build_icon(DVD_ICON))
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(track))
item_name.setToolTip(track)
self.listView.addItem(item_name)
@ -234,11 +309,6 @@ class MediaMediaItem(MediaManagerItem):
media = filter(lambda x: os.path.splitext(x)[1] in ext, media)
return media
def createPhonon(self):
log.debug(u'CreatePhonon')
if not self.mediaObject:
self.mediaObject = Phonon.MediaObject(self)
def search(self, string):
files = SettingsManager.load_list(self.settingsSection, u'media')
results = []

View File

@ -28,51 +28,181 @@
from PyQt4 import QtCore, QtGui
from openlp.core.lib import SettingsTab, translate, Receiver
from openlp.core.lib.ui import UiStrings, critical_error_message_box
class MediaTab(SettingsTab):
"""
MediaTab is the Media settings tab in the settings dialog.
"""
def __init__(self, parent, title, visible_title, icon_path):
def __init__(self, parent, title, visible_title, media_players, icon_path):
self.media_players = media_players
SettingsTab.__init__(self, parent, title, visible_title, icon_path)
def setupUi(self):
self.setObjectName(u'MediaTab')
SettingsTab.setupUi(self)
self.mediaModeGroupBox = QtGui.QGroupBox(self.leftColumn)
self.mediaModeGroupBox.setObjectName(u'mediaModeGroupBox')
self.mediaModeLayout = QtGui.QFormLayout(self.mediaModeGroupBox)
self.mediaModeLayout.setObjectName(u'mediaModeLayout')
self.usePhononCheckBox = QtGui.QCheckBox(self.mediaModeGroupBox)
self.usePhononCheckBox.setObjectName(u'usePhononCheckBox')
self.mediaModeLayout.addRow(self.usePhononCheckBox)
self.leftLayout.addWidget(self.mediaModeGroupBox)
self.mediaPlayerGroupBox = QtGui.QGroupBox(self.leftColumn)
self.mediaPlayerGroupBox.setObjectName(u'mediaPlayerGroupBox')
self.mediaPlayerLayout = QtGui.QVBoxLayout(self.mediaPlayerGroupBox)
self.mediaPlayerLayout.setObjectName(u'mediaPlayerLayout')
self.PlayerCheckBoxes = {}
for key in self.media_players:
player = self.media_players[key]
checkbox = QtGui.QCheckBox(self.mediaPlayerGroupBox)
checkbox.setEnabled(player.available)
checkbox.setObjectName(player.name + u'CheckBox')
self.PlayerCheckBoxes[player.name] = checkbox
self.mediaPlayerLayout.addWidget(checkbox)
self.leftLayout.addWidget(self.mediaPlayerGroupBox)
self.playerOrderGroupBox = QtGui.QGroupBox(self.leftColumn)
self.playerOrderGroupBox.setObjectName(u'playerOrderGroupBox')
self.playerOrderLayout = QtGui.QVBoxLayout(self.playerOrderGroupBox)
self.playerOrderLayout.setObjectName(u'playerOrderLayout')
self.playerOrderlistWidget = QtGui.QListWidget( \
self.playerOrderGroupBox)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum,
QtGui.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.playerOrderlistWidget. \
sizePolicy().hasHeightForWidth())
self.playerOrderlistWidget.setSizePolicy(sizePolicy)
self.playerOrderlistWidget.setVerticalScrollBarPolicy( \
QtCore.Qt.ScrollBarAsNeeded)
self.playerOrderlistWidget.setHorizontalScrollBarPolicy( \
QtCore.Qt.ScrollBarAlwaysOff)
self.playerOrderlistWidget.setEditTriggers( \
QtGui.QAbstractItemView.NoEditTriggers)
self.playerOrderlistWidget.setObjectName(u'playerOrderlistWidget')
self.playerOrderLayout.addWidget(self.playerOrderlistWidget)
self.orderingButtonsWidget = QtGui.QWidget(self.playerOrderGroupBox)
self.orderingButtonsWidget.setObjectName(u'orderingButtonsWidget')
self.orderingButtonLayout = QtGui.QHBoxLayout( \
self.orderingButtonsWidget)
self.orderingButtonLayout.setObjectName(u'orderingButtonLayout')
self.orderingDownButton = QtGui.QPushButton(self.orderingButtonsWidget)
self.orderingDownButton.setObjectName(u'orderingDownButton')
self.orderingButtonLayout.addWidget(self.orderingDownButton)
self.orderingUpButton = QtGui.QPushButton(self.playerOrderGroupBox)
self.orderingUpButton.setObjectName(u'orderingUpButton')
self.orderingButtonLayout.addWidget(self.orderingUpButton)
self.playerOrderLayout.addWidget(self.orderingButtonsWidget)
self.leftLayout.addWidget(self.playerOrderGroupBox)
self.AdvancedGroupBox = QtGui.QGroupBox(self.leftColumn)
self.AdvancedGroupBox.setObjectName(u'AdvancedGroupBox')
self.AdvancedLayout = QtGui.QVBoxLayout(self.AdvancedGroupBox)
self.AdvancedLayout.setObjectName(u'AdvancedLayout')
self.OverridePlayerCheckBox = QtGui.QCheckBox(self.AdvancedGroupBox)
self.OverridePlayerCheckBox.setObjectName(u'OverridePlayerCheckBox')
self.AdvancedLayout.addWidget(self.OverridePlayerCheckBox)
self.leftLayout.addWidget(self.AdvancedGroupBox)
self.leftLayout.addStretch()
self.rightLayout.addStretch()
QtCore.QObject.connect(self.usePhononCheckBox,
QtCore.SIGNAL(u'stateChanged(int)'),
self.onUsePhononCheckBoxChanged)
for key in self.media_players:
player = self.media_players[key]
checkbox = self.PlayerCheckBoxes[player.name]
QtCore.QObject.connect(checkbox,
QtCore.SIGNAL(u'stateChanged(int)'),
self.onPlayerCheckBoxChanged)
QtCore.QObject.connect(self.orderingUpButton,
QtCore.SIGNAL(u'pressed()'), self.onOrderingUpButtonPressed)
QtCore.QObject.connect(self.orderingDownButton,
QtCore.SIGNAL(u'pressed()'), self.onOrderingDownButtonPressed)
def retranslateUi(self):
self.mediaModeGroupBox.setTitle(
translate('MediaPlugin.MediaTab', 'Media Display'))
self.usePhononCheckBox.setText(
translate('MediaPlugin.MediaTab', 'Use Phonon for video playback'))
self.mediaPlayerGroupBox.setTitle(
translate('MediaPlugin.MediaTab', 'Available Media Players'))
for key in self.media_players:
player = self.media_players[key]
checkbox = self.PlayerCheckBoxes[player.name]
if player.available:
checkbox.setText(player.name)
else:
checkbox.setText(
unicode(translate('MediaPlugin.MediaTab',
'%s (unavailable)')) % player.name)
self.playerOrderGroupBox.setTitle(
translate('MediaPlugin.MediaTab', 'Player Order'))
self.orderingDownButton.setText(
translate('MediaPlugin.MediaTab', 'Down'))
self.orderingUpButton.setText(
translate('MediaPlugin.MediaTab', 'Up'))
self.AdvancedGroupBox.setTitle(UiStrings().Advanced)
self.OverridePlayerCheckBox.setText(
translate('MediaPlugin.MediaTab',
'Allow media player to be overriden'))
def onUsePhononCheckBoxChanged(self, check_state):
self.usePhonon = (check_state == QtCore.Qt.Checked)
self.usePhononChanged = True
def onPlayerCheckBoxChanged(self, check_state):
player = self.sender().text()
if check_state == QtCore.Qt.Checked:
if player not in self.usedPlayers:
self.usedPlayers.append(player)
else:
self.usedPlayers.takeAt(self.usedPlayers.indexOf(player))
self.updatePlayerList()
def updatePlayerList(self):
self.playerOrderlistWidget.clear()
for player in self.usedPlayers:
if player in self.PlayerCheckBoxes.keys():
if len(self.usedPlayers) == 1:
# at least one media player have to stay active
self.PlayerCheckBoxes[u'%s' % player].setEnabled(False)
else:
self.PlayerCheckBoxes[u'%s' % player].setEnabled(True)
self.playerOrderlistWidget.addItem(player)
def onOrderingUpButtonPressed(self):
currentRow = self.playerOrderlistWidget.currentRow()
if currentRow > 0:
item = self.playerOrderlistWidget.takeItem(currentRow)
self.playerOrderlistWidget.insertItem(currentRow - 1, item)
self.playerOrderlistWidget.setCurrentRow(currentRow - 1)
self.usedPlayers.move(currentRow, currentRow - 1)
def onOrderingDownButtonPressed(self):
currentRow = self.playerOrderlistWidget.currentRow()
if currentRow < self.playerOrderlistWidget.count() - 1:
item = self.playerOrderlistWidget.takeItem(currentRow)
self.playerOrderlistWidget.insertItem(currentRow + 1, item)
self.playerOrderlistWidget.setCurrentRow(currentRow + 1)
self.usedPlayers.move(currentRow, currentRow + 1)
def load(self):
self.usePhonon = QtCore.QSettings().value(
self.settingsSection + u'/use phonon',
QtCore.QVariant(True)).toBool()
self.usePhononCheckBox.setChecked(self.usePhonon)
self.usedPlayers = QtCore.QSettings().value(
self.settingsSection + u'/players',
QtCore.QVariant(u'webkit')).toString().split(u',')
for key in self.media_players:
player = self.media_players[key]
checkbox = self.PlayerCheckBoxes[player.name]
if player.available and player.name in self.usedPlayers:
checkbox.setChecked(True)
self.updatePlayerList()
self.OverridePlayerCheckBox.setChecked(QtCore.QSettings().value(
self.settingsSection + u'/override player',
QtCore.QVariant(QtCore.Qt.Unchecked)).toInt()[0])
def save(self):
oldUsePhonon = QtCore.QSettings().value(
u'media/use phonon', QtCore.QVariant(True)).toBool()
if oldUsePhonon != self.usePhonon:
QtCore.QSettings().setValue(self.settingsSection + u'/use phonon',
QtCore.QVariant(self.usePhonon))
override_changed = False
player_string_changed = False
oldPlayerString = QtCore.QSettings().value(
self.settingsSection + u'/players',
QtCore.QVariant(u'webkit')).toString()
newPlayerString = self.usedPlayers.join(u',')
if oldPlayerString != newPlayerString:
# clean old Media stuff
QtCore.QSettings().setValue(self.settingsSection + u'/players',
QtCore.QVariant(newPlayerString))
player_string_changed = True
override_changed = True
setting_key = self.settingsSection + u'/override player'
if QtCore.QSettings().value(setting_key) != \
self.OverridePlayerCheckBox.checkState():
QtCore.QSettings().setValue(setting_key,
QtCore.QVariant(self.OverridePlayerCheckBox.checkState()))
override_changed = True
if override_changed:
Receiver.send_message(u'mediaitem_media_rebuild')
if player_string_changed:
Receiver.send_message(u'mediaitem_media_rebuild')
Receiver.send_message(u'config_screen_changed')

View File

@ -26,9 +26,7 @@
###############################################################################
import logging
import mimetypes
from PyQt4.phonon import Phonon
import os
from openlp.core.lib import Plugin, StringContent, build_icon, translate
from openlp.plugins.media.lib import MediaMediaItem, MediaTab
@ -40,57 +38,28 @@ class MediaPlugin(Plugin):
def __init__(self, plugin_helpers):
Plugin.__init__(self, u'media', plugin_helpers,
MediaMediaItem, MediaTab)
MediaMediaItem)
self.weight = -6
self.icon_path = u':/plugins/plugin_media.png'
self.icon = build_icon(self.icon_path)
# passed with drag and drop messages
self.dnd_id = u'Media'
self.additional_extensions = {
u'audio/ac3': [u'.ac3'],
u'audio/flac': [u'.flac'],
u'audio/x-m4a': [u'.m4a'],
u'audio/midi': [u'.mid', u'.midi'],
u'audio/x-mp3': [u'.mp3'],
u'audio/mpeg': [u'.mp3', u'.mp2', u'.mpga', u'.mpega', u'.m4a'],
u'audio/qcelp': [u'.qcp'],
u'audio/x-wma': [u'.wma'],
u'audio/x-ms-wma': [u'.wma'],
u'video/x-flv': [u'.flv'],
u'video/x-matroska': [u'.mpv', u'.mkv'],
u'video/x-wmv': [u'.wmv'],
u'video/x-ms-wmv': [u'.wmv']}
self.audio_extensions_list = []
self.video_extensions_list = []
mimetypes.init()
for mimetype in Phonon.BackendCapabilities.availableMimeTypes():
mimetype = unicode(mimetype)
if mimetype.startswith(u'audio/'):
self._addToList(self.audio_extensions_list, mimetype)
elif mimetype.startswith(u'video/'):
self._addToList(self.video_extensions_list, mimetype)
self.audio_extensions_list = \
self.mediaController.get_audio_extensions_list()
for ext in self.audio_extensions_list:
self.serviceManager.supportedSuffixes(ext[2:])
self.video_extensions_list = \
self.mediaController.get_video_extensions_list()
for ext in self.video_extensions_list:
self.serviceManager.supportedSuffixes(ext[2:])
def _addToList(self, list, mimetype):
# Add all extensions which mimetypes provides us for supported types.
extensions = mimetypes.guess_all_extensions(unicode(mimetype))
for extension in extensions:
ext = u'*%s' % extension
if ext not in list:
list.append(ext)
self.serviceManager.supportedSuffixes(extension[1:])
log.info(u'MediaPlugin: %s extensions: %s' % (mimetype,
u' '.join(extensions)))
# Add extensions for this mimetype from self.additional_extensions.
# This hack clears mimetypes' and operating system's shortcomings
# by providing possibly missing extensions.
if mimetype in self.additional_extensions.keys():
for extension in self.additional_extensions[mimetype]:
ext = u'*%s' % extension
if ext not in list:
list.append(ext)
self.serviceManager.supportedSuffixes(extension[1:])
log.info(u'MediaPlugin: %s additional extensions: %s' % (mimetype,
u' '.join(self.additional_extensions[mimetype])))
def getSettingsTab(self, parent):
"""
Create the settings Tab
"""
visible_name = self.getString(StringContent.VisibleName)
return MediaTab(parent, self.name, visible_name[u'title'],
self.mediaController.mediaPlayers, self.icon_path)
def about(self):
about_text = translate('MediaPlugin', '<strong>Media Plugin</strong>'
@ -123,3 +92,29 @@ class MediaPlugin(Plugin):
'Add the selected media to the service.')
}
self.setPluginUiTextStrings(tooltips)
def finalise(self):
"""
Time to tidy up on exit
"""
log.info(u'Media Finalising')
self.mediaController.finalise()
Plugin.finalise(self)
def getDisplayCss(self):
"""
Add css style sheets to htmlbuilder
"""
return self.mediaController.get_media_display_css()
def getDisplayJavaScript(self):
"""
Add javascript functions to htmlbuilder
"""
return self.mediaController.get_media_display_javascript()
def getDisplayHtml(self):
"""
Add html code to htmlbuilder
"""
return self.mediaController.get_media_display_html()

View File

@ -30,10 +30,11 @@ window.OpenLP = {
$("#notes").html("");
for (idx in data.results.items) {
idx = parseInt(idx, 10);
if ((data.results.items[idx]["selected"]) &&
(data.results.items.length > idx + 1)) {
$("#notes").html(data.results.items[idx]["notes"]);
OpenLP.nextSong = data.results.items[idx + 1]["title"];
if (data.results.items[idx]["selected"]) {
$("#notes").html(data.results.items[idx]["notes"].replace(/\n/g, "<br />"));
if (data.results.items.length > idx + 1) {
OpenLP.nextSong = data.results.items[idx + 1]["title"];
}
break;
}
}

View File

@ -278,13 +278,11 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
if len(verse_tag) > 1:
index = VerseType.from_translated_string(verse_tag)
if index is None:
index = VerseType.from_string(verse_tag)
index = VerseType.from_string(verse_tag, None)
else:
verse_tags_translated = True
if index is None:
index = VerseType.from_tag(verse_tag)
if index is None:
index = VerseType.Other
verse[0][u'type'] = VerseType.Tags[index]
if verse[0][u'label'] == u'':
verse[0][u'label'] = u'1'
@ -308,7 +306,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
for verse_def in self.song.verse_order.split():
verse_index = None
if verse_tags_translated:
verse_index = VerseType.from_translated_tag(verse_def[0])
verse_index = VerseType.from_translated_tag(verse_def[0],
None)
if verse_index is None:
verse_index = VerseType.from_tag(verse_def[0])
verse_tag = VerseType.TranslatedTags[verse_index].upper()
@ -608,14 +607,14 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
order_names = unicode(self.verseOrderEdit.text()).split()
for item in order_names:
if len(item) == 1:
verse_index = VerseType.from_translated_tag(item)
verse_index = VerseType.from_translated_tag(item, None)
if verse_index is not None:
order.append(VerseType.Tags[verse_index] + u'1')
else:
# it matches no verses anyway
order.append(u'')
else:
verse_index = VerseType.from_translated_tag(item[0])
verse_index = VerseType.from_translated_tag(item[0], None)
if verse_index is None:
# it matches no verses anyway
order.append(u'')

View File

@ -113,7 +113,7 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
verse_num = int(match.group(2))
except ValueError:
verse_num = 1
verse_type_index = VerseType.from_loose_input(verse_tag)
verse_type_index = VerseType.from_loose_input(verse_tag, None)
if verse_type_index is not None:
self.verseNumberBox.setValue(verse_num)
@ -140,7 +140,7 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
match = VERSE_REGEX.match(text)
if match:
verse_type = match.group(1)
verse_type_index = VerseType.from_loose_input(verse_type)
verse_type_index = VerseType.from_loose_input(verse_type, None)
try:
verse_number = int(match.group(2))
except ValueError:
@ -153,7 +153,7 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
tag=u'%s1' % VerseType.Tags[VerseType.Verse]):
self.hasSingleVerse = single
if single:
verse_type_index = VerseType.from_tag(tag[0])
verse_type_index = VerseType.from_tag(tag[0], None)
verse_number = tag[1:]
if verse_type_index is not None:
self.verseTypeComboBox.setCurrentIndex(verse_type_index)

View File

@ -69,7 +69,7 @@ class VerseType(object):
TranslatedTags = [name[0].lower() for name in TranslatedNames]
@staticmethod
def translated_tag(verse_tag, strict=False):
def translated_tag(verse_tag, default=Other):
"""
Return the translated UPPERCASE tag for a given tag,
used to show translated verse tags in UI
@ -77,86 +77,84 @@ class VerseType(object):
``verse_tag``
The string to return a VerseType for
``strict``
Determines if the default Other or None should be returned
``default``
Default return value if no matching tag is found
"""
if strict:
not_found_value = None
else:
not_found_value = VerseType.TranslatedTags[VerseType.Other].upper()
verse_tag = verse_tag[0].lower()
for num, tag in enumerate(VerseType.Tags):
if verse_tag == tag:
return VerseType.TranslatedTags[num].upper()
return not_found_value
if default in VerseType.TranslatedTags:
return VerseType.TranslatedTags[default].upper()
@staticmethod
def translated_name(verse_tag, strict=False):
def translated_name(verse_tag, default=Other):
"""
Return the translated name for a given tag
``verse_tag``
The string to return a VerseType for
``strict``
Determines if the default Other or None should be returned
``default``
Default return value if no matching tag is found
"""
if strict:
not_found_value = None
else:
not_found_value = VerseType.TranslatedNames[VerseType.Other]
verse_tag = verse_tag[0].lower()
for num, tag in enumerate(VerseType.Tags):
if verse_tag == tag:
return VerseType.TranslatedNames[num]
return not_found_value
if default in VerseType.TranslatedNames:
return VerseType.TranslatedNames[default]
@staticmethod
def from_tag(verse_tag, strict=False):
def from_tag(verse_tag, default=Other):
"""
Return the VerseType for a given tag
``verse_tag``
The string to return a VerseType for
``strict``
Determines if the default Other or None should be returned
``default``
Default return value if no matching tag is found
"""
if strict:
no_return_value = None
else:
no_return_value = VerseType.Other
verse_tag = verse_tag[0].lower()
for num, tag in enumerate(VerseType.Tags):
if verse_tag == tag:
return num
return no_return_value
return default
@staticmethod
def from_translated_tag(verse_tag):
def from_translated_tag(verse_tag, default=Other):
"""
Return the VerseType for a given tag
``verse_tag``
The string to return a VerseType for
``default``
Default return value if no matching tag is found
"""
verse_tag = verse_tag[0].lower()
for num, tag in enumerate(VerseType.TranslatedTags):
if verse_tag == tag:
return num
return default
@staticmethod
def from_string(verse_name):
def from_string(verse_name, default=Other):
"""
Return the VerseType for a given string
``verse_name``
The string to return a VerseType for
``default``
Default return value if no matching tag is found
"""
verse_name = verse_name.lower()
for num, name in enumerate(VerseType.Names):
if verse_name == name.lower():
return num
return default
@staticmethod
def from_translated_string(verse_name):
@ -172,23 +170,26 @@ class VerseType(object):
return num
@staticmethod
def from_loose_input(verse_name):
def from_loose_input(verse_name, default=Other):
"""
Return the VerseType for a given string, Other if not found
Return the VerseType for a given string
``verse_name``
The string to return a VerseType for
``default``
Default return value if no matching tag is found
"""
verse_index = None
if len(verse_name) > 1:
verse_index = VerseType.from_translated_string(verse_name)
if verse_index is None:
verse_index = VerseType.from_string(verse_name)
verse_index = VerseType.from_string(verse_name, default)
elif len(verse_name) == 1:
verse_index = VerseType.from_translated_tag(verse_name, None)
if verse_index is None:
verse_index = VerseType.from_translated_tag(verse_name)
if verse_index is None:
verse_index = VerseType.from_tag(verse_name)
verse_index = VerseType.from_tag(verse_name, default)
else:
return default
return verse_index
def retrieve_windows_encoding(recommendation=None):

View File

@ -457,7 +457,7 @@ class SongMediaItem(MediaManagerItem):
verse_index = \
VerseType.from_translated_string(verse_tag)
if verse_index is None:
verse_index = VerseType.from_string(verse_tag)
verse_index = VerseType.from_string(verse_tag, None)
if verse_index is None:
verse_index = VerseType.from_tag(verse_tag)
verse_tag = VerseType.TranslatedTags[verse_index].upper()
@ -479,8 +479,6 @@ class SongMediaItem(MediaManagerItem):
else:
verse_index = VerseType.from_tag(
verse[0][u'type'])
if verse_index is None:
verse_index = VerseType.Other
verse_tag = VerseType.TranslatedTags[verse_index]
verse_def = u'%s%s' % (verse_tag,
verse[0][u'label'])

View File

@ -124,7 +124,7 @@ class OpenLPSongImport(SongImport):
if has_media_files:
source_media_files_table = source_meta.tables[u'media_files']
source_media_files_songs_table = \
source_meta.tables[u'media_files_songs']
source_meta.tables.get(u'media_files_songs')
try:
class_mapper(OldMediaFile)
except UnmappedClassError:
@ -137,8 +137,13 @@ class OpenLPSongImport(SongImport):
secondary=source_songs_topics_table)
}
if has_media_files:
song_props['media_files'] = relation(OldMediaFile, backref='songs',
secondary=source_media_files_songs_table)
if source_media_files_songs_table:
song_props['media_files'] = relation(OldMediaFile,
backref='songs',
secondary=source_media_files_songs_table)
else:
song_props['media_files'] = relation(OldMediaFile,
backref='songs')
try:
class_mapper(OldAuthor)
except UnmappedClassError:

View File

@ -67,9 +67,9 @@ def upgrade_1(session, metadata, tables):
"""
Table(u'media_files_songs', metadata, autoload=True).drop(checkfirst=True)
Column(u'song_id', types.Integer(), default=None)\
.create(table=tables[u'media_files'], populate_default=True)
.create(table=tables[u'media_files'])
Column(u'weight', types.Integer(), default=0)\
.create(table=tables[u'media_files'], populate_default=True)
.create(table=tables[u'media_files'])
if metadata.bind.url.get_dialect().name != 'sqlite':
# SQLite doesn't support ALTER TABLE ADD CONSTRAINT
ForeignKeyConstraint([u'song_id'], [u'songs.id'],
@ -82,6 +82,7 @@ def upgrade_2(session, metadata, tables):
This upgrade adds a create_date and last_modified date to the songs table
"""
Column(u'create_date', types.DateTime(), default=func.now())\
.create(table=tables[u'songs'], populate_default=True)
.create(table=tables[u'songs'])
Column(u'last_modified', types.DateTime(), default=func.now())\
.create(table=tables[u'songs'], populate_default=True)
.create(table=tables[u'songs'])

View File

@ -1,95 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MediaFilesDialog</class>
<widget class="QDialog" name="MediaFilesDialog">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Select Media File(s)</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="filesVerticalLayout">
<property name="spacing">
<number>8</number>
</property>
<property name="margin">
<number>8</number>
</property>
<item>
<widget class="QLabel" name="selectLabel">
<property name="text">
<string>Select one or more audio files from the list below, and click OK to import them into this song.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QListView" name="fileListView">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../images/openlp-2.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>MediaFilesDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>MediaFilesDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,30 @@
# -*- 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 #
# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler, #
# Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, #
# Jeffrey Smith, 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 #
###############################################################################
hiddenimports = ['openlp.core.ui.media.phononplayer',
'openlp.core.ui.media.vlcplayer',
'openlp.core.ui.media.webkitplayer']

View File

@ -233,6 +233,19 @@ def copy_plugins():
copy(os.path.join(root, filename),
os.path.join(dest_path, filename))
def copy_media_player():
print u'Copying media player...'
source = os.path.join(source_path, u'core', u'ui', u'media')
dest = os.path.join(dist_path, u'core', u'ui', u'media')
for root, dirs, files in os.walk(source):
for filename in files:
if not filename.endswith(u'.pyc'):
dest_path = os.path.join(dest, root[len(source)+1:])
if not os.path.exists(dest_path):
os.makedirs(dest_path)
copy(os.path.join(root, filename),
os.path.join(dest_path, filename))
def copy_windows_files():
print u'Copying extra files for Windows...'
copy(os.path.join(winres_path, u'OpenLP.ico'),
@ -355,6 +368,7 @@ def main():
run_pyinstaller()
write_version_file()
copy_plugins()
copy_media_player()
if os.path.exists(manual_path):
run_sphinx()
run_htmlhelp()