From 1406224a8ff9637ecba0062a60386afd0083e6ae Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Sun, 25 Sep 2016 11:30:00 +0200 Subject: [PATCH] Added support for import of ChordPro --- openlp/plugins/songs/lib/importer.py | 56 ++++--- .../plugins/songs/lib/importers/chordpro.py | 153 ++++++++++++++++++ .../plugins/songs/lib/importers/songimport.py | 17 +- 3 files changed, 200 insertions(+), 26 deletions(-) create mode 100644 openlp/plugins/songs/lib/importers/chordpro.py diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py index 8f7654161..c84d23942 100644 --- a/openlp/plugins/songs/lib/importer.py +++ b/openlp/plugins/songs/lib/importer.py @@ -48,6 +48,7 @@ from .importers.powerpraise import PowerPraiseImport from .importers.presentationmanager import PresentationManagerImport from .importers.lyrix import LyrixImport from .importers.videopsalm import VideoPsalmImport +from .importers.chordpro import ChordProImport log = logging.getLogger(__name__) @@ -155,29 +156,30 @@ class SongFormat(object): OpenLP2 = 1 Generic = 2 CCLI = 3 - DreamBeam = 4 - EasySlides = 5 - EasyWorshipDB = 6 - EasyWorshipService = 7 - FoilPresenter = 8 - Lyrix = 9 - MediaShout = 10 - OpenSong = 11 - OPSPro = 12 - PowerPraise = 13 - PowerSong = 14 - PresentationManager = 15 - ProPresenter = 16 - SongBeamer = 17 - SongPro = 18 - SongShowPlus = 19 - SongsOfFellowship = 20 - SundayPlus = 21 - VideoPsalm = 22 - WordsOfWorship = 23 - WorshipAssistant = 24 - WorshipCenterPro = 25 - ZionWorx = 26 + ChordPro = 4 + DreamBeam = 5 + EasySlides = 6 + EasyWorshipDB = 7 + EasyWorshipService = 8 + FoilPresenter = 9 + Lyrix = 10 + MediaShout = 11 + OpenSong = 12 + OPSPro = 13 + PowerPraise = 14 + PowerSong = 15 + PresentationManager = 16 + ProPresenter = 17 + SongBeamer = 18 + SongPro = 19 + SongShowPlus = 20 + SongsOfFellowship = 21 + SundayPlus = 22 + VideoPsalm = 23 + WordsOfWorship = 24 + WorshipAssistant = 25 + WorshipCenterPro = 26 + ZionWorx = 27 # Set optional attribute defaults __defaults__ = { @@ -224,6 +226,13 @@ class SongFormat(object): 'filter': '{text} (*.usr *.txt *.bin)'.format(text=translate('SongsPlugin.ImportWizardForm', 'CCLI SongSelect Files')) }, + ChordPro: { + 'class': ChordProImport, + 'name': 'ChordPro', + 'prefix': 'chordPro', + 'filter': '{text} (*.cho *.crd *.chordpro *.chopro *.txt)'.format(text=translate('SongsPlugin.ImportWizardForm', + 'ChordPro Files')) + }, DreamBeam: { 'class': DreamBeamImport, 'name': 'DreamBeam', @@ -427,6 +436,7 @@ class SongFormat(object): SongFormat.OpenLP2, SongFormat.Generic, SongFormat.CCLI, + SongFormat.ChordPro, SongFormat.DreamBeam, SongFormat.EasySlides, SongFormat.EasyWorshipDB, diff --git a/openlp/plugins/songs/lib/importers/chordpro.py b/openlp/plugins/songs/lib/importers/chordpro.py new file mode 100644 index 000000000..e3c1a37b9 --- /dev/null +++ b/openlp/plugins/songs/lib/importers/chordpro.py @@ -0,0 +1,153 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2016 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:`chordpro` module provides the functionality for importing +ChordPro files into the current database. +""" + +import os +import re + +from openlp.core.common import translate +from openlp.core.ui.lib.wizard import WizardStrings +from .songimport import SongImport + + +log = logging.getLogger(__name__) + +# This importer is based on the information available on these webpages: +# http://webchord.sourceforge.net/tech.html +# http://www.vromans.org/johan/projects/Chordii/chordpro/ +# http://www.tenbyten.com/software/songsgen/help/HtmlHelp/files_reference.htm +# http://linkesoft.com/songbook/chordproformat.html + +class ChordProImport(SongImport): + """ + The :class:`ChordProImport` class provides OpenLP with the + ability to import ChordPro files. + """ + def do_import(self): + self.import_wizard.progress_bar.setMaximum(len(self.import_source)) + for filename in self.import_source: + if self.stop_import_flag: + return + song_file = open(filename, 'rt') + self.do_import_file(song_file) + song_file.close() + + def do_import_file(self, song_file): + """ + Imports the songs in the given file + :param song_file: The file object to be imported from. + """ + self.set_defaults() + # Loop over the lines of the file + file_content = song_file.read() + current_verse = '' + current_verse_type = 'v' + skip_block = False + for line in file_content.splitlines(): + line = line.rstrip() + # Detect tags + if line.startswith('{'): + tag_name, tag_value = self.parse_tag(line) + # Detect which tag + if tag_name in ['title', 't']: + self.title = tag_value + elif tag_name in ['subtitle', 'su', 'st']: + self.alternate_title = tag_value + elif tag_name in ['comment', 'c', 'comment_italic', 'ci', 'comment_box', 'cb']: + # Detect if the comment is used as a chorus repeat marker + if tag_value.lower().startswith('chorus'): + if current_verse.strip(): + # Add collected verse to the lyrics + self.add_verse(current_verse.strip(), current_verse_type) + current_verse_type = 'v' + current_verse = '' + self.repeat_verse('c1') + else: + self.add_comment(tag_value) + elif tag_name in ['start_of_chorus', 'soc']: + current_verse_type = 'c' + elif tag_name in ['end_of_chorus', 'eoc']: + # Add collected chorus to the lyrics + self.add_verse(current_verse, current_verse_type) + current_verse_type = 'v' + current_verse = '' + elif tag_name in ['start_of_tab', 'sot']: + if current_verse.strip(): + # Add collected verse to the lyrics + self.add_verse(current_verse.strip(), current_verse_type) + current_verse_type = 'v' + current_verse = '' + skip_block = True + elif tag_name in ['end_of_tab', 'eot']: + skip_block = False + elif tag_name in ['new_song', 'ns']: + # A new song starts below this tag + if self.verses and self.title: + if current_verse.strip(): + self.add_verse(current_verse.strip(), current_verse_type) + if not self.finish(): + self.log_error(song_file.name) + self.set_defaults() + current_verse_type = 'v' + current_verse = '' + else: + # Unsupported tag + log.debug('unsupported tag: %s' % line) + elif line.startswith('#'): + # Found a comment line, which is ignored... + continue + elif line == "['|]": + # Found a vertical bar + continue + else: + if skip_block: + continue + elif line == '' and current_verse.strip() and current_verse_type != 'c': + # Add collected verse to the lyrics + self.add_verse(current_verse.strip(), current_verse_type) + current_verse_type = 'v' + current_verse = '' + else: + current_verse += line + '\n' + if current_verse.strip(): + self.add_verse(current_verse.strip(), current_verse_type) + if not self.finish(): + self.log_error(song_file.name) + + def parse_tag(self, line): + """ + :param line: Line with the tag to be parsed + :return: A tuple with tag name and tag value (if any) + """ + # Strip the first '}' + line = line[1:].strip() + colon_idx = line.find(':') + # check if this is a tag without value + if colon_idx < 0: + # strip the final '}' and return the tag name + return line[:-1], None + tag_name = line[:colon_idx] + tag_value = line[colon_idx+1:-1].strip() + return tag_name, tag_value diff --git a/openlp/plugins/songs/lib/importers/songimport.py b/openlp/plugins/songs/lib/importers/songimport.py index c9ef382a7..6482b7232 100644 --- a/openlp/plugins/songs/lib/importers/songimport.py +++ b/openlp/plugins/songs/lib/importers/songimport.py @@ -301,12 +301,23 @@ class SongImport(QtCore.QObject): if verse_def not in self.verse_order_list_generated: self.verse_order_list_generated.append(verse_def) - def repeat_verse(self): + def repeat_verse(self, verse_def=None): """ - Repeat the previous verse in the verse order + Repeat the verse with the given verse_def or default to repeating the previous verse in the verse order + + :param verse_def: verse_def of the verse to be repeated """ if self.verse_order_list_generated: - self.verse_order_list_generated.append(self.verse_order_list_generated[-1]) + if verse_def: + # If the given verse_def is only one char (like 'v' or 'c'), postfix it with '1' + if len(verse_def) == 1: + verse_def += '1' + if verse_def in self.verse_order_list_generated: + self.verse_order_list_generated.append(verse_def) + else: + log.warning('Trying to add unknown verse_def "%s"' % verse_def) + else: + self.verse_order_list_generated.append(self.verse_order_list_generated[-1]) self.verse_order_list_generated_useful = True def check_complete(self):