From 6b8178a0b42b5c68230c3127c46c40403284ffde Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Thu, 17 Dec 2015 22:39:52 +0100 Subject: [PATCH] Added support of importing VideoPsalm songbooks. --- openlp/plugins/songs/lib/importer.py | 34 ++--- openlp/plugins/songs/lib/importers/lyrix.py | 7 +- .../plugins/songs/lib/importers/videopsalm.py | 134 ++++++++++++++++++ .../openlp_plugins/songs/test_videopsalm.py | 49 +++++++ .../videopsalmsongs/as-safe-a-stronghold.json | 34 +++++ .../videopsalm-as-safe-a-stronghold.json | 47 ++++++ 6 files changed, 283 insertions(+), 22 deletions(-) create mode 100644 openlp/plugins/songs/lib/importers/videopsalm.py create mode 100644 tests/functional/openlp_plugins/songs/test_videopsalm.py create mode 100644 tests/resources/videopsalmsongs/as-safe-a-stronghold.json create mode 100644 tests/resources/videopsalmsongs/videopsalm-as-safe-a-stronghold.json diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py index 1701b0c50..10c20b225 100644 --- a/openlp/plugins/songs/lib/importer.py +++ b/openlp/plugins/songs/lib/importer.py @@ -47,6 +47,7 @@ from .importers.worshipassistant import WorshipAssistantImport from .importers.powerpraise import PowerPraiseImport from .importers.presentationmanager import PresentationManagerImport from .importers.lyrix import LyrixImport +from .importers.videopsalm import VideoPsalmImport log = logging.getLogger(__name__) @@ -247,6 +248,13 @@ class SongFormat(object): 'prefix': 'foilPresenter', 'filter': '%s (*.foil)' % translate('SongsPlugin.ImportWizardForm', 'Foilpresenter Song Files') }, + Lyrix: { + 'class': LyrixImport, + 'name': 'Lyrix', + 'prefix': 'lyrix', + 'filter': '%s (*.txt)' % translate('SongsPlugin.ImportWizardForm', 'Lyrix Files'), + 'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'Lyrix (exported txt-files)') + }, MediaShout: { 'name': 'MediaShout', 'prefix': 'mediaShout', @@ -327,6 +335,16 @@ class SongFormat(object): 'prefix': 'sundayPlus', 'filter': '%s (*.ptf)' % translate('SongsPlugin.ImportWizardForm', 'SundayPlus Song Files') }, + VideoPsalm: { + 'class': VideoPsalmImport, + 'name': 'VideoPsalm', + 'prefix': 'videopsalm', + 'selectMode': SongFormatSelect.SingleFile, + 'filter': '%s (*.json)' % translate('SongsPlugin.ImportWizardForm', 'VideoPsalm Files'), + 'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'VideoPsalm'), + 'descriptionText': translate('SongsPlugin.ImportWizardForm','The VideoPsalm songbooks are normally located ' + 'in %s') % 'C:\\Users\\Public\\Documents\\VideoPsalm\\SongBooks\\' + }, WordsOfWorship: { 'class': WordsOfWorshipImport, 'name': 'Words of Worship', @@ -364,22 +382,6 @@ class SongFormat(object): 'First convert your ZionWorx database to a CSV text file, as ' 'explained in the User Manual.') - }, - Lyrix: { - 'class': LyrixImport, - 'name': 'Lyrix', - 'prefix': 'lyrix', - 'filter': '%s (*.txt)' % translate('SongsPlugin.ImportWizardForm', 'Lyrix Files'), - 'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'Exported Lyrix files') - }, - VideoPsalm: { - #'class': VideoPsalmImport, - 'name': 'VideoPsalm', - 'prefix': 'videopsalm', - 'filter': '%s (*.json)' % translate('SongsPlugin.ImportWizardForm', 'VideoPsalm Files'), - 'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'Exported VideoPsalm files'), - 'descriptionText': translate('SongsPlugin.ImportWizardForm','The VideoPsalm songbooks are normally located ' - 'in %s') % 'C:\\Users\\Public\\Documents\\VideoPsalm\\SongBooks\\' } } diff --git a/openlp/plugins/songs/lib/importers/lyrix.py b/openlp/plugins/songs/lib/importers/lyrix.py index 984eb5f6d..f6e5bb3a9 100644 --- a/openlp/plugins/songs/lib/importers/lyrix.py +++ b/openlp/plugins/songs/lib/importers/lyrix.py @@ -26,13 +26,8 @@ exproted from Lyrix.""" import logging import re -from lxml import objectify -from lxml.etree import Error, LxmlError - from openlp.core.common import translate -from openlp.plugins.songs.lib import VerseType from openlp.plugins.songs.lib.importers.songimport import SongImport -from openlp.plugins.songs.lib.ui import SongStrings log = logging.getLogger(__name__) @@ -64,7 +59,7 @@ class LyrixImport(SongImport): def do_import_file(self, file): """ - Process the OpenSong file - pass in a file-like object, not a file path. + Process the Lyrix file - pass in a file-like object, not a file path. """ self.set_defaults() # Setup variables diff --git a/openlp/plugins/songs/lib/importers/videopsalm.py b/openlp/plugins/songs/lib/importers/videopsalm.py new file mode 100644 index 000000000..134649997 --- /dev/null +++ b/openlp/plugins/songs/lib/importers/videopsalm.py @@ -0,0 +1,134 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2015 OpenLP Developers # +# --------------------------------------------------------------------------- # +# 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:`lyrix` module provides the functionality for importing songs which are +exproted from Lyrix.""" + +import logging +import json +import os + +from openlp.core.common import translate +from openlp.plugins.songs.lib import VerseType +from openlp.plugins.songs.lib.importers.songimport import SongImport +from openlp.plugins.songs.lib.ui import SongStrings + +log = logging.getLogger(__name__) + + +class VideoPsalmImport(SongImport): + """ + Import songs exported from Lyrix + """ + + def __init__(self, manager, **kwargs): + """ + Initialise the class. + """ + super(VideoPsalmImport, self).__init__(manager, **kwargs) + + def do_import(self): + """ + Process the VideoPsalm file - pass in a file-like object, not a file path. + """ + self.set_defaults() + # Open SongBook file + song_file = open(self.import_source, 'rt', encoding='utf-8-sig') + try: + file_content = song_file.read() + processed_content = '' + inside_quotes = False + # The VideoPsalm format is not valid json, it uses illegal line breaks and unquoted keys, this must be fixed. + file_content_it = iter(file_content) + for c in file_content_it: + if c == '"': + inside_quotes = not inside_quotes + # Detect invalid linebreak + if c == '\n': + if inside_quotes: + processed_content += '\\n' + # Put keys in quotes + elif c.isalnum() and not inside_quotes: + processed_content += '"' + c + c = next(file_content_it) + while c.isalnum(): + processed_content += c + c = next(file_content_it) + processed_content += '"' + c + else: + processed_content += c + songbook = json.loads(processed_content.strip()) + # Get song array + songs = songbook['Songs'] + self.import_wizard.progress_bar.setMaximum(len(songs)) + songbook_name = songbook['Text'] + media_folder = os.path.normpath(os.path.join(os.path.dirname(song_file.name), '..', 'Audio')) + for song in songs: + #song['Composer'] + try: + self.title = song['Text'] + except KeyError: + pass + try: + self.add_author(song['Author']) + except KeyError: + pass + try: + self.add_copyright(song['Copyright'].replace('\n', ' ').strip()) + except KeyError: + pass + try: + self.ccli_number = song['CCLI'] + except KeyError: + pass + try: + self.song_book_name = songbook_name + except KeyError: + pass + try: + self.topics = song['Theme'].splitlines() + except KeyError: + pass + #try: + # self.add_media_file(os.path.join(media_folder, song['AudioFile'])) + #except KeyError: + # pass + try: + self.add_comment(song['Memo1']) + except KeyError: + pass + try: + self.add_comment(song['Memo2']) + except KeyError: + pass + try: + self.add_comment(song['Memo3']) + except KeyError: + pass + for verse in song['Verses']: + self.add_verse(verse['Text'], 'v') + if not self.finish(): + self.log_error('Could not import %s' % self.title) + except Exception as e: + self.log_error(translate('SongsPlugin.VideoPsalmImport', 'File %s' % file.name), + translate('SongsPlugin.VideoPsalmImport', 'Error: %s') % e) + song_file.close() \ No newline at end of file diff --git a/tests/functional/openlp_plugins/songs/test_videopsalm.py b/tests/functional/openlp_plugins/songs/test_videopsalm.py new file mode 100644 index 000000000..5f2cd9fde --- /dev/null +++ b/tests/functional/openlp_plugins/songs/test_videopsalm.py @@ -0,0 +1,49 @@ +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2015 OpenLP Developers # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +This module contains tests for the OpenSong song importer. +""" + +import os +from unittest import TestCase + +from tests.helpers.songfileimport import SongImportTestHelper +from openlp.plugins.songs.lib.importers.opensong import OpenSongImport +from openlp.core.common import Registry +from tests.functional import patch, MagicMock + +TEST_PATH = os.path.abspath( + os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'videopsalmsongs')) + + +class TestVideoPsalmFileImport(SongImportTestHelper): + + def __init__(self, *args, **kwargs): + self.importer_class_name = 'VideoPsalmImport' + self.importer_module_name = 'videopsalm' + super(TestVideoPsalmFileImport, self).__init__(*args, **kwargs) + + def test_song_import(self): + """ + Test that loading an VideoPsalm file works correctly on various files + """ + self.file_import(os.path.join(TEST_PATH, 'videopsalm-as-safe-a-stronghold.json'), + self.load_external_result_data(os.path.join(TEST_PATH, 'as-safe-a-stronghold.json'))) diff --git a/tests/resources/videopsalmsongs/as-safe-a-stronghold.json b/tests/resources/videopsalmsongs/as-safe-a-stronghold.json new file mode 100644 index 000000000..a48ff1f9d --- /dev/null +++ b/tests/resources/videopsalmsongs/as-safe-a-stronghold.json @@ -0,0 +1,34 @@ +{ + "authors": [ + "Martin Luther" + ], + "ccli_number": "12345", + "comments": "This is\nthe first comment\nThis is\nthe second comment\nThis is\nthe third comment\n", + "copyright": "Public Domain", + "song_book_name": "SongBook1", + "song_number": 0, + "title": "A Safe Stronghold Our God is Still", + "topics": [ + "tema1", + "tema2" + ], + "verse_order_list": [], + "verses": [ + [ + "As safe a stronghold our God is still,\nA trusty shield and weapon;\nHe’ll help us clear from all the ill\nThat hath us now o’ertaken.\nThe ancient prince of hell\nHath risen with purpose fell;\nStrong mail of craft and power\nHe weareth in this hour;\nOn earth is not His fellow.", + "v" + ], + [ + "With force of arms we nothing can,\nFull soon were we down-ridden;\nBut for us fights the proper Man,\nWhom God Himself hath bidden.\nAsk ye: Who is this same?\nChrist Jesus is His name,\nThe Lord Sabaoth’s Son;\nHe, and no other one,\nShall conquer in the battle.", + "v" + ], + [ + "And were this world all devils o’er,\nAnd watching to devour us,\nWe lay it not to heart so sore;\nNot they can overpower us.\nAnd let the prince of ill\nLook grim as e’er he will,\nHe harms us not a whit;\nFor why? his doom is writ;\nA word shall quickly slay him.", + "v" + ], + [ + "God’s word, for all their craft and force,\nOne moment will not linger,\nBut, spite of hell, shall have its course;\n’Tis written by His finger.\nAnd though they take our life,\nGoods, honour, children, wife,\nYet is their profit small:\nThese things shall vanish all;\nThe city of God remaineth.", + "v" + ] + ] +} diff --git a/tests/resources/videopsalmsongs/videopsalm-as-safe-a-stronghold.json b/tests/resources/videopsalmsongs/videopsalm-as-safe-a-stronghold.json new file mode 100644 index 000000000..d3e9296c7 --- /dev/null +++ b/tests/resources/videopsalmsongs/videopsalm-as-safe-a-stronghold.json @@ -0,0 +1,47 @@ +{Abbreviation:"SB1",Copyright:"Public domain",Songs:[{ID:3,Composer:"Unknown",Author:"Martin Luther",Copyright:"Public +Domain",Theme:"tema1 +tema2",CCLI:"12345",Alias:"A safe stronghold",Memo1:"This is +the first comment +",Memo2:"This is +the second comment +",Memo3:"This is +the third comment +",Reference:"reference",Guid:"jtCkrJdPIUOmECjaQylg/g",Verses:[{ +Text:"As safe a stronghold our God is still, +A trusty shield and weapon; +He’ll help us clear from all the ill +That hath us now o’ertaken. +The ancient prince of hell +Hath risen with purpose fell; +Strong mail of craft and power +He weareth in this hour; +On earth is not His fellow."},{ID:2, +Text:"With force of arms we nothing can, +Full soon were we down-ridden; +But for us fights the proper Man, +Whom God Himself hath bidden. +Ask ye: Who is this same? +Christ Jesus is His name, +The Lord Sabaoth’s Son; +He, and no other one, +Shall conquer in the battle."},{ID:3, +Text:"And were this world all devils o’er, +And watching to devour us, +We lay it not to heart so sore; +Not they can overpower us. +And let the prince of ill +Look grim as e’er he will, +He harms us not a whit; +For why? his doom is writ; +A word shall quickly slay him."},{ID:4, +Text:"God’s word, for all their craft and force, +One moment will not linger, +But, spite of hell, shall have its course; +’Tis written by His finger. +And though they take our life, +Goods, honour, children, wife, +Yet is their profit small: +These things shall vanish all; +The city of God remaineth."}],AudioFile:"282.mp3",IsAudioFileEnabled:1, +Text:"A Safe Stronghold Our God is Still"}],Guid:"khiHU2blX0Kb41dGdbDLhA",VersionDate:"20121012000000", +Text:"SongBook1"}