This commit is contained in:
Andreas Preikschat 2011-02-24 13:53:02 +01:00
commit 2b41cf545a
18 changed files with 816 additions and 134 deletions

View File

@ -63,7 +63,6 @@ from splashscreen import SplashScreen
from generaltab import GeneralTab
from themestab import ThemesTab
from advancedtab import AdvancedTab
from displaytagtab import DisplayTagTab
from aboutform import AboutForm
from pluginform import PluginForm
from settingsform import SettingsForm

View File

@ -28,8 +28,9 @@ The :mod:`advancedtab` provides an advanced settings facility.
"""
from PyQt4 import QtCore, QtGui
from openlp.core.lib import SettingsTab, translate
from openlp.core.lib import SettingsTab, translate, build_icon
from openlp.core.lib.ui import UiStrings
from openlp.core.utils import get_images_filter
class AdvancedTab(SettingsTab):
"""
@ -41,6 +42,8 @@ class AdvancedTab(SettingsTab):
Initialise the settings tab
"""
SettingsTab.__init__(self, u'Advanced')
self.default_image = u':/graphics/openlp-splash-screen.png'
self.default_color = u'#ffffff'
def setupUi(self):
"""
@ -81,33 +84,38 @@ class AdvancedTab(SettingsTab):
self.hideMouseCheckBox.setObjectName(u'hideMouseCheckBox')
self.hideMouseLayout.addWidget(self.hideMouseCheckBox)
self.leftLayout.addWidget(self.hideMouseGroupBox)
# self.sharedDirGroupBox = QtGui.QGroupBox(self.leftColumn)
# self.sharedDirGroupBox.setObjectName(u'sharedDirGroupBox')
# self.sharedDirLayout = QtGui.QFormLayout(self.sharedDirGroupBox)
# self.sharedCheckBox = QtGui.QCheckBox(self.sharedDirGroupBox)
# self.sharedCheckBox.setObjectName(u'sharedCheckBox')
# self.sharedDirLayout.addRow(self.sharedCheckBox)
# self.sharedLabel = QtGui.QLabel(self.sharedDirGroupBox)
# self.sharedLabel.setObjectName(u'sharedLabel')
# self.sharedSubLayout = QtGui.QHBoxLayout()
# self.sharedSubLayout.setObjectName(u'sharedSubLayout')
# self.sharedLineEdit = QtGui.QLineEdit(self.sharedDirGroupBox)
# self.sharedLineEdit.setObjectName(u'sharedLineEdit')
# self.sharedSubLayout.addWidget(self.sharedLineEdit)
# self.sharedPushButton = QtGui.QPushButton(self.sharedDirGroupBox)
# self.sharedPushButton.setObjectName(u'sharedPushButton')
# self.sharedSubLayout.addWidget(self.sharedPushButton)
# self.sharedDirLayout.addRow(self.sharedLabel, self.sharedSubLayout)
# self.leftLayout.addWidget(self.sharedDirGroupBox)
self.leftLayout.addStretch()
# self.databaseGroupBox = QtGui.QGroupBox(self.rightColumn)
# self.databaseGroupBox.setObjectName(u'databaseGroupBox')
# self.databaseGroupBox.setEnabled(False)
# self.databaseLayout = QtGui.QVBoxLayout(self.databaseGroupBox)
# self.rightLayout.addWidget(self.databaseGroupBox)
self.defaultImageGroupBox = QtGui.QGroupBox(self.rightColumn)
self.defaultImageGroupBox.setObjectName(u'defaultImageGroupBox')
self.defaultImageLayout = QtGui.QFormLayout(self.defaultImageGroupBox)
self.defaultImageLayout.setObjectName(u'defaultImageLayout')
self.defaultColorLabel = QtGui.QLabel(self.defaultImageGroupBox)
self.defaultColorLabel.setObjectName(u'defaultColorLabel')
self.defaultColorButton = QtGui.QPushButton(self.defaultImageGroupBox)
self.defaultColorButton.setObjectName(u'defaultColorButton')
self.defaultImageLayout.addRow(self.defaultColorLabel,
self.defaultColorButton)
self.defaultFileLabel = QtGui.QLabel(self.defaultImageGroupBox)
self.defaultFileLabel.setObjectName(u'defaultFileLabel')
self.defaultFileEdit = QtGui.QLineEdit(self.defaultImageGroupBox)
self.defaultFileEdit.setObjectName(u'defaultFileEdit')
self.defaultBrowseButton = QtGui.QToolButton(self.defaultImageGroupBox)
self.defaultBrowseButton.setObjectName(u'defaultBrowseButton')
self.defaultBrowseButton.setIcon(
build_icon(u':/general/general_open.png'))
self.defaultFileLayout = QtGui.QHBoxLayout()
self.defaultFileLayout.setObjectName(u'defaultFileLayout')
self.defaultFileLayout.addWidget(self.defaultFileEdit)
self.defaultFileLayout.addWidget(self.defaultBrowseButton)
self.defaultImageLayout.addRow(self.defaultFileLabel,
self.defaultFileLayout)
self.rightLayout.addWidget(self.defaultImageGroupBox)
self.rightLayout.addStretch()
# QtCore.QObject.connect(self.sharedCheckBox,
# QtCore.SIGNAL(u'stateChanged(int)'), self.onSharedCheckBoxChanged)
QtCore.QObject.connect(self.defaultColorButton,
QtCore.SIGNAL(u'pressed()'), self.onDefaultColorButtonPressed)
QtCore.QObject.connect(self.defaultBrowseButton,
QtCore.SIGNAL(u'pressed()'), self.onDefaultBrowseButtonPressed)
def retranslateUi(self):
"""
@ -129,14 +137,13 @@ class AdvancedTab(SettingsTab):
self.hideMouseGroupBox.setTitle(translate('OpenLP.AdvancedTab',
'Mouse Cursor'))
self.hideMouseCheckBox.setText(translate('OpenLP.AdvancedTab',
'Hide the mouse cursor when moved over the display window'))
# self.sharedDirGroupBox.setTitle(
# translate('AdvancedTab', 'Central Data Store'))
# self.sharedCheckBox.setText(
# translate('AdvancedTab', 'Enable a shared data location'))
# self.sharedLabel.setText(translate('AdvancedTab', 'Store location:'))
# self.sharedPushButton.setText(UiStrings.Browse)
# self.databaseGroupBox.setTitle(translate('AdvancedTab', 'Databases'))
'Hide mouse cursor when over display window'))
self.defaultImageGroupBox.setTitle(translate('OpenLP.AdvancedTab',
'Default Image'))
self.defaultColorLabel.setText(translate('OpenLP.AdvancedTab',
'Background color:'))
self.defaultFileLabel.setText(translate('OpenLP.AdvancedTab',
'Image file:'))
def load(self):
"""
@ -165,7 +172,14 @@ class AdvancedTab(SettingsTab):
QtCore.QVariant(True)).toBool())
self.hideMouseCheckBox.setChecked(
settings.value(u'hide mouse', QtCore.QVariant(False)).toBool())
self.default_color = settings.value(u'default color',
QtCore.QVariant(u'#ffffff')).toString()
self.defaultFileEdit.setText(settings.value(u'default image',
QtCore.QVariant(u':/graphics/openlp-splash-screen.png'))\
.toString())
settings.endGroup()
self.defaultColorButton.setStyleSheet(
u'background-color: %s' % self.default_color)
def save(self):
"""
@ -185,12 +199,24 @@ class AdvancedTab(SettingsTab):
QtCore.QVariant(self.enableAutoCloseCheckBox.isChecked()))
settings.setValue(u'hide mouse',
QtCore.QVariant(self.hideMouseCheckBox.isChecked()))
settings.setValue(u'default color', self.default_color)
settings.setValue(u'default image', self.defaultFileEdit.text())
settings.endGroup()
# def onSharedCheckBoxChanged(self, checked):
# """
# Enables the widgets to allow a shared data location
# """
# self.sharedLabel.setEnabled(checked)
# self.sharedTextEdit.setEnabled(checked)
# self.sharedPushButton.setEnabled(checked)
def onDefaultColorButtonPressed(self):
new_color = QtGui.QColorDialog.getColor(
QtGui.QColor(self.default_color), self)
if new_color.isValid():
self.default_color = new_color.name()
self.defaultColorButton.setStyleSheet(
u'background-color: %s' % self.default_color)
def onDefaultBrowseButtonPressed(self):
file_filters = u'%s;;%s (*.*) (*)' % (get_images_filter(),
UiStrings.AllFiles)
filename = QtGui.QFileDialog.getOpenFileName(self,
translate('OpenLP.AdvancedTab', 'Open File'), '',
file_filters)
if filename:
self.defaultFileEdit.setText(filename)
self.defaultFileEdit.setFocus()

View File

@ -123,30 +123,30 @@ class Ui_DisplayTagDialog(object):
QtCore.QMetaObject.connectSlotsByName(displayTagDialog)
def retranslateUi(self, displayTagDialog):
displayTagDialog.setWindowTitle(translate('OpenLP.displayTagForm',
displayTagDialog.setWindowTitle(translate('OpenLP.displayTagDialog',
'Configure Display Tags'))
self.editGroupBox.setTitle(
translate('OpenLP.DisplayTagTab', 'Edit Selection'))
translate('OpenLP.DisplayTagDialog', 'Edit Selection'))
self.updatePushButton.setText(
translate('OpenLP.DisplayTagTab', 'Update'))
translate('OpenLP.DisplayTagDialog', 'Update'))
self.descriptionLabel.setText(
translate('OpenLP.DisplayTagTab', 'Description'))
self.tagLabel.setText(translate('OpenLP.DisplayTagTab', 'Tag'))
translate('OpenLP.DisplayTagDialog', 'Description'))
self.tagLabel.setText(translate('OpenLP.DisplayTagDialog', 'Tag'))
self.startTagLabel.setText(
translate('OpenLP.DisplayTagTab', 'Start tag'))
self.endTagLabel.setText(translate('OpenLP.DisplayTagTab', 'End tag'))
translate('OpenLP.DisplayTagDialog', 'Start tag'))
self.endTagLabel.setText(translate('OpenLP.DisplayTagDialog', 'End tag'))
self.deletePushButton.setText(UiStrings.Delete)
self.defaultPushButton.setText(
translate('OpenLP.DisplayTagTab', 'Default'))
translate('OpenLP.DisplayTagDialog', 'Default'))
self.newPushButton.setText(UiStrings.New)
self.tagTableWidget.horizontalHeaderItem(0).setText(
translate('OpenLP.DisplayTagTab', 'Description'))
translate('OpenLP.DisplayTagDialog', 'Description'))
self.tagTableWidget.horizontalHeaderItem(1).setText(
translate('OpenLP.DisplayTagTab', 'Tag id'))
translate('OpenLP.DisplayTagDialog', 'Tag id'))
self.tagTableWidget.horizontalHeaderItem(2).setText(
translate('OpenLP.DisplayTagTab', 'Start Html'))
translate('OpenLP.DisplayTagDialog', 'Start Html'))
self.tagTableWidget.horizontalHeaderItem(3).setText(
translate('OpenLP.DisplayTagTab', 'End Html'))
translate('OpenLP.DisplayTagDialog', 'End Html'))
self.tagTableWidget.setColumnWidth(0, 120)
self.tagTableWidget.setColumnWidth(1, 40)
self.tagTableWidget.setColumnWidth(2, 240)

View File

@ -47,6 +47,7 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog):
"""
QtGui.QDialog.__init__(self, parent)
self.setupUi(self)
self.preLoad()
QtCore.QObject.connect(self.tagTableWidget,
QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onRowSelected)
QtCore.QObject.connect(self.defaultPushButton,
@ -63,7 +64,18 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog):
Load Display and set field state.
"""
# Create initial copy from master
self.preLoad()
self._resetTable()
self.selected = -1
return QtGui.QDialog.exec_(self)
def preLoad(self):
"""
Load the Tags from store so can be used in the system or used to
update the display. If Cancel was selected this is needed to reset the
dsiplay to the correct version.
"""
# Initial Load of the Tags
DisplayTags.reset_html_tags()
user_expands = QtCore.QSettings().value(u'displayTags/html_tags',
QtCore.QVariant(u'')).toString()
@ -74,37 +86,6 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog):
# If we have some user ones added them as well
for t in user_tags:
DisplayTags.add_html_tag(t)
self.selected = -1
self.load()
return QtGui.QDialog.exec_(self)
def load(self):
"""
Load the form with data and set the initial state of the buttons
"""
self.newPushButton.setEnabled(True)
self.updatePushButton.setEnabled(False)
self.deletePushButton.setEnabled(False)
for linenumber, html in enumerate(DisplayTags.get_html_tags()):
self.tagTableWidget.setRowCount(
self.tagTableWidget.rowCount() + 1)
self.tagTableWidget.setItem(linenumber, 0,
QtGui.QTableWidgetItem(html[u'desc']))
self.tagTableWidget.setItem(linenumber, 1,
QtGui.QTableWidgetItem(self._strip(html[u'start tag'])))
self.tagTableWidget.setItem(linenumber, 2,
QtGui.QTableWidgetItem(html[u'start html']))
self.tagTableWidget.setItem(linenumber, 3,
QtGui.QTableWidgetItem(html[u'end html']))
self.tagTableWidget.resizeRowsToContents()
self.descriptionLineEdit.setText(u'')
self.tagLineEdit.setText(u'')
self.startTagLineEdit.setText(u'')
self.endTagLineEdit.setText(u'')
self.descriptionLineEdit.setEnabled(False)
self.tagLineEdit.setEnabled(False)
self.startTagLineEdit.setEnabled(False)
self.endTagLineEdit.setEnabled(False)
def accept(self):
"""
@ -223,6 +204,29 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog):
"""
self.tagTableWidget.clearContents()
self.tagTableWidget.setRowCount(0)
self.newPushButton.setEnabled(True)
self.updatePushButton.setEnabled(False)
self.deletePushButton.setEnabled(False)
for linenumber, html in enumerate(DisplayTags.get_html_tags()):
self.tagTableWidget.setRowCount(
self.tagTableWidget.rowCount() + 1)
self.tagTableWidget.setItem(linenumber, 0,
QtGui.QTableWidgetItem(html[u'desc']))
self.tagTableWidget.setItem(linenumber, 1,
QtGui.QTableWidgetItem(self._strip(html[u'start tag'])))
self.tagTableWidget.setItem(linenumber, 2,
QtGui.QTableWidgetItem(html[u'start html']))
self.tagTableWidget.setItem(linenumber, 3,
QtGui.QTableWidgetItem(html[u'end html']))
self.tagTableWidget.resizeRowsToContents()
self.descriptionLineEdit.setText(u'')
self.tagLineEdit.setText(u'')
self.startTagLineEdit.setText(u'')
self.endTagLineEdit.setText(u'')
self.descriptionLineEdit.setEnabled(False)
self.tagLineEdit.setEnabled(False)
self.startTagLineEdit.setEnabled(False)
self.endTagLineEdit.setEnabled(False)
def _strip(self, tag):
"""

View File

@ -132,14 +132,22 @@ class MainDisplay(DisplayWidget):
painter_image.begin(self.black)
painter_image.fillRect(self.black.rect(), QtCore.Qt.black)
# Build the initial frame.
image_file = QtCore.QSettings().value(u'advanced/default image',
QtCore.QVariant(u':/graphics/openlp-splash-screen.png'))\
.toString()
background_color = QtGui.QColor(QtCore.QSettings().value(
u'advanced/default color',
QtCore.QVariant(u'#ffffff')).toString())
if not background_color.isValid():
background_color = QtCore.Qt.white
splash_image = QtGui.QImage(image_file)
initialFrame = QtGui.QImage(
self.screens.current[u'size'].width(),
self.screens.current[u'size'].height(),
QtGui.QImage.Format_ARGB32_Premultiplied)
splash_image = QtGui.QImage(u':/graphics/openlp-splash-screen.png')
painter_image = QtGui.QPainter()
painter_image.begin(initialFrame)
painter_image.fillRect(initialFrame.rect(), QtCore.Qt.white)
painter_image.fillRect(initialFrame.rect(), background_color)
painter_image.drawImage(
(self.screens.current[u'size'].width() -
splash_image.width()) / 2,

View File

@ -47,6 +47,7 @@ class WizardStrings(object):
CSV = u'CSV'
EW = u'EasyWorship'
ES = u'EasiSlides'
FP = u'Foilpresenter'
OL = u'OpenLyrics'
OS = u'OpenSong'
OSIS = u'OSIS'

View File

@ -61,8 +61,8 @@ class MediaMediaItem(MediaManagerItem):
self.onNewPrompt = translate('MediaPlugin.MediaItem', 'Select Media')
self.onNewFileMasks = unicode(translate('MediaPlugin.MediaItem',
'Videos (%s);;Audio (%s);;%s (*)')) % (
u' '.join(self.parent.video_list),
u' '.join(self.parent.audio_list), UiStrings.AllFiles)
u' '.join(self.parent.video_extensions_list),
u' '.join(self.parent.audio_extensions_list), UiStrings.AllFiles)
self.replaceAction.setText(UiStrings.ReplaceBG)
self.replaceAction.setToolTip(UiStrings.ReplaceLiveBG)
self.resetAction.setText(UiStrings.ResetBG)

View File

@ -45,28 +45,26 @@ class MediaPlugin(Plugin):
self.icon = build_icon(self.icon_path)
# passed with drag and drop messages
self.dnd_id = u'Media'
self.audio_list = []
self.video_list = []
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_list, mimetype)
self._addToList(self.audio_extensions_list, mimetype)
elif mimetype.startswith(u'video/'):
self._addToList(self.video_list, mimetype)
log.info(u'MediaPlugin handles audio extensions: %s',
u' '.join(self.audio_list))
log.info(u'MediaPlugin handles video extensions: %s',
u' '.join(self.video_list))
self._addToList(self.video_extensions_list, mimetype)
def _addToList(self, list, mimetype):
# Is it a media type
# 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)))
def about(self):
about_text = translate('MediaPlugin', '<strong>Media Plugin</strong>'

View File

@ -68,11 +68,13 @@ class HttpServer(object):
"""
log.debug(u'Start TCP server')
port = QtCore.QSettings().value(
self.parent.settingsSection + u'/remote port',
self.parent.settingsSection + u'/port',
QtCore.QVariant(4316)).toInt()[0]
address = QtCore.QSettings().value(
self.parent.settingsSection + u'/ip address',
QtCore.QVariant(u'0.0.0.0')).toString()
self.server = QtNetwork.QTcpServer()
self.server.listen(QtNetwork.QHostAddress(QtNetwork.QHostAddress.Any),
port)
self.server.listen(QtNetwork.QHostAddress(address), port)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'slidecontroller_live_changed'),
self.slide_change)
@ -347,4 +349,4 @@ class HttpConnection(object):
log.debug(u'close socket')
self.socket.close()
self.socket = None
self.parent.close_connection(self)
self.parent.close_connection(self)

View File

@ -146,6 +146,12 @@ class SongImportForm(OpenLPWizard):
QtCore.QObject.connect(self.songShowPlusRemoveButton,
QtCore.SIGNAL(u'clicked()'),
self.onSongShowPlusRemoveButtonClicked)
QtCore.QObject.connect(self.foilPresenterAddButton,
QtCore.SIGNAL(u'clicked()'),
self.onFoilPresenterAddButtonClicked)
QtCore.QObject.connect(self.foilPresenterRemoveButton,
QtCore.SIGNAL(u'clicked()'),
self.onFoilPresenterRemoveButtonClicked)
def addCustomPages(self):
"""
@ -196,6 +202,8 @@ class SongImportForm(OpenLPWizard):
self.addFileSelectItem(u'songBeamer')
# Song Show Plus
self.addFileSelectItem(u'songShowPlus')
# Foilpresenter
self.addFileSelectItem(u'foilPresenter')
# Commented out for future use.
# self.addFileSelectItem(u'csv', u'CSV', single_select=True)
self.sourceLayout.addLayout(self.formatStack)
@ -238,6 +246,8 @@ class SongImportForm(OpenLPWizard):
SongFormat.SongBeamer, WizardStrings.SB)
self.formatComboBox.setItemText(
SongFormat.SongShowPlus, WizardStrings.SSP)
self.formatComboBox.setItemText(
SongFormat.FoilPresenter, WizardStrings.FP)
# self.formatComboBox.setItemText(SongFormat.CSV, WizardStrings.CSV)
self.openLP2FilenameLabel.setText(
translate('SongsPlugin.ImportWizardForm', 'Filename:'))
@ -297,6 +307,10 @@ class SongImportForm(OpenLPWizard):
translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
self.songShowPlusRemoveButton.setText(
translate('SongsPlugin.ImportWizardForm', 'Remove File(s)'))
self.foilPresenterAddButton.setText(
translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
self.foilPresenterRemoveButton.setText(
translate('SongsPlugin.ImportWizardForm', 'Remove File(s)'))
# self.csvFilenameLabel.setText(
# translate('SongsPlugin.ImportWizardForm', 'Filename:'))
# self.csvBrowseButton.setText(UiStrings.Browse)
@ -394,6 +408,12 @@ class SongImportForm(OpenLPWizard):
WizardStrings.YouSpecifyFile % WizardStrings.SSP)
self.wordsOfWorshipAddButton.setFocus()
return False
elif source_format == SongFormat.FoilPresenter:
if self.foilPresenterFileListWidget.count() == 0:
critical_error_message_box(UiStrings.NFSp,
WizardStrings.YouSpecifyFile % WizardStrings.FP)
self.foilPresenterAddButton.setFocus()
return False
return True
elif self.currentPage() == self.progressPage:
return True
@ -574,7 +594,7 @@ class SongImportForm(OpenLPWizard):
Remove selected SongBeamer files from the import list
"""
self.removeSelectedItems(self.songBeamerFileListWidget)
def onSongShowPlusAddButtonClicked(self):
"""
Get SongShow Plus song database files
@ -591,6 +611,22 @@ class SongImportForm(OpenLPWizard):
"""
self.removeSelectedItems(self.songShowPlusFileListWidget)
def onFoilPresenterAddButtonClicked(self):
"""
Get FoilPresenter song database files
"""
self.getFiles(WizardStrings.OpenTypeFile % WizardStrings.FP,
self.foilPresenterFileListWidget, u'%s (*.foil)'
% translate('SongsPlugin.ImportWizardForm',
'Foilpresenter Song Files')
)
def onFoilPresenterRemoveButtonClicked(self):
"""
Remove selected FoilPresenter files from the import list
"""
self.removeSelectedItems(self.foilPresenterFileListWidget)
def setDefaults(self):
"""
Set default form values for the song import wizard.
@ -611,6 +647,7 @@ class SongImportForm(OpenLPWizard):
self.ewFilenameEdit.setText(u'')
self.songBeamerFileListWidget.clear()
self.songShowPlusFileListWidget.clear()
self.foilPresenterFileListWidget.clear()
#self.csvFilenameEdit.setText(u'')
def preWizard(self):
@ -691,6 +728,11 @@ class SongImportForm(OpenLPWizard):
importer = self.plugin.importSongs(SongFormat.SongShowPlus,
filenames=self.getListOfFiles(self.songShowPlusFileListWidget)
)
elif source_format == SongFormat.FoilPresenter:
# Import Foilpresenter songs
importer = self.plugin.importSongs(SongFormat.FoilPresenter,
filenames=self.getListOfFiles(self.foilPresenterFileListWidget)
)
if importer.do_import():
self.progressLabel.setText(WizardStrings.FinishedImport)
else:

View File

@ -25,7 +25,10 @@
###############################################################################
from PyQt4 import QtGui
from openlp.core.lib import translate
from db import Author
from ui import SongStrings
class VerseType(object):
"""
@ -241,6 +244,23 @@ def retrieve_windows_encoding(recommendation=None):
return None
return filter(lambda item: item[1] == choice[0], encodings)[0][0]
def add_author_unknown(manager, song):
"""
Add the default author *Author Unknown* to the song.
``manager``
The song's manager.
``song``
The song object.
"""
name = SongStrings.AuthorUnknown
author = manager.get_object_filtered(Author, Author.display_name == name)
if author is None:
author = Author.populate(
display_name=name, last_name=u'', first_name=u'')
song.authors.append(author)
from xml import OpenLyrics, SongXML
from songstab import SongsTab
from mediaitem import SongMediaItem

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, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard, 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 #
###############################################################################
"""
The XML of `Foilpresenter <http://foilpresenter.de/>`_ songs is of the format::
<?xml version="1.0" encoding="UTF-8"?>
<foilpresenterfolie version="00300.000092">
<id>2004.6.18.18.44.37.0767</id>
<lastchanged>2011.1.21.8.53.5</lastchanged>
<titel>
<titelstring>Above all</titelstring>
</titel>
<sprache>1</sprache>
<ccliid></ccliid>
<tonart></tonart>
<valign>0</valign>
<notiz>Notiz</notiz>
<versionsinfo>1.0</versionsinfo>
<farben>
<cback>0,0,0</cback>
<ctext>255,255,255</ctext>
</farben>
<reihenfolge>
<name>Standard</name>
<strophennummer>0</strophennummer>
</reihenfolge>
<strophen>
<strophe>
<align>0</align>
<font>Verdana</font>
<textsize>14</textsize>
<bold>0</bold>
<italic>0</italic>
<underline>0</underline>
<key>1</key>
<text>Above all powers, above all kings,
above all nature an all created things;
above all wisdom and all the ways of man,
You were here before the world began.</text>
<sortnr>1</sortnr>
</strophe>
</strophen>
<verkn>
<filename>Herr du bist maechtig.foil</filename>
</verkn>
<copyright>
<font>Arial</font>
<textsize>7</textsize>
<anzeigedauer>3</anzeigedauer>
<bold>0</bold>
<italic>1</italic>
<underline>0</underline>
<text>Text und Musik: Lenny LeBlanc/Paul Baloche</text>
</copyright>
<buch>
<bucheintrag>
<name>Feiert Jesus 3</name>
<nummer>10</nummer>
</bucheintrag>
</buch>
<kategorien>
<name>Worship</name>
</kategorien>
</foilpresenterfolie>
"""
import logging
import re
import os
from lxml import etree, objectify
from openlp.core.lib import translate
from openlp.core.ui.wizard import WizardStrings
from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib.songimport import SongImport
from openlp.plugins.songs.lib.db import Author, Book, Song, Topic
from openlp.plugins.songs.lib.xml import SongXML
from openlp.plugins.songs.lib.ui import SongStrings
log = logging.getLogger(__name__)
class FoilPresenterImport(SongImport):
"""
This provides the Foilpresenter import.
"""
def __init__(self, master_manager, **kwargs):
"""
Initialise the import.
"""
log.debug(u'initialise FoilPresenterImport')
SongImport.__init__(self, master_manager, **kwargs)
self.FoilPresenter = FoilPresenter(self.manager)
def do_import(self):
"""
Imports the songs.
"""
self.import_wizard.progressBar.setMaximum(len(self.import_source))
parser = etree.XMLParser(remove_blank_text=True)
for file_path in self.import_source:
if self.stop_import_flag:
return False
self.import_wizard.incrementProgressBar(
WizardStrings.ImportingType % os.path.basename(file_path))
try:
parsed_file = etree.parse(file_path, parser)
xml = unicode(etree.tostring(parsed_file))
if self.FoilPresenter.xml_to_song(xml) is None:
log.debug(u'File could not be imported: %s' % file_path)
except etree.XMLSyntaxError:
log.exception(u'XML syntax error in file %s' % file_path)
return True
class FoilPresenter(object):
"""
This class represents the converter for Foilpresenter XML from a song.
As Foilpresenter has a rich set of different features, we cannot support
them all. The following features are supported by the :class:`Foilpresenter`
OpenPL does not support styletype and font attributes like "align, font,
textsize, bold, italic, underline"
*<lastchanged>*
This property is currently not supported.
*<title>*
As OpenLP does only support one title, the first titlestring becomes
title, all other titlestrings will be alternate titles
*<sprache>*
This property is not supported.
*<ccliid>*
The *<ccliid>* property is fully supported.
*<tonart>*
This property is currently not supported.
*<valign>*
This property is not supported.
*<notiz>*
The *<notiz>* property is fully supported.
*<versionsinfo>*
This property is not supported.
*<farben>*
This property is not supported.
*<reihenfolge>* = verseOrder
OpenLP supports this property.
*<strophen>*
Only the attributes *key* and *text* are supported.
*<verkn>*
This property is not supported.
*<verkn>*
This property is not supported.
*<copyright>*
Only the attribute *text* is supported. => Done
*<buch>* = songbooks
As OpenLP does only support one songbook, we cannot consider more than
one songbook.
*<kategorien>*
This property is not supported.
The tag *<author>* is not support by foilpresenter, mostly the author is
named in the <copyright> tag. We try to extract the authors from the
<copyright> tag.
"""
def __init__(self, manager):
self.manager = manager
def xml_to_song(self, xml):
"""
Create and save a song from Foilpresenter format xml to the database.
``xml``
The XML to parse (unicode).
"""
# No xml get out of here.
if not xml:
return None
song = Song()
if xml[:5] == u'<?xml':
xml = xml[38:]
# Because "text" seems to be an reserverd word, we have to recompile it.
xml = re.compile(u'<text>').sub(u'<text_>', xml)
xml = re.compile(u'</text>').sub(u'</text_>', xml)
song_xml = objectify.fromstring(xml)
foilpresenterfolie = song_xml
self._process_copyright(foilpresenterfolie, song)
self._process_cclinumber(foilpresenterfolie, song)
self._process_titles(foilpresenterfolie, song)
# The verse order is processed with the lyrics!
self._process_lyrics(foilpresenterfolie, song)
self._process_comments(foilpresenterfolie, song)
self._process_authors(foilpresenterfolie, song)
self._process_songbooks(foilpresenterfolie, song)
self._process_topics(foilpresenterfolie, song)
self.manager.save_object(song)
return song.id
def _child(self, element):
"""
This returns the text of an element as unicode string.
``element``
The element.
"""
if element is not None:
return unicode(element)
return u''
def _process_authors(self, foilpresenterfolie, song):
"""
Adds the authors specified in the XML to the song.
``foilpresenterfolie``
The property object (lxml.objectify.ObjectifiedElement).
``song``
The song object.
"""
authors = []
try:
copyright = self._child(foilpresenterfolie.copyright.text_)
except AttributeError:
copyright = None
if copyright:
strings = []
author_temp = []
if copyright.find(u'Copyright') != -1:
temp = copyright.partition(u'Copyright')
copyright = temp[0]
elif copyright.find(u'copyright') != -1:
temp = copyright.partition(u'copyright')
copyright = temp[0]
elif copyright.find(u'©') != -1:
temp = copyright.partition(u'©')
copyright = temp[0]
elif copyright.find(u'(c)') != -1:
temp = copyright.partition(u'(c)')
copyright = temp[0]
elif copyright.find(u'(C)') != -1:
temp = copyright.partition(u'(C)')
copyright = temp[0]
elif copyright.find(u'c)') != -1:
temp = copyright.partition(u'c)')
copyright = temp[0]
elif copyright.find(u'C)') != -1:
temp = copyright.partition(u'C)')
copyright = temp[0]
elif copyright.find(u'C:') != -1:
temp = copyright.partition(u'C:')
copyright = temp[0]
elif copyright.find(u'C,)') != -1:
temp = copyright.partition(u'C,)')
copyright = temp[0]
copyright = re.compile(u'\\n').sub(u' ', copyright)
copyright = re.compile(u'\(.*\)').sub(u'', copyright)
if copyright.find(u'Rechte') != -1:
temp = copyright.partition(u'Rechte')
copyright = temp[0]
markers = [u'Text +u\.?n?d? +Melodie[a-zA-Z0-9\,\. ]*:',
u'Text +u\.?n?d? +Musik', u'T & M', u'Melodie und Satz',
u'Text[a-zA-Z0-9\,\. ]*:', u'Melodie', u'Musik', u'Satz',
u'Weise', u'[dD]eutsch', u'[dD]t[\.\:]', u'Englisch',
u'[oO]riginal', u'Bearbeitung', u'[R|r]efrain']
for marker in markers:
copyright = re.compile(marker).sub(u'<marker>', copyright)
copyright = re.compile(u'(?<=<marker>) *:').sub(u'', copyright)
i = 0
x = 0
while i != 1:
if copyright.find(u'<marker>') != -1:
temp = copyright.partition(u'<marker>')
if (temp[0].strip() != u'') & (x > 0):
strings.append(temp[0])
copyright = temp[2]
x += 1
elif x > 0:
strings.append(copyright)
i = 1
else:
i = 1
for author in strings:
temp = re.split(u',(?=\D{2})|(?<=\D),|\/(?=\D{3,})|(?<=\D);',
author)
for tempx in temp:
author_temp.append(tempx)
for author in author_temp:
regex = u'^[\/,;\-\s]+|[\/,;\-\s]+$|'\
'\s*[0-9]{4}\s*[\-\/]?\s*([0-9]{4})?[\/,;\-\s]*$'
author = re.compile(regex).sub(u'', author)
author = re.compile(
u'[0-9]{1,2}\.\s?J(ahr)?h\.|um\s*$|vor\s*$').sub(u'',
author)
author = re.compile(u'[N|n]ach.*$').sub(u'', author)
author = author.strip()
if re.search(
u'\w+\.?\s+\w{3,}\s+[a|u]nd\s|\w+\.?\s+\w{3,}\s+&\s',
author, re.U) != None:
temp = re.split(u'\s[a|u]nd\s|\s&\s', author)
for tempx in temp:
tempx = tempx.strip()
authors.append(tempx)
elif (len(author) > 2):
authors.append(author)
if not authors:
authors.append(SongStrings.AuthorUnknownUnT)
for display_name in authors:
author = self.manager.get_object_filtered(Author,
Author.display_name == display_name)
if author is None:
# We need to create a new author, as the author does not exist.
author = Author.populate(display_name=display_name,
last_name = display_name.split(u' ')[-1],
first_name = u' '.join(display_name.split(u' ')[:-1]))
self.manager.save_object(author)
song.authors.append(author)
def _process_cclinumber(self, foilpresenterfolie, song):
"""
Adds the CCLI number to the song.
``foilpresenterfolie``
The property object (lxml.objectify.ObjectifiedElement).
``song``
The song object.
"""
try:
song.ccli_number = self._child(foilpresenterfolie.ccliid)
except AttributeError:
song.ccli_number = u''
def _process_comments(self, foilpresenterfolie, song):
"""
Joins the comments specified in the XML and add it to the song.
``foilpresenterfolie``
The property object (lxml.objectify.ObjectifiedElement).
``song``
The song object.
"""
try:
song.comments = self._child(foilpresenterfolie.notiz)
except AttributeError:
song.comments = u''
def _process_copyright(self, foilpresenterfolie, song):
"""
Adds the copyright to the song.
``foilpresenterfolie``
The property object (lxml.objectify.ObjectifiedElement).
``song``
The song object.
"""
try:
song.copyright = self._child(foilpresenterfolie.copyright.text_)
except AttributeError:
song.copyright = u''
def _process_lyrics(self, foilpresenterfolie, song):
"""
Processes the verses and search_lyrics for the song.
``foilpresenterfolie``
The foilpresenterfolie object (lxml.objectify.ObjectifiedElement).
``song``
The song object.
"""
sxml = SongXML()
search_text = u''
temp_verse_order = {}
temp_verse_order_backup = []
temp_verse_sort = []
temp_sortnr_backup = 1
temp_sortnr_liste = []
versenumber = {u'V': 1, u'C': 1, u'B': 1, u'E': 1, u'O': 1, u'I': 1,
u'P': 1}
for strophe in foilpresenterfolie.strophen.strophe:
text = self._child(strophe.text_)
verse_name = self._child(strophe.key)
children = strophe.getchildren()
sortnr = False
for child in children:
if child.tag == u'sortnr':
verse_sortnr = self._child(strophe.sortnr)
sortnr = True
# In older Version there is no sortnr, but we need one
if sortnr == False:
verse_sortnr = unicode(temp_sortnr_backup)
temp_sortnr_backup += 1
# Foilpresenter allows e. g. "Ref" or "1", but we need "C1" or "V1".
temp_sortnr_liste.append(verse_sortnr)
temp_verse_name = re.compile(u'[0-9].*').sub(u'', verse_name)
temp_verse_name = temp_verse_name[:3].lower()
if temp_verse_name == u'ref':
verse_type = u'C'
elif temp_verse_name == u'r':
verse_type = u'C'
elif temp_verse_name == u'':
verse_type = u'V'
elif temp_verse_name == u'v':
verse_type = u'V'
elif temp_verse_name == u'bri':
verse_type = u'B'
elif temp_verse_name == u'cod':
verse_type = u'E'
elif temp_verse_name == u'sch':
verse_type = u'E'
elif temp_verse_name == u'pre':
verse_type = u'P'
elif temp_verse_name == u'int':
verse_type = u'I'
else:
verse_type = u'O'
verse_number = re.compile(u'[a-zA-Z.+-_ ]*').sub(u'', verse_name)
verse_part = re.compile(u'[0-9]*').sub(u'', verse_name[1:])
# Foilpresenter allows e. g. "C", but we need "C1".
if not verse_number:
verse_number = unicode(versenumber[verse_type])
versenumber[verse_type] += 1
else:
# test if foilpresenter have the same versenumber two times with
# different parts raise the verse number
for value in temp_verse_order_backup:
if value == (u''.join((verse_type, verse_number))):
verse_number = unicode(int(verse_number) + 1)
verse_type_index = VerseType.from_tag(verse_type[0])
verse_type = VerseType.Names[verse_type_index]
temp_verse_order[verse_sortnr] = (u''.join((verse_type[0],
verse_number)))
temp_verse_order_backup.append(u''.join((verse_type[0],
verse_number)))
sxml.add_verse_to_lyrics(verse_type, verse_number, text)
search_text = search_text + text
song.search_lyrics = search_text.lower()
song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
# Process verse order
verse_order = []
verse_strophenr = []
for strophennummer in foilpresenterfolie.reihenfolge.strophennummer:
verse_strophenr.append(strophennummer)
# Currently we do not support different "parts"!
if u'0' in temp_verse_order:
for vers in temp_verse_order_backup:
verse_order.append(vers)
else:
for number in verse_strophenr:
numberx = temp_sortnr_liste[int(number)]
verse_order.append(temp_verse_order[unicode(numberx)])
song.verse_order = u' '.join(verse_order)
def _process_songbooks(self, foilpresenterfolie, song):
"""
Adds the song book and song number specified in the XML to the song.
``foilpresenterfolie``
The property object (lxml.objectify.ObjectifiedElement).
``song``
The song object.
"""
song.song_book_id = 0
song.song_number = u''
try:
for bucheintrag in foilpresenterfolie.buch.bucheintrag:
bookname = self._child(bucheintrag.name)
if bookname:
book = self.manager.get_object_filtered(Book,
Book.name == bookname)
if book is None:
# We need to create a book, because it does not exist.
book = Book.populate(name=bookname, publisher=u'')
self.manager.save_object(book)
song.song_book_id = book.id
try:
if self._child(bucheintrag.nummer):
song.song_number = self._child(bucheintrag.nummer)
except AttributeError:
pass
# We only support one song book, so take the first one.
break
except AttributeError:
pass
def _process_titles(self, foilpresenterfolie, song):
"""
Processes the titles specified in the song's XML.
``foilpresenterfolie``
The property object (lxml.objectify.ObjectifiedElement).
``song``
The song object.
"""
for titelstring in foilpresenterfolie.titel.titelstring:
if not song.title:
song.title = self._child(titelstring)
song.search_title = unicode(song.title)
song.alternate_title = u''
else:
song.alternate_title = self._child(titelstring)
song.search_title += u'@' + song.alternate_title
song.search_title = re.sub(r'[\'"`,;:(){}?]+', u'',
unicode(song.search_title)).lower()
def _process_topics(self, foilpresenterfolie, song):
"""
Adds the topics to the song.
``foilpresenterfolie``
The property object (lxml.objectify.ObjectifiedElement).
``song``
The song object.
"""
try:
for name in foilpresenterfolie.kategorien.name:
topictext = self._child(name)
if topictext:
topic = self.manager.get_object_filtered(Topic,
Topic.name == topictext)
if topic is None:
# We need to create a topic, because it does not exist.
topic = Topic.populate(name=topictext)
self.manager.save_object(topic)
song.topics.append(topic)
except AttributeError:
pass
def _dump_xml(self, xml):
"""
Debugging aid to dump XML so that we can see what we have.
"""
return etree.tostring(xml, encoding=u'UTF-8',
xml_declaration=True, pretty_print=True)

View File

@ -35,6 +35,7 @@ from cclifileimport import CCLIFileImport
from ewimport import EasyWorshipSongImport
from songbeamerimport import SongBeamerImport
from songshowplusimport import SongShowPlusImport
from foilpresenterimport import FoilPresenterImport
# Imports that might fail
try:
from olp1import import OpenLP1SongImport
@ -72,7 +73,8 @@ class SongFormat(object):
EasyWorship = 9
SongBeamer = 10
SongShowPlus = 11
#CSV = 12
FoilPresenter = 12
#CSV = 13
@staticmethod
def get_class(format):
@ -106,6 +108,8 @@ class SongFormat(object):
return SongBeamerImport
elif format == SongFormat.SongShowPlus:
return SongShowPlusImport
elif format == SongFormat.FoilPresenter:
return FoilPresenterImport
return None
@staticmethod
@ -125,7 +129,8 @@ class SongFormat(object):
SongFormat.EasiSlides,
SongFormat.EasyWorship,
SongFormat.SongBeamer,
SongFormat.SongShowPlus
SongFormat.SongShowPlus,
SongFormat.FoilPresenter
]
@staticmethod

View File

@ -36,6 +36,7 @@ from sqlalchemy.orm.exc import UnmappedClassError
from openlp.core.lib import translate
from openlp.core.lib.db import BaseModel
from openlp.plugins.songs.lib import add_author_unknown
from openlp.plugins.songs.lib.db import Author, Book, Song, Topic #, MediaFile
from songimport import SongImport
@ -47,30 +48,35 @@ class OldAuthor(BaseModel):
"""
pass
class OldBook(BaseModel):
"""
Book model
"""
pass
class OldMediaFile(BaseModel):
"""
MediaFile model
"""
pass
class OldSong(BaseModel):
"""
Song model
"""
pass
class OldTopic(BaseModel):
"""
Topic model
"""
pass
class OpenLPSongImport(SongImport):
"""
The :class:`OpenLPSongImport` class provides OpenLP with the ability to
@ -170,25 +176,18 @@ class OpenLPSongImport(SongImport):
new_song.comments = song.comments
new_song.theme_name = song.theme_name
new_song.ccli_number = song.ccli_number
if song.authors:
for author in song.authors:
existing_author = self.manager.get_object_filtered(
Author, Author.display_name == author.display_name)
if existing_author:
new_song.authors.append(existing_author)
else:
new_song.authors.append(Author.populate(
first_name=author.first_name,
last_name=author.last_name,
display_name=author.display_name))
else:
au = self.manager.get_object_filtered(Author,
Author.display_name == u'Author Unknown')
if au:
new_song.authors.append(au)
for author in song.authors:
existing_author = self.manager.get_object_filtered(
Author, Author.display_name == author.display_name)
if existing_author:
new_song.authors.append(existing_author)
else:
new_song.authors.append(Author.populate(
display_name=u'Author Unknown'))
first_name=author.first_name,
last_name=author.last_name,
display_name=author.display_name))
if not new_song.authors:
add_author_unknown(self.manager, new_song)
if song.book:
existing_song_book = self.manager.get_object_filtered(
Book, Book.name == song.book.name)

View File

@ -29,7 +29,7 @@ import re
from PyQt4 import QtCore
from openlp.core.lib import Receiver, translate
from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib import add_author_unknown, VerseType
from openlp.plugins.songs.lib.db import Song, Author, Topic, Book, MediaFile
from openlp.plugins.songs.lib.ui import SongStrings
from openlp.plugins.songs.lib.xml import SongXML
@ -270,8 +270,6 @@ class SongImport(QtCore.QObject):
"""
All fields have been set to this song. Write the song to disk.
"""
if not self.authors:
self.authors.append(SongStrings.AuthorUnknownUnT)
log.info(u'committing song %s to database', self.title)
song = Song()
song.title = self.title
@ -315,10 +313,13 @@ class SongImport(QtCore.QObject):
author = self.manager.get_object_filtered(Author,
Author.display_name == authortext)
if not author:
author = Author.populate(display_name = authortext,
author = Author.populate(display_name=authortext,
last_name=authortext.split(u' ')[-1],
first_name=u' '.join(authortext.split(u' ')[:-1]))
song.authors.append(author)
# No author, add the default author.
if not song.authors:
add_author_unknown(self.manager, song)
for filename in self.media_files:
media_file = self.manager.get_object_filtered(MediaFile,
MediaFile.file_name == filename)

View File

@ -36,8 +36,7 @@ class SongStrings(object):
# These strings should need a good reason to be retranslated elsewhere.
Author = translate('OpenLP.Ui', 'Author', 'Singular')
Authors = translate('OpenLP.Ui', 'Authors', 'Plural')
AuthorUnknown = translate('OpenLP.Ui', 'Author Unknown') # Used in the UI.
AuthorUnknownUnT = u'Author Unknown' # Used to populate the database.
AuthorUnknown = u'Author Unknown' # Used to populate the database.
CopyrightSymbol = translate('OpenLP.Ui', '\xa9', 'Copyright symbol.')
SongBook = translate('OpenLP.Ui', 'Song Book', 'Singular')
SongBooks = translate('OpenLP.Ui', 'Song Books', 'Plural')

View File

@ -66,7 +66,7 @@ import re
from lxml import etree, objectify
from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib import add_author_unknown, VerseType
from openlp.plugins.songs.lib.db import Author, Book, Song, Topic
from openlp.plugins.songs.lib.ui import SongStrings
@ -374,8 +374,6 @@ class OpenLyrics(object):
display_name = self._text(author)
if display_name:
authors.append(display_name)
if not authors:
authors.append(SongStrings.AuthorUnknownUnT)
for display_name in authors:
author = self.manager.get_object_filtered(Author,
Author.display_name == display_name)
@ -384,8 +382,8 @@ class OpenLyrics(object):
author = Author.populate(display_name=display_name,
last_name=display_name.split(u' ')[-1],
first_name=u' '.join(display_name.split(u' ')[:-1]))
self.manager.save_object(author)
song.authors.append(author)
if not song.authors:
add_author_unknown(self.manager, song)
def _process_cclinumber(self, properties, song):
"""

View File

@ -32,7 +32,8 @@ from PyQt4 import QtCore, QtGui
from openlp.core.lib import Plugin, StringContent, build_icon, translate
from openlp.core.lib.db import Manager
from openlp.core.lib.ui import UiStrings
from openlp.plugins.songs.lib import SongMediaItem, SongsTab, SongXML
from openlp.plugins.songs.lib import add_author_unknown, SongMediaItem, \
SongsTab, SongXML
from openlp.plugins.songs.lib.db import init_schema, Song
from openlp.plugins.songs.lib.importer import SongFormat
@ -145,6 +146,9 @@ class SongsPlugin(Plugin):
counter = 0
for song in songs:
counter += 1
# The song does not have any author, add one.
if not song.authors:
add_author_unknown(self.manager, song)
if song.title is None:
song.title = u''
if song.alternate_title is None: