diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index 8cd8c70fa..e641ccba7 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -140,6 +140,12 @@ class SongImportForm(OpenLPWizard): QtCore.QObject.connect(self.songBeamerRemoveButton, QtCore.SIGNAL(u'clicked()'), self.onSongBeamerRemoveButtonClicked) + QtCore.QObject.connect(self.songShowPlusAddButton, + QtCore.SIGNAL(u'clicked()'), + self.onSongShowPlusAddButtonClicked) + QtCore.QObject.connect(self.songShowPlusRemoveButton, + QtCore.SIGNAL(u'clicked()'), + self.onSongShowPlusRemoveButtonClicked) def addCustomPages(self): """ @@ -188,6 +194,8 @@ class SongImportForm(OpenLPWizard): self.addFileSelectItem(u'ew', single_select=True) # Words of Worship self.addFileSelectItem(u'songBeamer') + # Song Show Plus + self.addFileSelectItem(u'songShowPlus') # Commented out for future use. # self.addFileSelectItem(u'csv', u'CSV', single_select=True) self.sourceLayout.addLayout(self.formatStack) @@ -237,6 +245,8 @@ class SongImportForm(OpenLPWizard): translate('SongsPlugin.ImportWizardForm', 'EasyWorship')) self.formatComboBox.setItemText(10, translate('SongsPlugin.ImportWizardForm', 'SongBeamer')) + self.formatComboBox.setItemText(11, + translate('SongsPlugin.ImportWizardForm', 'SongShow Plus')) # self.formatComboBox.setItemText(11, # translate('SongsPlugin.ImportWizardForm', 'CSV')) self.openLP2FilenameLabel.setText( @@ -301,6 +311,10 @@ class SongImportForm(OpenLPWizard): translate('SongsPlugin.ImportWizardForm', 'Add Files...')) self.songBeamerRemoveButton.setText( translate('SongsPlugin.ImportWizardForm', 'Remove File(s)')) + self.songShowPlusAddButton.setText( + translate('SongsPlugin.ImportWizardForm', 'Add Files...')) + self.songShowPlusRemoveButton.setText( + translate('SongsPlugin.ImportWizardForm', 'Remove File(s)')) # self.csvFilenameLabel.setText( # translate('SongsPlugin.ImportWizardForm', 'Filename:')) # self.csvBrowseButton.setText( @@ -438,6 +452,16 @@ class SongImportForm(OpenLPWizard): 'file to import from.')) self.songBeamerAddButton.setFocus() return False + elif source_format == SongFormat.SongShowPlus: + if self.songShowPlusFileListWidget.count() == 0: + critical_error_message_box( + translate('SongsPlugin.ImportWizardForm', + 'No SongShow Plus Files Selected'), + translate('SongsPlugin.ImportWizardForm', + 'You need to add at least one SongShow Plus ' + 'file to import from.')) + self.wordsOfWorshipAddButton.setFocus() + return False return True elif self.currentPage() == self.progressPage: return True @@ -671,6 +695,24 @@ class SongImportForm(OpenLPWizard): Remove selected SongBeamer files from the import list """ self.removeSelectedItems(self.songBeamerFileListWidget) + + def onSongShowPlusAddButtonClicked(self): + """ + Get SongShow Plus song database files + """ + self.getFiles( + translate('SongsPlugin.ImportWizardForm', + 'Select SongShow Plus Files'), + self.songShowPlusFileListWidget, u'%s (*.sbsong)' + % translate('SongsPlugin.ImportWizardForm', + 'SongShow Plus Song Files') + ) + + def onSongShowPlusRemoveButtonClicked(self): + """ + Remove selected SongShow Plus files from the import list + """ + self.removeSelectedItems(self.songShowPlusFileListWidget) def registerFields(self): """ @@ -697,6 +739,7 @@ class SongImportForm(OpenLPWizard): self.easiSlidesFilenameEdit.setText(u'') self.ewFilenameEdit.setText(u'') self.songBeamerFileListWidget.clear() + self.songShowPlusFileListWidget.clear() #self.csvFilenameEdit.setText(u'') def preWizard(self): @@ -773,6 +816,12 @@ class SongImportForm(OpenLPWizard): importer = self.plugin.importSongs(SongFormat.SongBeamer, filenames=self.getListOfFiles(self.songBeamerFileListWidget) ) + elif source_format == SongFormat.SongShowPlus: + # Import ShongShow Plus songs + importer = self.plugin.importSongs(SongFormat.SongShowPlus, + filenames=self.getListOfFiles( + self.songShowPlusFileListWidget) + ) if importer.do_import(): self.progressLabel.setText( translate('SongsPlugin.SongImportForm', 'Finished import.')) diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py index 6f566ff4f..cbe5c6922 100644 --- a/openlp/plugins/songs/lib/importer.py +++ b/openlp/plugins/songs/lib/importer.py @@ -34,6 +34,7 @@ from wowimport import WowImport from cclifileimport import CCLIFileImport from ewimport import EasyWorshipSongImport from songbeamerimport import SongBeamerImport +from songshowplusimport import SongShowPlusImport # Imports that might fail try: from olp1import import OpenLP1SongImport @@ -71,6 +72,7 @@ class SongFormat(object): EasiSlides = 8 EasyWorship = 9 SongBeamer = 10 + SongShowPlus = 11 @staticmethod def get_class(format): @@ -102,6 +104,8 @@ class SongFormat(object): return EasyWorshipSongImport elif format == SongFormat.SongBeamer: return SongBeamerImport + elif format == SongFormat.SongShowPlus: + return SongShowPlusImport return None @staticmethod @@ -120,7 +124,8 @@ class SongFormat(object): SongFormat.Generic, SongFormat.EasiSlides, SongFormat.EasyWorship, - SongFormat.SongBeamer + SongFormat.SongBeamer, + SongFormat.SongShowPlus ] @staticmethod diff --git a/openlp/plugins/songs/lib/songshowplusimport.py b/openlp/plugins/songs/lib/songshowplusimport.py new file mode 100644 index 000000000..78c2e838d --- /dev/null +++ b/openlp/plugins/songs/lib/songshowplusimport.py @@ -0,0 +1,212 @@ +# -*- 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 :mod:`wowimport` module provides the functionality for importing Words of +Worship songs into the OpenLP database. +""" +import os +import logging +import struct + +from openlp.plugins.songs.lib.songimport import SongImport + +TITLE = 1 +AUTHOR = 2 +COPYRIGHT = 3 +CCLI_NO = 5 +VERSE = 12 +CHORUS = 20 +TOPIC = 29 +COMMENTS = 30 +VERSE_ORDER = 31 +SONG_BOOK = 35 +SONG_NUMBER = 36 +CUSTOM_VERSE = 37 + +log = logging.getLogger(__name__) + +class SongShowPlusImport(SongImport): + """ + The :class:`SongShowPlusImport` class provides the ability to import song + files from SongShow Plus. + + **SongShow Plus Song File Format:** + + The SongShow Plus song file format is as follows: + + * Each piece of data in the song file has some information that precedes + it. + * The general format of this data is as follows: + 4 Bytes, forming a 32 bit number, a key if you will, this describes what + the data is (see blockKey below) + 4 Bytes, forming a 32 bit number, which is the number of bytes until the + next block starts + 1 Byte, which tells how namy bytes follows + 1 or 4 Bytes, describes how long the string is, if its 1 byte, the string + is less than 255 + The next bytes are the actuall data. + The next block of data follows on. + + This description does differ for verses. Which includes extra bytes + stating the verse type or number. In some cases a "custom" verse is used, + in that case, this block will in include 2 strings, with the associated + string length descriptors. The first string is the name of the verse, the + second is the verse content. + + The file is ended with four null bytes. + + Valid extensions for a SongShow Plus song file are: + + * .sbsong + """ + otherList = {} + otherCount = 0 + + def __init__(self, master_manager, **kwargs): + """ + Initialise the import. + + ``master_manager`` + The song manager for the running OpenLP installation. + """ + SongImport.__init__(self, master_manager) + if kwargs.has_key(u'filename'): + self.import_source = kwargs[u'filename'] + if kwargs.has_key(u'filenames'): + self.import_source = kwargs[u'filenames'] + log.debug(self.import_source) + + def do_import(self): + """ + Receive a single file or a list of files to import. + """ + if isinstance(self.import_source, list): + self.import_wizard.progressBar.setMaximum(len(self.import_source)) + for file in self.import_source: + author = u'' + copyright = u'' + self.sspVerseOrderList = [] + otherCount = 0 + otherList = {} + file_name = os.path.split(file)[1] + self.import_wizard.incrementProgressBar( + u'Importing %s' % (file_name), 0) + songData = open(file, 'rb') + while (1): + blockKey, = struct.unpack("I",songData.read(4)) + # The file ends with 4 NUL's + if blockKey == 0: + break + nextBlockStarts, = struct.unpack("I",songData.read(4)) + if blockKey == VERSE or blockKey == CHORUS: + null, verseNo, = struct.unpack("BB",songData.read(2)) + elif blockKey == CUSTOM_VERSE: + null, verseNameLength, = struct.unpack("BB", + songData.read(2)) + verseName = songData.read(verseNameLength) + lengthDescriptorSize, = struct.unpack("B",songData.read(1)) + # Detect if/how long the length descriptor is + if lengthDescriptorSize == 12: + lengthDescriptor, = struct.unpack("I",songData.read(4)) + elif lengthDescriptorSize == 2: + lengthDescriptor = 1 + elif lengthDescriptorSize == 9: + lengthDescriptor = 0 + else: + lengthDescriptor, = struct.unpack("B",songData.read(1)) + data = songData.read(lengthDescriptor) + + if blockKey == TITLE: + self.title = unicode(data, u'cp1252') + elif blockKey == AUTHOR: + authors = data.split(" / ") + for author in authors: + if author.find(",") !=-1: + authorParts = author.split(", ") + author = authorParts[1] + " " + authorParts[0] + self.parse_author(unicode(author, u'cp1252')) + elif blockKey == COPYRIGHT: + self.add_copyright(unicode(data, u'cp1252')) + elif blockKey == CCLI_NO: + self.ccli_number = int(data) + elif blockKey == VERSE: + self.add_verse(unicode(data, u'cp1252'), + "V%s" % verseNo) + elif blockKey == CHORUS: + self.add_verse(unicode(data, u'cp1252'), + "C%s" % verseNo) + elif blockKey == TOPIC: + self.topics.append(unicode(data, u'cp1252')) + elif blockKey == COMMENTS: + self.comments = unicode(data, u'cp1252') + elif blockKey == VERSE_ORDER: + verseTag = self.toOpenLPVerseTag(data) + self.sspVerseOrderList.append(unicode(verseTag, + u'cp1252')) + elif blockKey == SONG_BOOK: + self.song_book_name = unicode(data, u'cp1252') + elif blockKey == SONG_NUMBER: + self.song_number = ord(data) + elif blockKey == CUSTOM_VERSE: + verseTag = self.toOpenLPVerseTag(verseName) + self.add_verse(unicode(data, u'cp1252'), verseTag) + else: + log.debug("Unrecognised blockKey: %s, data: %s" + %(blockKey, data)) + self.verse_order_list = self.sspVerseOrderList + songData.close() + self.finish() + self.import_wizard.incrementProgressBar( + u'Importing %s' % (file_name)) + return True + + def toOpenLPVerseTag(self, verseName): + if verseName.find(" ")!=-1: + verseParts = verseName.split(" ") + verseType = verseParts[0] + verseNumber = verseParts[1] + else: + verseType = verseName + verseNumber = "1" + verseType = verseType.lower() + if verseType == "verse": + verseTag = "V" + elif verseType == "chorus": + verseTag = "C" + elif verseType == "bridge": + verseTag = "B" + elif verseType == "pre-chorus": + verseTag = "P" + elif verseType == "bridge": + verseTag = "B" + else: + if not self.otherList.has_key(verseName): + self.otherCount = self.otherCount + 1 + self.otherList[verseName] = str(self.otherCount) + verseTag = "O" + verseNumber = self.otherList[verseName] + verseTag = verseTag + verseNumber + return verseTag