openlp-core team mailing list archive
-
openlp-core team
-
Mailing list archive
-
Message #11706
[Merge] lp:~raoul-snyman/openlp/song-audio into lp:openlp
Raoul Snyman has proposed merging lp:~raoul-snyman/openlp/song-audio into lp:openlp.
Requested reviews:
Andreas Preikschat (googol)
Tim Bentley (trb143)
Related bugs:
Bug #739770 in OpenLP: "Link audio to songs"
https://bugs.launchpad.net/openlp/+bug/739770
For more details, see:
https://code.launchpad.net/~raoul-snyman/openlp/song-audio/+merge/73867
Songs can how have audio files attached, and these files are transferred to and from services, stored in service files, and played when you display the song. There's a new pause button to pause playback.
Notable exceptions:
- openlp.org 1.x import probably doesn't support this newish setup yet.
- OpenLP 2.0 import probably doesn't either.
- I couldn't figure out how to get the volume control to display, nevermind work, so I left it out.
--
https://code.launchpad.net/~raoul-snyman/openlp/song-audio/+merge/73867
Your team OpenLP Core is subscribed to branch lp:openlp.
=== modified file 'openlp/__init__.py'
--- openlp/__init__.py 2011-06-12 16:02:52 +0000
+++ openlp/__init__.py 2011-09-02 18:47:24 +0000
@@ -27,3 +27,9 @@
"""
The :mod:`openlp` module contains all the project produced OpenLP functionality
"""
+
+import core
+import plugins
+
+__all__ = [u'core', u'plugins']
+
=== modified file 'openlp/core/__init__.py'
--- openlp/core/__init__.py 2011-08-26 11:04:56 +0000
+++ openlp/core/__init__.py 2011-09-02 18:47:24 +0000
@@ -25,7 +25,12 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
-__all__ = ('OpenLP', 'main')
+"""
+The :mod:`core` module provides all core application functions
+
+All the core functions of the OpenLP application including the GUI, settings,
+logging and a plugin framework are contained within the openlp.core module.
+"""
import os
import sys
@@ -46,16 +51,11 @@
from openlp.core.utils import AppLocation, LanguageManager, VersionThread, \
get_application_version, DelayStartThread
+
+__all__ = [u'OpenLP', u'main']
+
+
log = logging.getLogger()
-
-
-"""
-The :mod:`core` module provides all core application functions
-
-All the core functions of the OpenLP application including the GUI, settings,
-logging and a plugin framework are contained within the openlp.core module.
-"""
-
application_stylesheet = u"""
QMainWindow::separator
{
=== modified file 'openlp/core/lib/__init__.py'
--- openlp/core/lib/__init__.py 2011-08-20 11:45:06 +0000
+++ openlp/core/lib/__init__.py 2011-09-02 18:47:24 +0000
@@ -36,6 +36,13 @@
log = logging.getLogger(__name__)
+class MediaType(object):
+ """
+ An enumeration class for types of media.
+ """
+ Audio = 1
+ Video = 2
+
def translate(context, text, comment=None,
encoding=QtCore.QCoreApplication.CodecForTr, n=-1,
translate=QtCore.QCoreApplication.translate):
@@ -241,9 +248,7 @@
from plugin import PluginStatus, StringContent, Plugin
from pluginmanager import PluginManager
from settingstab import SettingsTab
-from serviceitem import ServiceItem
-from serviceitem import ServiceItemType
-from serviceitem import ItemCapabilities
+from serviceitem import ServiceItem, ServiceItemType, ItemCapabilities
from htmlbuilder import build_html, build_lyrics_format_css, \
build_lyrics_outline_css
from toolbar import OpenLPToolbar
=== modified file 'openlp/core/lib/db.py'
--- openlp/core/lib/db.py 2011-08-27 18:43:05 +0000
+++ openlp/core/lib/db.py 2011-09-02 18:47:24 +0000
@@ -82,7 +82,7 @@
load_changes = True
try:
tables = upgrade.upgrade_setup(metadata)
- except SQLAlchemyError, DBAPIError:
+ except (SQLAlchemyError, DBAPIError):
load_changes = False
metadata_table = Table(u'metadata', metadata,
Column(u'key', types.Unicode(64), primary_key=True),
@@ -106,7 +106,7 @@
getattr(upgrade, u'upgrade_%d' % version) \
(session, metadata, tables)
version_meta.value = unicode(version)
- except SQLAlchemyError, DBAPIError:
+ except (SQLAlchemyError, DBAPIError):
log.exception(u'Could not run database upgrade script '
'"upgrade_%s", upgrade process has been halted.', version)
break
@@ -213,7 +213,8 @@
return
try:
self.session = init_schema(self.db_url)
- except:
+ except (SQLAlchemyError, DBAPIError):
+ log.exception(u'Error loading database: %s', self.db_url)
critical_error_message_box(
translate('OpenLP.Manager', 'Database Error'),
unicode(translate('OpenLP.Manager', 'OpenLP cannot load your '
=== modified file 'openlp/core/lib/mediamanageritem.py'
--- openlp/core/lib/mediamanageritem.py 2011-08-15 22:44:02 +0000
+++ openlp/core/lib/mediamanageritem.py 2011-09-02 18:47:24 +0000
@@ -111,7 +111,7 @@
self.requiredIcons()
self.setupUi()
self.retranslateUi()
- self.auto_select_id = -1
+ self.autoSelectId = -1
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'%s_service_load' % self.plugin.name),
self.serviceLoad)
@@ -506,7 +506,7 @@
if QtCore.QSettings().value(u'advanced/single click preview',
QtCore.QVariant(False)).toBool() and self.quickPreviewAllowed \
and self.listView.selectedIndexes() \
- and self.auto_select_id == -1:
+ and self.autoSelectId == -1:
self.onPreviewClick(True)
def onPreviewClick(self, keepFocus=False):
@@ -626,7 +626,7 @@
"""
pass
- def check_search_result(self):
+ def checkSearchResult(self):
"""
Checks if the listView is empty and adds a "No Search Results" item.
"""
@@ -662,15 +662,15 @@
item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
return item_id
- def save_auto_select_id(self):
+ def saveAutoSelectId(self):
"""
Sorts out, what item to select after loading a list.
"""
# The item to select has not been set.
- if self.auto_select_id == -1:
+ if self.autoSelectId == -1:
item = self.listView.currentItem()
if item:
- self.auto_select_id = item.data(QtCore.Qt.UserRole).toInt()[0]
+ self.autoSelectId = item.data(QtCore.Qt.UserRole).toInt()[0]
def search(self, string):
"""
=== modified file 'openlp/core/lib/plugin.py'
--- openlp/core/lib/plugin.py 2011-07-23 21:29:24 +0000
+++ openlp/core/lib/plugin.py 2011-09-02 18:47:24 +0000
@@ -368,3 +368,4 @@
after this has been set.
"""
self.textStrings[name] = {u'title': title, u'tooltip': tooltip}
+
=== modified file 'openlp/core/lib/renderer.py'
--- openlp/core/lib/renderer.py 2011-08-30 15:15:39 +0000
+++ openlp/core/lib/renderer.py 2011-09-02 18:47:24 +0000
@@ -222,14 +222,14 @@
if item.is_capable(ItemCapabilities.NoLineBreaks):
line_end = u' '
# Bibles
- if item.is_capable(ItemCapabilities.AllowsWordSplit):
+ if item.is_capable(ItemCapabilities.CanWordSplit):
pages = self._paginate_slide_words(text.split(u'\n'), line_end)
else:
# Clean up line endings.
lines = self._lines_split(text)
pages = self._paginate_slide(lines, line_end)
# Songs and Custom
- if item.is_capable(ItemCapabilities.AllowsVirtualSplit) and \
+ if item.is_capable(ItemCapabilities.CanSoftBreak) and \
len(pages) > 1 and u'[---]' in text:
pages = []
while True:
=== modified file 'openlp/core/lib/serviceitem.py'
--- openlp/core/lib/serviceitem.py 2011-08-20 11:45:06 +0000
+++ openlp/core/lib/serviceitem.py 2011-09-02 18:47:24 +0000
@@ -52,20 +52,21 @@
"""
Provides an enumeration of a serviceitem's capabilities
"""
- AllowsPreview = 1
- AllowsEdit = 2
- AllowsMaintain = 3
+ CanPreview = 1
+ CanEdit = 2
+ CanMaintain = 3
RequiresMedia = 4
- AllowsLoop = 5
- AllowsAdditions = 6
+ CanLoop = 5
+ CanAppend = 6
NoLineBreaks = 7
OnLoadUpdate = 8
AddIfNewItem = 9
ProvidesOwnDisplay = 10
- AllowsDetailedTitleDisplay = 11
- AllowsVariableStartTime = 12
- AllowsVirtualSplit = 13
- AllowsWordSplit = 14
+ HasDetailedTitleDisplay = 11
+ HasVariableStartTime = 12
+ CanSoftBreak = 13
+ CanWordSplit = 14
+ HasBackgroundAudio = 15
class ServiceItem(object):
@@ -116,6 +117,7 @@
self.media_length = 0
self.from_service = False
self.image_border = u'#000000'
+ self.background_audio = []
self._new_item()
def _new_item(self):
@@ -159,7 +161,7 @@
"""
The render method is what generates the frames for the screen and
obtains the display information from the renderemanager.
- At this point all the slides are build for the given
+ At this point all the slides are built for the given
display size.
"""
log.debug(u'Render called')
@@ -272,7 +274,8 @@
u'xml_version': self.xml_version,
u'start_time': self.start_time,
u'end_time': self.end_time,
- u'media_length': self.media_length
+ u'media_length': self.media_length,
+ u'background_audio': self.background_audio
}
service_data = []
if self.service_item_type == ServiceItemType.Text:
@@ -320,6 +323,8 @@
self.end_time = header[u'end_time']
if u'media_length' in header:
self.media_length = header[u'media_length']
+ if u'background_audio' in header:
+ self.background_audio = header[u'background_audio']
if self.service_item_type == ServiceItemType.Text:
for slide in serviceitem[u'serviceitem'][u'data']:
self._raw_frames.append(slide)
@@ -341,7 +346,7 @@
if self.is_text():
return self.title
else:
- if ItemCapabilities.AllowsDetailedTitleDisplay in self.capabilities:
+ if ItemCapabilities.HasDetailedTitleDisplay in self.capabilities:
return self._raw_frames[0][u'title']
elif len(self._raw_frames) > 1:
return self.title
=== modified file 'openlp/core/ui/maindisplay.py'
--- openlp/core/ui/maindisplay.py 2011-08-22 17:32:18 +0000
+++ openlp/core/ui/maindisplay.py 2011-09-02 18:47:24 +0000
@@ -62,6 +62,10 @@
self.override = {}
self.retranslateUi()
self.mediaObject = None
+ if live:
+ self.audioPlayer = AudioPlayer(self)
+ else:
+ self.audioPlayer = None
self.firstTime = True
self.setStyleSheet(u'border: 0px; margin: 0px; padding: 0px;')
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.Tool |
@@ -587,61 +591,76 @@
"""
log.debug(u'AudioPlayer Initialisation started')
QtCore.QObject.__init__(self, parent)
- self.message = None
+ self.currentIndex = -1
+ self.playlist = []
self.mediaObject = Phonon.MediaObject()
self.audioObject = Phonon.AudioOutput(Phonon.VideoCategory)
Phonon.createPath(self.mediaObject, self.audioObject)
-
- def setup(self):
- """
- Sets up the Audio Player for use
- """
- log.debug(u'AudioPlayer Setup')
-
- def close(self):
+ QtCore.QObject.connect(self.mediaObject,
+ QtCore.SIGNAL(u'aboutToFinish()'), self.onAboutToFinish)
+
+ def __del__(self):
"""
Shutting down so clean up connections
"""
- self.onMediaStop()
+ self.stop()
for path in self.mediaObject.outputPaths():
path.disconnect()
-
- def onMediaQueue(self, message):
- """
- Set up a video to play from the serviceitem.
- """
- log.debug(u'AudioPlayer Queue new media message %s' % message)
- mfile = os.path.join(message[0].get_frame_path(),
- message[0].get_frame_title())
- self.mediaObject.setCurrentSource(Phonon.MediaSource(mfile))
- self.onMediaPlay()
-
- def onMediaPlay(self):
- """
- We want to play the play so start it
- """
- log.debug(u'AudioPlayer _play called')
+ QtCore.QObject.__del__(self)
+
+ def onAboutToFinish(self):
+ """
+ Just before the audio player finishes the current track, queue the next
+ item in the playlist, if there is one.
+ """
+ self.currentIndex += 1
+ if len(self.playlist) > self.currentIndex:
+ self.mediaObject.enqueue(self.playlist[self.currentIndex])
+
+ def connectVolumeSlider(self, slider):
+ slider.setAudioOutput(self.audioObject)
+
+ def reset(self):
+ """
+ Reset the audio player, clearing the playlist and the queue.
+ """
+ self.currentIndex = -1
+ self.playlist = []
+ self.stop()
+ self.mediaObject.clear()
+
+ def play(self):
+ """
+ We want to play the file so start it
+ """
+ log.debug(u'AudioPlayer.play() called')
+ if self.currentIndex == -1:
+ self.onAboutToFinish()
self.mediaObject.play()
- def onMediaPause(self):
+ def pause(self):
"""
Pause the Audio
"""
- log.debug(u'AudioPlayer Media paused by user')
+ log.debug(u'AudioPlayer.pause() called')
self.mediaObject.pause()
- def onMediaStop(self):
+ def stop(self):
"""
Stop the Audio and clean up
"""
- log.debug(u'AudioPlayer Media stopped by user')
- self.message = None
+ log.debug(u'AudioPlayer.stop() called')
self.mediaObject.stop()
- self.onMediaFinish()
-
- def onMediaFinish(self):
- """
- Clean up the Object queue
- """
- log.debug(u'AudioPlayer Reached end of media playlist')
- self.mediaObject.clearQueue()
+
+ def addToPlaylist(self, filenames):
+ """
+ Add another file to the playlist.
+
+ ``filename``
+ The file to add to the playlist.
+ """
+ if not isinstance(filenames, list):
+ filenames = [filenames]
+ for filename in filenames:
+ self.playlist.append(Phonon.MediaSource(filename))
+
=== modified file 'openlp/core/ui/servicemanager.py'
--- openlp/core/ui/servicemanager.py 2011-08-29 13:08:50 +0000
+++ openlp/core/ui/servicemanager.py 2011-09-02 18:47:24 +0000
@@ -28,6 +28,7 @@
import cPickle
import logging
import os
+import shutil
import zipfile
log = logging.getLogger(__name__)
@@ -471,23 +472,34 @@
if not self.fileName():
return self.saveFileAs()
path_file_name = unicode(self.fileName())
- (path, file_name) = os.path.split(path_file_name)
- (basename, extension) = os.path.splitext(file_name)
- service_file_name = basename + '.osd'
+ path, file_name = os.path.split(path_file_name)
+ basename, extension = os.path.splitext(file_name)
+ service_file_name = '%s.osd' % basename
log.debug(u'ServiceManager.saveFile - %s' % path_file_name)
SettingsManager.set_last_dir(
self.mainwindow.servicemanagerSettingsSection,
path)
service = []
write_list = []
+ audio_files = []
total_size = 0
Receiver.send_message(u'cursor_busy')
# Number of items + 1 to zip it
self.mainwindow.displayProgressBar(len(self.serviceItems) + 1)
for item in self.serviceItems:
self.mainwindow.incrementProgressBar()
- service.append({u'serviceitem':
- item[u'service_item'].get_service_repr()})
+ service_item = item[u'service_item'].get_service_repr()
+ # Get all the audio files, and ready them for embedding in the
+ # service file.
+ if len(service_item[u'header'][u'background_audio']) > 0:
+ for i, filename in \
+ enumerate(service_item[u'header'][u'background_audio']):
+ new_file = os.path.join(u'audio', item[u'service_item']._uuid,
+ os.path.split(filename)[1])
+ audio_files.append((filename, new_file))
+ service_item[u'header'][u'background_audio'][i] = new_file
+ # Add the service item to the service.
+ service.append({u'serviceitem': service_item})
if not item[u'service_item'].uses_file():
continue
skipMissing = False
@@ -541,6 +553,8 @@
# Finally add all the listed media files.
for path_from in write_list:
zip.write(path_from, path_from.encode(u'utf-8'))
+ for path_from, path_to in audio_files:
+ zip.write(path_from, path_to.encode(u'utf-8'))
except IOError:
log.exception(u'Failed to save service to disk')
success = False
@@ -595,11 +609,12 @@
'The content encoding is not UTF-8.'))
continue
osfile = unicode(QtCore.QDir.toNativeSeparators(ucsfile))
- filename_only = os.path.split(osfile)[1]
- zipinfo.filename = filename_only
+ if not osfile.startswith(u'audio'):
+ osfile = os.path.split(osfile)[1]
+ zipinfo.filename = osfile
zip.extract(zipinfo, self.servicePath)
- if filename_only.endswith(u'osd'):
- p_file = os.path.join(self.servicePath, filename_only)
+ if osfile.endswith(u'osd'):
+ p_file = os.path.join(self.servicePath, osfile)
if 'p_file' in locals():
Receiver.send_message(u'cursor_busy')
fileTo = open(p_file, u'r')
@@ -630,10 +645,10 @@
'File is not a valid service.'))
log.exception(u'File contains no service data')
except (IOError, NameError, zipfile.BadZipfile):
+ log.exception(u'Problem loading service file %s' % fileName)
critical_error_message_box(
message=translate('OpenLP.ServiceManager',
'File could not be opened because it is corrupt.'))
- log.exception(u'Problem loading service file %s' % fileName)
except zipfile.BadZipfile:
if os.path.getsize(fileName) == 0:
log.exception(u'Service file is zero sized: %s' % fileName)
@@ -682,16 +697,16 @@
self.maintainAction.setVisible(False)
self.notesAction.setVisible(False)
self.timeAction.setVisible(False)
- if serviceItem[u'service_item'].is_capable(ItemCapabilities.AllowsEdit)\
+ if serviceItem[u'service_item'].is_capable(ItemCapabilities.CanEdit)\
and serviceItem[u'service_item'].edit_id:
self.editAction.setVisible(True)
if serviceItem[u'service_item']\
- .is_capable(ItemCapabilities.AllowsMaintain):
+ .is_capable(ItemCapabilities.CanMaintain):
self.maintainAction.setVisible(True)
if item.parent() is None:
self.notesAction.setVisible(True)
if serviceItem[u'service_item']\
- .is_capable(ItemCapabilities.AllowsVariableStartTime):
+ .is_capable(ItemCapabilities.HasVariableStartTime):
self.timeAction.setVisible(True)
self.themeMenu.menuAction().setVisible(False)
# Set up the theme menu.
@@ -962,7 +977,7 @@
(unicode(translate('OpenLP.ServiceManager', 'Notes')),
cgi.escape(unicode(serviceitem.notes))))
if item[u'service_item'] \
- .is_capable(ItemCapabilities.AllowsVariableStartTime):
+ .is_capable(ItemCapabilities.HasVariableStartTime):
tips.append(item[u'service_item'].get_media_time())
treewidgetitem.setToolTip(0, u'<br>'.join(tips))
treewidgetitem.setData(0, QtCore.Qt.UserRole,
@@ -998,6 +1013,8 @@
for file in os.listdir(self.servicePath):
file_path = os.path.join(self.servicePath, file)
delete_file(file_path)
+ if os.path.exists(os.path.join(self.servicePath, u'audio')):
+ shutil.rmtree(os.path.join(self.servicePath, u'audio'), False)
def onThemeComboBoxSelected(self, currentIndex):
"""
@@ -1196,7 +1213,7 @@
item += 1
if self.serviceItems and item < len(self.serviceItems) and \
self.serviceItems[item][u'service_item'].is_capable(
- ItemCapabilities.AllowsPreview):
+ ItemCapabilities.CanPreview):
self.mainwindow.previewController.addServiceManagerItem(
self.serviceItems[item][u'service_item'], 0)
self.mainwindow.liveController.previewListWidget.setFocus()
@@ -1214,7 +1231,7 @@
"""
item = self.findServiceItem()[0]
if self.serviceItems[item][u'service_item']\
- .is_capable(ItemCapabilities.AllowsEdit):
+ .is_capable(ItemCapabilities.CanEdit):
Receiver.send_message(u'%s_edit' %
self.serviceItems[item][u'service_item'].name.lower(),
u'L:%s' % self.serviceItems[item][u'service_item'].edit_id)
@@ -1297,7 +1314,7 @@
serviceItem = self.serviceItems[pos]
if (plugin == serviceItem[u'service_item'].name and
serviceItem[u'service_item'].is_capable(
- ItemCapabilities.AllowsAdditions)):
+ ItemCapabilities.CanAppend)):
action = self.dndMenu.exec_(QtGui.QCursor.pos())
# New action required
if action == self.newAction:
=== modified file 'openlp/core/ui/slidecontroller.py'
--- openlp/core/ui/slidecontroller.py 2011-07-24 19:41:33 +0000
+++ openlp/core/ui/slidecontroller.py 2011-09-02 18:47:24 +0000
@@ -256,6 +256,12 @@
self.songMenu.setMenu(QtGui.QMenu(
translate('OpenLP.SlideController', 'Go To'), self.toolbar))
self.toolbar.makeWidgetsInvisible([u'Song Menu'])
+ # Stuff for items with background audio.
+ self.audioPauseItem = self.toolbar.addToolbarButton(
+ u'Pause Audio', u':/slides/media_playback_pause.png',
+ 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)
@@ -512,13 +518,13 @@
self.playSlidesOnce.setChecked(False)
self.playSlidesOnce.setIcon(build_icon(u':/media/media_time.png'))
self.playSlidesLoop.setChecked(False)
- self.playSlidesLoop.setIcon(build_icon(u':/media/media_time.png'))
+ self.playSlidesLoop.setIcon(build_icon(u':/media/media_time.png'))
if item.is_text():
if QtCore.QSettings().value(
self.parent().songsSettingsSection + u'/display songbar',
QtCore.QVariant(True)).toBool() and len(self.slideList) > 0:
self.toolbar.makeWidgetsVisible([u'Song Menu'])
- if item.is_capable(ItemCapabilities.AllowsLoop) and \
+ if item.is_capable(ItemCapabilities.CanLoop) and \
len(item.get_frames()) > 1:
self.toolbar.makeWidgetsVisible(self.loopList)
if item.is_media():
@@ -538,7 +544,7 @@
self.toolbar.hide()
self.mediabar.setVisible(False)
self.toolbar.makeWidgetsInvisible(self.songEditList)
- if item.is_capable(ItemCapabilities.AllowsEdit) and item.from_plugin:
+ if item.is_capable(ItemCapabilities.CanEdit) and item.from_plugin:
self.toolbar.makeWidgetsVisible(self.songEditList)
elif item.is_media():
self.toolbar.setVisible(False)
@@ -576,7 +582,7 @@
"""
Replacement item following a remote edit
"""
- if item.__eq__(self.serviceItem):
+ if item == self.serviceItem:
self._processItem(item, self.previewListWidget.currentRow())
def addServiceManagerItem(self, item, slideno):
@@ -586,15 +592,17 @@
Called by ServiceManager
"""
log.debug(u'addServiceManagerItem live = %s' % self.isLive)
- # If no valid slide number is specified we take the first one.
+ # If no valid slide number is specified we take the first one, but we
+ # remember the initial value to see if we should reload the song or not
+ slidenum = slideno
if slideno == -1:
- slideno = 0
- # If service item is the same as the current on only change slide
- if item.__eq__(self.serviceItem):
- self.__checkUpdateSelectedSlide(slideno)
+ slidenum = 0
+ # If service item is the same as the current one, only change slide
+ if slideno >= 0 and item == self.serviceItem:
+ self.__checkUpdateSelectedSlide(slidenum)
self.slideSelected()
- return
- self._processItem(item, slideno)
+ else:
+ self._processItem(item, slidenum)
def _processItem(self, serviceItem, slideno):
"""
@@ -618,6 +626,15 @@
self.previewListWidget.setColumnWidth(0, width)
if self.isLive:
self.songMenu.menu().clear()
+ self.display.audioPlayer.reset()
+ self.setAudioItemsVisibility(False)
+ self.audioPauseItem.setChecked(False)
+ if self.serviceItem.is_capable(ItemCapabilities.HasBackgroundAudio):
+ log.debug(u'Starting to play...')
+ self.display.audioPlayer.addToPlaylist(
+ self.serviceItem.background_audio)
+ self.display.audioPlayer.play()
+ self.setAudioItemsVisibility(True)
row = 0
text = []
for framenumber, frame in enumerate(self.serviceItem.get_frames()):
@@ -1097,6 +1114,17 @@
self.playSlidesLoop.setChecked(False)
self.onToggleLoop()
+ def setAudioItemsVisibility(self, visible):
+ self.audioPauseItem.setVisible(visible)
+
+ def onAudioPauseClicked(self, checked):
+ if not self.audioPauseItem.isVisible():
+ return
+ if checked:
+ self.display.audioPlayer.pause()
+ else:
+ self.display.audioPlayer.play()
+
def timerEvent(self, event):
"""
If the timer event is for this window select next slide
=== modified file 'openlp/plugins/bibles/lib/mediaitem.py'
--- openlp/plugins/bibles/lib/mediaitem.py 2011-08-29 12:47:32 +0000
+++ openlp/plugins/bibles/lib/mediaitem.py 2011-09-02 18:47:24 +0000
@@ -67,7 +67,7 @@
self.hasSearch = True
self.search_results = {}
self.second_search_results = {}
- self.check_search_result()
+ self.checkSearchReslt()
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'bibles_load_list'), self.reloadBibles)
@@ -651,7 +651,7 @@
elif self.search_results:
self.displayResults(bible, second_bible)
self.advancedSearchButton.setEnabled(True)
- self.check_search_result()
+ self.checkSearchReslt()
Receiver.send_message(u'cursor_normal')
Receiver.send_message(u'openlp_process_events')
@@ -715,7 +715,7 @@
elif self.search_results:
self.displayResults(bible, second_bible)
self.quickSearchButton.setEnabled(True)
- self.check_search_result()
+ self.checkSearchReslt()
Receiver.send_message(u'cursor_normal')
Receiver.send_message(u'openlp_process_events')
@@ -863,9 +863,9 @@
not second_bible:
# Split the line but do not replace line breaks in renderer.
service_item.add_capability(ItemCapabilities.NoLineBreaks)
- service_item.add_capability(ItemCapabilities.AllowsPreview)
- service_item.add_capability(ItemCapabilities.AllowsLoop)
- service_item.add_capability(ItemCapabilities.AllowsWordSplit)
+ service_item.add_capability(ItemCapabilities.CanPreview)
+ service_item.add_capability(ItemCapabilities.CanLoop)
+ service_item.add_capability(ItemCapabilities.CanWordSplit)
# Service Item: Title
service_item.title = u', '.join(raw_title)
# Service Item: Theme
=== modified file 'openlp/plugins/custom/forms/editcustomform.py'
--- openlp/plugins/custom/forms/editcustomform.py 2011-06-29 08:04:55 +0000
+++ openlp/plugins/custom/forms/editcustomform.py 2011-09-02 18:47:24 +0000
@@ -135,7 +135,7 @@
self.customSlide.credits = unicode(self.creditEdit.text())
self.customSlide.theme_name = unicode(self.themeComboBox.currentText())
success = self.manager.save_object(self.customSlide)
- self.mediaitem.auto_select_id = self.customSlide.id
+ self.mediaitem.autoSelectId = self.customSlide.id
return success
def onUpButtonClicked(self):
=== modified file 'openlp/plugins/custom/lib/mediaitem.py'
--- openlp/plugins/custom/lib/mediaitem.py 2011-08-25 19:52:07 +0000
+++ openlp/plugins/custom/lib/mediaitem.py 2011-09-02 18:47:24 +0000
@@ -132,7 +132,7 @@
def loadList(self, custom_slides):
# Sort out what custom we want to select after loading the list.
- self.save_auto_select_id()
+ self.saveAutoSelectId()
self.listView.clear()
# Sort the customs by its title considering language specific
# characters. lower() is needed for windows!
@@ -144,9 +144,9 @@
QtCore.Qt.UserRole, QtCore.QVariant(custom_slide.id))
self.listView.addItem(custom_name)
# Auto-select the custom.
- if custom_slide.id == self.auto_select_id:
+ if custom_slide.id == self.autoSelectId:
self.listView.setCurrentItem(custom_name)
- self.auto_select_id = -1
+ self.autoSelectId = -1
# Called to redisplay the custom list screen edith from a search
# or from the exit of the Custom edit dialog. If remote editing is
# active trigger it and clean up so it will not update again.
@@ -180,7 +180,7 @@
self.remoteTriggered = remote_type
self.edit_custom_form.loadCustom(custom_id, (remote_type == u'P'))
self.edit_custom_form.exec_()
- self.auto_select_id = -1
+ self.autoSelectId = -1
self.onSearchTextButtonClick()
def onEditClick(self):
@@ -192,7 +192,7 @@
item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
self.edit_custom_form.loadCustom(item_id, False)
self.edit_custom_form.exec_()
- self.auto_select_id = -1
+ self.autoSelectId = -1
self.onSearchTextButtonClick()
def onDeleteClick(self):
@@ -227,10 +227,10 @@
slide = None
theme = None
item_id = self._getIdOfItemToGenerate(item, self.remoteCustom)
- service_item.add_capability(ItemCapabilities.AllowsEdit)
- service_item.add_capability(ItemCapabilities.AllowsPreview)
- service_item.add_capability(ItemCapabilities.AllowsLoop)
- service_item.add_capability(ItemCapabilities.AllowsVirtualSplit)
+ service_item.add_capability(ItemCapabilities.CanEdit)
+ service_item.add_capability(ItemCapabilities.CanPreview)
+ service_item.add_capability(ItemCapabilities.CanLoop)
+ service_item.add_capability(ItemCapabilities.CanSoftBreak)
customSlide = self.plugin.manager.get_object(CustomSlide, item_id)
title = customSlide.title
credit = customSlide.credits
@@ -273,7 +273,7 @@
CustomSlide.theme_name.like(u'%' + self.whitespace.sub(u' ',
search_keywords) + u'%'), order_by_ref=CustomSlide.title)
self.loadList(search_results)
- self.check_search_result()
+ self.checkSearchReslt()
def onSearchTextEditChanged(self, text):
"""
=== modified file 'openlp/plugins/images/lib/mediaitem.py'
--- openlp/plugins/images/lib/mediaitem.py 2011-08-22 17:32:18 +0000
+++ openlp/plugins/images/lib/mediaitem.py 2011-09-02 18:47:24 +0000
@@ -149,10 +149,10 @@
if not items:
return False
service_item.title = unicode(self.plugin.nameStrings[u'plural'])
- service_item.add_capability(ItemCapabilities.AllowsMaintain)
- service_item.add_capability(ItemCapabilities.AllowsPreview)
- service_item.add_capability(ItemCapabilities.AllowsLoop)
- service_item.add_capability(ItemCapabilities.AllowsAdditions)
+ service_item.add_capability(ItemCapabilities.CanMaintain)
+ service_item.add_capability(ItemCapabilities.CanPreview)
+ service_item.add_capability(ItemCapabilities.CanLoop)
+ service_item.add_capability(ItemCapabilities.CanAppend)
# force a nonexistent theme
service_item.theme = -1
missing_items = []
=== modified file 'openlp/plugins/media/lib/mediaitem.py'
--- openlp/plugins/media/lib/mediaitem.py 2011-08-02 05:07:09 +0000
+++ openlp/plugins/media/lib/mediaitem.py 2011-09-02 18:47:24 +0000
@@ -31,11 +31,11 @@
import locale
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
+ SettingsManager, translate, check_item_selected, Receiver, MediaType
from openlp.core.lib.ui import UiStrings, critical_error_message_box
-from PyQt4.phonon import Phonon
log = logging.getLogger(__name__)
@@ -48,9 +48,9 @@
log.info(u'%s MediaMediaItem loaded', __name__)
def __init__(self, parent, plugin, icon):
- self.IconPath = u'images/image'
+ self.iconPath = u'images/image'
self.background = False
- self.PreviewFunction = CLAPPERBOARD
+ self.previewFunction = CLAPPERBOARD
MediaManagerItem.__init__(self, parent, plugin, icon)
self.singleServiceItem = False
self.hasSearch = True
@@ -139,8 +139,8 @@
# File is no longer present
critical_error_message_box(
translate('MediaPlugin.MediaItem', 'Missing Media File'),
- unicode(translate('MediaPlugin.MediaItem',
- 'The file %s no longer exists.')) % filename)
+ unicode(translate('MediaPlugin.MediaItem',
+ 'The file %s no longer exists.')) % filename)
return False
self.mediaObject.stop()
self.mediaObject.clearQueue()
@@ -156,13 +156,16 @@
or self.mediaObject.currentSource().type() \
== Phonon.MediaSource.Invalid:
self.mediaObject.stop()
- critical_error_message_box(UiStrings().UnsupportedFile,
- UiStrings().UnsupportedFile)
+ 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.AllowsVariableStartTime)
+ ItemCapabilities.HasVariableStartTime)
service_item.title = unicode(self.plugin.nameStrings[u'singular'])
service_item.add_capability(ItemCapabilities.RequiresMedia)
# force a non-existent theme
@@ -217,6 +220,19 @@
item_name.setToolTip(track)
self.listView.addItem(item_name)
+ def getList(self, type=MediaType.Audio):
+ media = SettingsManager.load_list(self.settingsSection, u'media')
+ media.sort(cmp=locale.strcoll,
+ key=lambda filename: os.path.split(unicode(filename))[1].lower())
+ ext = []
+ if type == MediaType.Audio:
+ ext = self.plugin.audio_extensions_list
+ else:
+ ext = self.plugin.video_extensions_list
+ ext = map(lambda x: x[1:], ext)
+ 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:
=== modified file 'openlp/plugins/presentations/lib/mediaitem.py'
--- openlp/plugins/presentations/lib/mediaitem.py 2011-08-02 05:07:09 +0000
+++ openlp/plugins/presentations/lib/mediaitem.py 2011-09-02 18:47:24 +0000
@@ -248,7 +248,7 @@
service_item.title = unicode(self.displayTypeComboBox.currentText())
service_item.shortname = unicode(self.displayTypeComboBox.currentText())
service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay)
- service_item.add_capability(ItemCapabilities.AllowsDetailedTitleDisplay)
+ service_item.add_capability(ItemCapabilities.HasDetailedTitleDisplay)
shortname = service_item.shortname
if shortname:
for bitem in items:
=== modified file 'openlp/plugins/songs/forms/__init__.py'
--- openlp/plugins/songs/forms/__init__.py 2011-06-12 16:02:52 +0000
+++ openlp/plugins/songs/forms/__init__.py 2011-09-02 18:47:24 +0000
@@ -52,6 +52,7 @@
from the .ui files later if necessary.
"""
+from mediafilesform import MediaFilesForm
from authorsform import AuthorsForm
from topicsform import TopicsForm
from songbookform import SongBookForm
=== modified file 'openlp/plugins/songs/forms/editsongdialog.py'
--- openlp/plugins/songs/forms/editsongdialog.py 2011-06-14 19:55:17 +0000
+++ openlp/plugins/songs/forms/editsongdialog.py 2011-09-02 18:47:24 +0000
@@ -28,7 +28,8 @@
from PyQt4 import QtCore, QtGui
from openlp.core.lib import build_icon, translate
-from openlp.core.lib.ui import UiStrings, create_accept_reject_button_box
+from openlp.core.lib.ui import UiStrings, create_accept_reject_button_box, \
+ create_up_down_push_button_set
from openlp.plugins.songs.lib.ui import SongStrings
class Ui_EditSongDialog(object):
@@ -36,9 +37,11 @@
editSongDialog.setObjectName(u'editSongDialog')
editSongDialog.resize(650, 400)
editSongDialog.setWindowIcon(
- build_icon(u':/icon/openlp.org-icon-32.bmp'))
+ build_icon(u':/icon/openlp-logo-16x16.png'))
editSongDialog.setModal(True)
self.dialogLayout = QtGui.QVBoxLayout(editSongDialog)
+ self.dialogLayout.setSpacing(8)
+ self.dialogLayout.setContentsMargins(8, 8, 8, 8)
self.dialogLayout.setObjectName(u'dialogLayout')
self.songTabWidget = QtGui.QTabWidget(editSongDialog)
self.songTabWidget.setObjectName(u'songTabWidget')
@@ -246,6 +249,36 @@
self.commentsLayout.addWidget(self.commentsEdit)
self.themeTabLayout.addWidget(self.commentsGroupBox)
self.songTabWidget.addTab(self.themeTab, u'')
+ # audio tab
+ self.audioTab = QtGui.QWidget()
+ self.audioTab.setObjectName(u'audioTab')
+ self.audioLayout = QtGui.QHBoxLayout(self.audioTab)
+ self.audioLayout.setObjectName(u'audioLayout')
+ self.audioListWidget = QtGui.QListWidget(self.audioTab)
+ self.audioListWidget.setObjectName(u'audioListWidget')
+ self.audioLayout.addWidget(self.audioListWidget)
+ self.audioButtonsLayout = QtGui.QVBoxLayout()
+ self.audioButtonsLayout.setObjectName(u'audioButtonsLayout')
+ self.audioAddFromFileButton = QtGui.QPushButton(self.audioTab)
+ self.audioAddFromFileButton.setObjectName(u'audioAddFromFileButton')
+ self.audioButtonsLayout.addWidget(self.audioAddFromFileButton)
+ self.audioAddFromMediaButton = QtGui.QPushButton(self.audioTab)
+ self.audioAddFromMediaButton.setObjectName(u'audioAddFromMediaButton')
+ self.audioButtonsLayout.addWidget(self.audioAddFromMediaButton)
+ self.audioRemoveButton = QtGui.QPushButton(self.audioTab)
+ self.audioRemoveButton.setObjectName(u'audioRemoveButton')
+ self.audioButtonsLayout.addWidget(self.audioRemoveButton)
+ self.audioRemoveAllButton = QtGui.QPushButton(self.audioTab)
+ self.audioRemoveAllButton.setObjectName(u'audioRemoveAllButton')
+ self.audioButtonsLayout.addWidget(self.audioRemoveAllButton)
+ self.audioButtonsLayout.addStretch(1)
+ self.upButton, self.downButton = \
+ create_up_down_push_button_set(self)
+ self.audioButtonsLayout.addWidget(self.upButton)
+ self.audioButtonsLayout.addWidget(self.downButton)
+ self.audioLayout.addLayout(self.audioButtonsLayout)
+ self.songTabWidget.addTab(self.audioTab, u'')
+ # Last few bits
self.dialogLayout.addWidget(self.songTabWidget)
self.buttonBox = create_accept_reject_button_box(editSongDialog)
self.dialogLayout.addWidget(self.buttonBox)
@@ -305,6 +338,17 @@
self.songTabWidget.indexOf(self.themeTab),
translate('SongsPlugin.EditSongForm',
'Theme, Copyright Info && Comments'))
+ self.songTabWidget.setTabText(
+ self.songTabWidget.indexOf(self.audioTab),
+ translate('SongsPlugin.EditSongForm', 'Linked Audio'))
+ self.audioAddFromFileButton.setText(
+ translate('SongsPlugin.EditSongForm', 'Add &File(s)'))
+ self.audioAddFromMediaButton.setText(
+ translate('SongsPlugin.EditSongForm', 'Add &Media'))
+ self.audioRemoveButton.setText(
+ translate('SongsPlugin.EditSongForm', '&Remove'))
+ self.audioRemoveAllButton.setText(
+ translate('SongsPlugin.EditSongForm', 'Remove &All'))
def editSongDialogComboBox(parent, name):
"""
=== modified file 'openlp/plugins/songs/forms/editsongform.py'
--- openlp/plugins/songs/forms/editsongform.py 2011-07-06 14:00:32 +0000
+++ openlp/plugins/songs/forms/editsongform.py 2011-09-02 18:47:24 +0000
@@ -27,15 +27,18 @@
import logging
import re
+import os
+import shutil
from PyQt4 import QtCore, QtGui
-from openlp.core.lib import Receiver, translate
+from openlp.core.lib import PluginStatus, Receiver, MediaType, translate
from openlp.core.lib.ui import UiStrings, add_widget_completer, \
critical_error_message_box, find_and_set_in_combo_box
-from openlp.plugins.songs.forms import EditVerseForm
+from openlp.core.utils import AppLocation
+from openlp.plugins.songs.forms import EditVerseForm, MediaFilesForm
from openlp.plugins.songs.lib import SongXML, VerseType, clean_song
-from openlp.plugins.songs.lib.db import Book, Song, Author, Topic
+from openlp.plugins.songs.lib.db import Book, Song, Author, Topic, MediaFile
from openlp.plugins.songs.lib.ui import SongStrings
from editsongdialog import Ui_EditSongDialog
@@ -93,6 +96,14 @@
self.mediaitem.plugin.renderer.themeManager.onAddTheme)
QtCore.QObject.connect(self.maintenanceButton,
QtCore.SIGNAL(u'clicked()'), self.onMaintenanceButtonClicked)
+ QtCore.QObject.connect(self.audioAddFromFileButton,
+ QtCore.SIGNAL(u'clicked()'), self.onAudioAddFromFileButtonClicked)
+ QtCore.QObject.connect(self.audioAddFromMediaButton,
+ QtCore.SIGNAL(u'clicked()'), self.onAudioAddFromMediaButtonClicked)
+ QtCore.QObject.connect(self.audioRemoveButton,
+ QtCore.SIGNAL(u'clicked()'), self.onAudioRemoveButtonClicked)
+ QtCore.QObject.connect(self.audioRemoveAllButton,
+ QtCore.SIGNAL(u'clicked()'), self.onAudioRemoveAllButtonClicked)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'theme_update_list'), self.loadThemes)
self.previewButton = QtGui.QPushButton()
@@ -104,12 +115,14 @@
QtCore.SIGNAL(u'clicked(QAbstractButton*)'), self.onPreview)
# Create other objects and forms
self.manager = manager
- self.verse_form = EditVerseForm(self)
+ self.verseForm = EditVerseForm(self)
+ self.mediaForm = MediaFilesForm(self)
self.initialise()
self.authorsListView.setSortingEnabled(False)
self.authorsListView.setAlternatingRowColors(True)
self.topicsListView.setSortingEnabled(False)
self.topicsListView.setAlternatingRowColors(True)
+ self.audioListWidget.setAlternatingRowColors(True)
self.findVerseSplit = re.compile(u'---\[\]---\n', re.UNICODE)
self.whitespace = re.compile(r'\W+', re.UNICODE)
@@ -161,6 +174,15 @@
self.themes.append(theme)
add_widget_completer(self.themes, self.themeComboBox)
+ def loadMediaFiles(self):
+ self.audioAddFromMediaButton.setVisible(False)
+ for plugin in self.parent().pluginManager.plugins:
+ if plugin.name == u'media' and plugin.status == PluginStatus.Active:
+ self.audioAddFromMediaButton.setVisible(True)
+ self.mediaForm.populateFiles(
+ plugin.getMediaManagerItem().getList(MediaType.Audio))
+ break
+
def newSong(self):
log.debug(u'New Song')
self.song = None
@@ -176,11 +198,13 @@
self.verseListWidget.setRowCount(0)
self.authorsListView.clear()
self.topicsListView.clear()
+ self.audioListWidget.clear()
self.titleEdit.setFocus(QtCore.Qt.OtherFocusReason)
self.songBookNumberEdit.setText(u'')
self.loadAuthors()
self.loadTopics()
self.loadBooks()
+ self.loadMediaFiles()
self.themeComboBox.setCurrentIndex(0)
# it's a new song to preview is not possible
self.previewButton.setVisible(False)
@@ -201,6 +225,7 @@
self.loadAuthors()
self.loadTopics()
self.loadBooks()
+ self.loadMediaFiles()
self.song = self.manager.get_object(Song, id)
self.titleEdit.setText(self.song.title)
if self.song.alternate_title:
@@ -303,6 +328,11 @@
topic_name = QtGui.QListWidgetItem(unicode(topic.name))
topic_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(topic.id))
self.topicsListView.addItem(topic_name)
+ self.audioListWidget.clear()
+ for media in self.song.media_files:
+ media_file = QtGui.QListWidgetItem(os.path.split(media.file_name)[1])
+ media_file.setData(QtCore.Qt.UserRole, QtCore.QVariant(media.file_name))
+ self.audioListWidget.addItem(media_file)
self.titleEdit.setFocus(QtCore.Qt.OtherFocusReason)
# Hide or show the preview button.
self.previewButton.setVisible(preview)
@@ -436,9 +466,9 @@
self.verseDeleteButton.setEnabled(True)
def onVerseAddButtonClicked(self):
- self.verse_form.setVerse(u'', True)
- if self.verse_form.exec_():
- after_text, verse_tag, verse_num = self.verse_form.getVerse()
+ self.verseForm.setVerse(u'', True)
+ if self.verseForm.exec_():
+ after_text, verse_tag, verse_num = self.verseForm.getVerse()
verse_def = u'%s%s' % (verse_tag, verse_num)
item = QtGui.QTableWidgetItem(after_text)
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(verse_def))
@@ -454,9 +484,9 @@
if item:
tempText = item.text()
verseId = unicode(item.data(QtCore.Qt.UserRole).toString())
- self.verse_form.setVerse(tempText, True, verseId)
- if self.verse_form.exec_():
- after_text, verse_tag, verse_num = self.verse_form.getVerse()
+ self.verseForm.setVerse(tempText, True, verseId)
+ if self.verseForm.exec_():
+ after_text, verse_tag, verse_num = self.verseForm.getVerse()
verse_def = u'%s%s' % (verse_tag, verse_num)
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(verse_def))
item.setText(after_text)
@@ -486,12 +516,12 @@
verse_list += u'---[%s:%s]---\n' % (verse_tag, verse_num)
verse_list += item.text()
verse_list += u'\n'
- self.verse_form.setVerse(verse_list)
+ self.verseForm.setVerse(verse_list)
else:
- self.verse_form.setVerse(u'')
- if not self.verse_form.exec_():
+ self.verseForm.setVerse(u'')
+ if not self.verseForm.exec_():
return
- verse_list = self.verse_form.getVerseAll()
+ verse_list = self.verseForm.getVerseAll()
verse_list = unicode(verse_list.replace(u'\r\n', u'\n'))
self.verseListWidget.clear()
self.verseListWidget.setRowCount(0)
@@ -670,6 +700,66 @@
self.saveSong(True)
Receiver.send_message(u'songs_preview')
+ def onAudioAddFromFileButtonClicked(self):
+ """
+ Loads file(s) from the filesystem.
+ """
+ filters = u'%s (*)' % UiStrings().AllFiles
+ filenames = QtGui.QFileDialog.getOpenFileNames(self,
+ translate('SongsPlugin.EditSongForm', 'Open File(s)'),
+ QtCore.QString(), filters)
+ for filename in filenames:
+ item = QtGui.QListWidgetItem(os.path.split(unicode(filename))[1])
+ item.setData(QtCore.Qt.UserRole, filename)
+ self.audioListWidget.addItem(item)
+
+ def onAudioAddFromMediaButtonClicked(self):
+ """
+ Loads file(s) from the media plugin.
+ """
+ if self.mediaForm.exec_():
+ for filename in self.mediaForm.getSelectedFiles():
+ item = QtGui.QListWidgetItem(os.path.split(unicode(filename))[1])
+ item.setData(QtCore.Qt.UserRole, filename)
+ self.audioListWidget.addItem(item)
+
+ def onAudioRemoveButtonClicked(self):
+ """
+ Removes a file from the list.
+ """
+ row = self.audioListWidget.currentRow()
+ if row == -1:
+ return
+ self.audioListWidget.takeItem(row)
+
+ def onAudioRemoveAllButtonClicked(self):
+ """
+ Removes all files from the list.
+ """
+ self.audioListWidget.clear()
+
+ def onUpButtonClicked(self):
+ """
+ Moves a file up when the user clicks the up button on the audio tab.
+ """
+ row = self.audioListWidget.currentRow()
+ if row <= 0:
+ return
+ item = self.audioListWidget.takeItem(row)
+ self.audioListWidget.insertItem(row - 1, item)
+ self.audioListWidget.setCurrentRow(row - 1)
+
+ def onDownButtonClicked(self):
+ """
+ Moves a file down when the user clicks the up button on the audio tab.
+ """
+ row = self.audioListWidget.currentRow()
+ if row == -1 or row > self.audioListWidget.count() - 1:
+ return
+ item = self.audioListWidget.takeItem(row)
+ self.audioListWidget.insertItem(row + 1, item)
+ self.audioListWidget.setCurrentRow(row + 1)
+
def clearCaches(self):
"""
Free up autocompletion memory on dialog exit
@@ -744,18 +834,50 @@
self.song.theme_name = None
self._processLyrics()
self.song.authors = []
- for row in range(self.authorsListView.count()):
+ for row in xrange(self.authorsListView.count()):
item = self.authorsListView.item(row)
authorId = (item.data(QtCore.Qt.UserRole)).toInt()[0]
self.song.authors.append(self.manager.get_object(Author, authorId))
self.song.topics = []
- for row in range(self.topicsListView.count()):
+ for row in xrange(self.topicsListView.count()):
item = self.topicsListView.item(row)
topicId = (item.data(QtCore.Qt.UserRole)).toInt()[0]
self.song.topics.append(self.manager.get_object(Topic, topicId))
- clean_song(self.manager, self.song)
- self.manager.save_object(self.song)
- self.mediaitem.auto_select_id = self.song.id
+ # Save the song here because we need a valid song id for the audio files.
+ clean_song(self.manager, self.song)
+ self.manager.save_object(self.song)
+ audio_files = map(lambda a: a.file_name, self.song.media_files)
+ log.debug(audio_files)
+ save_path = os.path.join(
+ AppLocation.get_section_data_path(self.mediaitem.plugin.name),
+ 'audio', str(self.song.id))
+ if not os.path.exists(save_path):
+ os.makedirs(save_path)
+ self.song.media_files = []
+ files = []
+ for row in xrange(self.audioListWidget.count()):
+ item = self.audioListWidget.item(row)
+ filename = unicode(item.data(QtCore.Qt.UserRole).toString())
+ if not filename.startswith(save_path):
+ oldfile, filename = filename, os.path.join(save_path,
+ os.path.split(filename)[1])
+ shutil.copyfile(oldfile, filename)
+ files.append(filename)
+ media_file = MediaFile()
+ media_file.file_name = filename
+ media_file.type = u'audio'
+ media_file.weight = row
+ self.song.media_files.append(media_file)
+ for audio in audio_files:
+ if audio not in files:
+ try:
+ os.remove(audio)
+ except:
+ log.exception('Could not remove file: %s', audio)
+ pass
+ clean_song(self.manager, self.song)
+ self.manager.save_object(self.song)
+ self.mediaitem.autoSelectId = self.song.id
def _processLyrics(self):
"""
@@ -783,3 +905,4 @@
except:
log.exception(u'Problem processing song Lyrics \n%s',
sxml.dump_xml())
+
=== added file 'openlp/plugins/songs/forms/mediafilesdialog.py'
--- openlp/plugins/songs/forms/mediafilesdialog.py 1970-01-01 00:00:00 +0000
+++ openlp/plugins/songs/forms/mediafilesdialog.py 2011-09-02 18:47:24 +0000
@@ -0,0 +1,75 @@
+# -*- 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 PyQt4 import QtCore, QtGui
+
+from openlp.core.lib import translate, build_icon
+
+class Ui_MediaFilesDialog(object):
+ def setupUi(self, mediaFilesDialog):
+ mediaFilesDialog.setObjectName(u'mediaFilesDialog')
+ mediaFilesDialog.setWindowModality(QtCore.Qt.ApplicationModal)
+ mediaFilesDialog.resize(400, 300)
+ mediaFilesDialog.setModal(True)
+ mediaFilesDialog.setWindowIcon(
+ build_icon(u':/icon/openlp-logo-16x16.png'))
+ self.filesVerticalLayout = QtGui.QVBoxLayout(mediaFilesDialog)
+ self.filesVerticalLayout.setSpacing(8)
+ self.filesVerticalLayout.setMargin(8)
+ self.filesVerticalLayout.setObjectName(u'filesVerticalLayout')
+ self.selectLabel = QtGui.QLabel(mediaFilesDialog)
+ self.selectLabel.setWordWrap(True)
+ self.selectLabel.setObjectName(u'selectLabel')
+ self.filesVerticalLayout.addWidget(self.selectLabel)
+ self.fileListWidget = QtGui.QListWidget(mediaFilesDialog)
+ self.fileListWidget.setAlternatingRowColors(True)
+ self.fileListWidget.setSelectionMode(
+ QtGui.QAbstractItemView.ExtendedSelection)
+ self.fileListWidget.setObjectName(u'fileListWidget')
+ self.filesVerticalLayout.addWidget(self.fileListWidget)
+ self.buttonBox = QtGui.QDialogButtonBox(mediaFilesDialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(
+ QtGui.QDialogButtonBox.Cancel | QtGui.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName(u'buttonBox')
+ self.filesVerticalLayout.addWidget(self.buttonBox)
+
+ self.retranslateUi(mediaFilesDialog)
+ QtCore.QObject.connect(self.buttonBox,
+ QtCore.SIGNAL(u'accepted()'), mediaFilesDialog.accept)
+ QtCore.QObject.connect(self.buttonBox,
+ QtCore.SIGNAL(u'rejected()'), mediaFilesDialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(mediaFilesDialog)
+
+ def retranslateUi(self, mediaFilesDialog):
+ mediaFilesDialog.setWindowTitle(
+ translate('SongsPlugin.MediaFilesForm', 'Select Media File(s)'))
+ self.selectLabel.setText(
+ translate('SongsPlugin.MediaFilesForm', u'Select one or more '
+ 'audio files from the list below, and click OK to import them '
+ 'into this song.'))
+
=== added file 'openlp/plugins/songs/forms/mediafilesform.py'
--- openlp/plugins/songs/forms/mediafilesform.py 1970-01-01 00:00:00 +0000
+++ openlp/plugins/songs/forms/mediafilesform.py 2011-09-02 18:47:24 +0000
@@ -0,0 +1,57 @@
+# -*- 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 os
+
+from PyQt4 import QtCore, QtGui
+
+from mediafilesdialog import Ui_MediaFilesDialog
+
+log = logging.getLogger(__name__)
+
+class MediaFilesForm(QtGui.QDialog, Ui_MediaFilesDialog):
+ """
+ Class to show a list of files from the
+ """
+ log.info(u'%s MediaFilesForm loaded', __name__)
+
+ def __init__(self, parent):
+ QtGui.QDialog.__init__(self)
+ self.setupUi(self)
+
+ def populateFiles(self, files):
+ self.fileListWidget.clear()
+ for file in files:
+ item = QtGui.QListWidgetItem(os.path.split(file)[1])
+ item.setData(QtCore.Qt.UserRole, file)
+ self.fileListWidget.addItem(item)
+
+ def getSelectedFiles(self):
+ return map(lambda x: unicode(x.data(QtCore.Qt.UserRole).toString()),
+ self.fileListWidget.selectedItems())
+
=== modified file 'openlp/plugins/songs/lib/db.py'
--- openlp/plugins/songs/lib/db.py 2011-08-26 13:13:32 +0000
+++ openlp/plugins/songs/lib/db.py 2011-09-02 18:47:24 +0000
@@ -232,7 +232,8 @@
'authors': relation(Author, backref='songs',
secondary=authors_songs_table, lazy=False),
'book': relation(Book, backref='songs'),
- 'media_files': relation(MediaFile, backref='songs'),
+ 'media_files': relation(MediaFile, backref='songs',
+ order_by=media_files_table.c.weight),
'topics': relation(Topic, backref='songs',
secondary=songs_topics_table)
})
=== modified file 'openlp/plugins/songs/lib/mediaitem.py'
--- openlp/plugins/songs/lib/mediaitem.py 2011-07-07 17:03:38 +0000
+++ openlp/plugins/songs/lib/mediaitem.py 2011-09-02 18:47:24 +0000
@@ -28,6 +28,8 @@
import logging
import locale
import re
+import os
+import shutil
from PyQt4 import QtCore, QtGui
from sqlalchemy.sql import or_
@@ -37,11 +39,12 @@
from openlp.core.lib.searchedit import SearchEdit
from openlp.core.lib.ui import UiStrings, context_menu_action, \
context_menu_separator
+from openlp.core.utils import AppLocation
from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \
SongImportForm, SongExportForm
from openlp.plugins.songs.lib import OpenLyrics, SongXML, VerseType, \
clean_string
-from openlp.plugins.songs.lib.db import Author, Song
+from openlp.plugins.songs.lib.db import Author, Song, MediaFile
from openlp.plugins.songs.lib.ui import SongStrings
log = logging.getLogger(__name__)
@@ -79,6 +82,22 @@
self.quickPreviewAllowed = True
self.hasSearch = True
+ def _updateBackgroundAudio(self, song, item):
+ song.media_files = []
+ for i, bga in enumerate(item.background_audio):
+ dest_file = os.path.join(
+ AppLocation.get_section_data_path(self.plugin.name),
+ u'audio', str(song.id), os.path.split(bga)[1])
+ if not os.path.exists(os.path.split(dest_file)[0]):
+ os.makedirs(os.path.split(dest_file)[0])
+ shutil.copyfile(os.path.join(
+ AppLocation.get_section_data_path(
+ u'servicemanager'), bga),
+ dest_file)
+ song.media_files.append(MediaFile.populate(
+ weight=i, file_name=dest_file))
+ self.plugin.manager.save_object(song, True)
+
def addEndHeaderBar(self):
self.addToolbarSeparator()
## Song Maintenance Button ##
@@ -210,7 +229,7 @@
search_results = self.plugin.manager.get_all_objects(Song,
Song.theme_name.like(u'%' + search_keywords + u'%'))
self.displayResultsSong(search_results)
- self.check_search_result()
+ self.checkSearchReslt()
def searchEntire(self, search_keywords):
return self.plugin.manager.get_all_objects(Song,
@@ -244,7 +263,7 @@
def displayResultsSong(self, searchresults):
log.debug(u'display results Song')
- self.save_auto_select_id()
+ self.saveAutoSelectId()
self.listView.clear()
# Sort the songs by its title considering language specific characters.
# lower() is needed for windows!
@@ -258,9 +277,9 @@
song_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(song.id))
self.listView.addItem(song_name)
# Auto-select the item if name has been set
- if song.id == self.auto_select_id:
+ if song.id == self.autoSelectId:
self.listView.setCurrentItem(song_name)
- self.auto_select_id = -1
+ self.autoSelectId = -1
def displayResultsAuthor(self, searchresults):
log.debug(u'display results Author')
@@ -312,7 +331,7 @@
self.edit_song_form.exec_()
self.onClearTextButtonClick()
self.onSelectionChange()
- self.auto_select_id = -1
+ self.autoSelectId = -1
def onSongMaintenanceClick(self):
self.song_maintenance_form.exec_()
@@ -335,9 +354,9 @@
if valid:
self.remoteSong = song_id
self.remoteTriggered = remote_type
- self.edit_song_form.loadSong(song_id, (remote_type == u'P'))
+ self.edit_song_form.loadSong(song_id, remote_type == u'P')
self.edit_song_form.exec_()
- self.auto_select_id = -1
+ self.autoSelectId = -1
self.onSongListLoad()
def onEditClick(self):
@@ -350,7 +369,7 @@
item_id = (self.editItem.data(QtCore.Qt.UserRole)).toInt()[0]
self.edit_song_form.loadSong(item_id, False)
self.edit_song_form.exec_()
- self.auto_select_id = -1
+ self.autoSelectId = -1
self.onSongListLoad()
self.editItem = None
@@ -395,12 +414,12 @@
def generateSlideData(self, service_item, item=None, xmlVersion=False):
log.debug(u'generateSlideData (%s:%s)' % (service_item, item))
item_id = self._getIdOfItemToGenerate(item, self.remoteSong)
- service_item.add_capability(ItemCapabilities.AllowsEdit)
- service_item.add_capability(ItemCapabilities.AllowsPreview)
- service_item.add_capability(ItemCapabilities.AllowsLoop)
+ service_item.add_capability(ItemCapabilities.CanEdit)
+ service_item.add_capability(ItemCapabilities.CanPreview)
+ service_item.add_capability(ItemCapabilities.CanLoop)
service_item.add_capability(ItemCapabilities.OnLoadUpdate)
service_item.add_capability(ItemCapabilities.AddIfNewItem)
- service_item.add_capability(ItemCapabilities.AllowsVirtualSplit)
+ service_item.add_capability(ItemCapabilities.CanSoftBreak)
song = self.plugin.manager.get_object(Song, item_id)
service_item.theme = song.theme_name
service_item.edit_id = item_id
@@ -471,6 +490,10 @@
service_item.data_string = {u'title': song.search_title,
u'authors': u', '.join(author_list)}
service_item.xml_version = self.openLyrics.song_to_xml(song)
+ # Add the audio file to the service item.
+ if len(song.media_files) > 0:
+ service_item.add_capability(ItemCapabilities.HasBackgroundAudio)
+ service_item.background_audio = [m.file_name for m in song.media_files]
return True
def serviceLoad(self, item):
@@ -510,8 +533,15 @@
add_song = False
editId = song.id
break
+ # If there's any backing tracks, copy them over.
+ if len(item.background_audio) > 0:
+ self._updateBackgroundAudio(song, item)
if add_song and self.addSongFromService:
- editId = self.openLyrics.xml_to_song(item.xml_version)
+ song = self.openLyrics.xml_to_song(item.xml_version)
+ # If there's any backing tracks, copy them over.
+ if len(item.background_audio) > 0:
+ self._updateBackgroundAudio(song, item)
+ editId = song.id
self.onSearchTextButtonClick()
# Update service with correct song id.
if editId:
=== modified file 'openlp/plugins/songs/lib/xml.py'
--- openlp/plugins/songs/lib/xml.py 2011-08-14 10:56:05 +0000
+++ openlp/plugins/songs/lib/xml.py 2011-09-02 18:47:24 +0000
@@ -343,7 +343,7 @@
self._process_topics(properties, song)
clean_song(self.manager, song)
self.manager.save_object(song)
- return song.id
+ return song
def _add_text_to_element(self, tag, parent, text=None, label=None):
if label:
=== added file 'resources/forms/mediafilesdialog.ui'
--- resources/forms/mediafilesdialog.ui 1970-01-01 00:00:00 +0000
+++ resources/forms/mediafilesdialog.ui 2011-09-02 18:47:24 +0000
@@ -0,0 +1,95 @@
+<?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>