2016-09-25 09:30:00 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
2019-04-13 13:00:22 +00:00
|
|
|
##########################################################################
|
|
|
|
# OpenLP - Open Source Lyrics Projection #
|
|
|
|
# ---------------------------------------------------------------------- #
|
2022-02-01 10:10:57 +00:00
|
|
|
# Copyright (c) 2008-2022 OpenLP Developers #
|
2019-04-13 13:00:22 +00:00
|
|
|
# ---------------------------------------------------------------------- #
|
|
|
|
# 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, either version 3 of the License, or #
|
|
|
|
# (at your option) any later version. #
|
|
|
|
# #
|
|
|
|
# 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, see <https://www.gnu.org/licenses/>. #
|
|
|
|
##########################################################################
|
2016-09-25 09:30:00 +00:00
|
|
|
"""
|
|
|
|
The :mod:`chordpro` module provides the functionality for importing
|
|
|
|
ChordPro files into the current database.
|
|
|
|
"""
|
2016-09-25 19:27:46 +00:00
|
|
|
import logging
|
2017-02-26 21:14:49 +00:00
|
|
|
import re
|
|
|
|
|
2020-01-18 21:45:47 +00:00
|
|
|
from openlp.core.common.i18n import translate
|
2017-10-07 07:05:07 +00:00
|
|
|
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
2019-08-26 20:07:33 +00:00
|
|
|
from openlp.plugins.songs.lib.db import AuthorType
|
2016-09-25 09:30:00 +00:00
|
|
|
|
2018-10-02 04:39:42 +00:00
|
|
|
|
2016-09-25 09:30:00 +00:00
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
2016-11-15 21:13:52 +00:00
|
|
|
|
2016-09-25 09:30:00 +00:00
|
|
|
class ChordProImport(SongImport):
|
|
|
|
"""
|
2017-10-07 07:05:07 +00:00
|
|
|
The :class:`ChordProImport` class provides OpenLP with the ability to import ChordPro files.
|
|
|
|
|
2016-09-25 19:27:46 +00:00
|
|
|
This importer is based on the information available on these webpages:
|
2017-10-07 07:05:07 +00:00
|
|
|
|
2019-08-26 20:07:33 +00:00
|
|
|
- https://www.chordpro.org
|
2017-10-07 07:05:07 +00:00
|
|
|
- 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
|
2016-09-25 09:30:00 +00:00
|
|
|
"""
|
|
|
|
def do_import(self):
|
|
|
|
self.import_wizard.progress_bar.setMaximum(len(self.import_source))
|
2017-09-30 20:16:30 +00:00
|
|
|
for file_path in self.import_source:
|
2016-09-25 09:30:00 +00:00
|
|
|
if self.stop_import_flag:
|
|
|
|
return
|
2017-09-30 20:16:30 +00:00
|
|
|
with file_path.open('rt') as song_file:
|
|
|
|
self.do_import_file(song_file)
|
2016-09-25 09:30:00 +00:00
|
|
|
|
|
|
|
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
|
2020-01-18 21:45:47 +00:00
|
|
|
try:
|
|
|
|
file_content = song_file.read()
|
|
|
|
except UnicodeDecodeError:
|
|
|
|
self.log_error(song_file.name, translate('SongsPlugin.CCLIFileImport',
|
|
|
|
'The file contains unreadable characters.'))
|
|
|
|
return
|
2016-09-25 09:30:00 +00:00
|
|
|
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
|
2019-08-26 20:07:33 +00:00
|
|
|
elif tag_name == 'composer':
|
|
|
|
self.parse_author(tag_value, AuthorType.Music)
|
2019-08-29 18:50:43 +00:00
|
|
|
elif tag_name in ['lyricist', 'artist', 'author']: # author is not an official directive
|
2019-08-26 20:07:33 +00:00
|
|
|
self.parse_author(tag_value, AuthorType.Words)
|
|
|
|
elif tag_name == 'meta':
|
|
|
|
meta_tag_name, meta_tag_value = tag_value.split(' ', 1)
|
|
|
|
# Skip, if no value
|
|
|
|
if not meta_tag_value:
|
|
|
|
continue
|
2019-08-29 18:50:43 +00:00
|
|
|
# The meta-tag can contain anything. We check for the ones above and a few more
|
2019-08-26 20:07:33 +00:00
|
|
|
if meta_tag_name in ['title', 't']:
|
|
|
|
self.title = meta_tag_value
|
|
|
|
elif meta_tag_name in ['subtitle', 'su', 'st']:
|
|
|
|
self.alternate_title = meta_tag_value
|
|
|
|
elif meta_tag_name == 'composer':
|
|
|
|
self.parse_author(meta_tag_value, AuthorType.Music)
|
2019-08-29 18:50:43 +00:00
|
|
|
elif meta_tag_name in ['lyricist', 'artist', 'author']:
|
2019-08-26 20:07:33 +00:00
|
|
|
self.parse_author(meta_tag_value, AuthorType.Words)
|
2019-08-28 19:28:27 +00:00
|
|
|
elif meta_tag_name in ['topic', 'topics']:
|
|
|
|
for topic in meta_tag_value.split(','):
|
|
|
|
self.topics.append(topic.strip())
|
|
|
|
elif 'ccli' in meta_tag_name:
|
|
|
|
self.ccli_number = meta_tag_value
|
2016-09-25 09:30:00 +00:00
|
|
|
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
|
2017-02-26 21:14:49 +00:00
|
|
|
# Strip out chords if set up to
|
2020-01-26 17:12:45 +00:00
|
|
|
if not self.settings.value('songs/enable chords') or \
|
|
|
|
self.settings.value('songs/disable chords import'):
|
2017-02-26 21:14:49 +00:00
|
|
|
current_verse = re.sub(r'\[.*?\]', '', current_verse)
|
2016-09-25 19:27:46 +00:00
|
|
|
self.add_verse(current_verse.rstrip(), current_verse_type)
|
2016-09-25 09:30:00 +00:00
|
|
|
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
|
2017-02-26 21:14:49 +00:00
|
|
|
# Strip out chords if set up to
|
2020-01-26 17:12:45 +00:00
|
|
|
if not self.settings.value('songs/enable chords') or \
|
|
|
|
self.settings.value('songs/disable chords import'):
|
2017-02-26 21:14:49 +00:00
|
|
|
current_verse = re.sub(r'\[.*?\]', '', current_verse)
|
2016-09-25 19:27:46 +00:00
|
|
|
self.add_verse(current_verse.rstrip(), current_verse_type)
|
2016-09-25 09:30:00 +00:00
|
|
|
current_verse_type = 'v'
|
|
|
|
current_verse = ''
|
|
|
|
elif tag_name in ['start_of_tab', 'sot']:
|
|
|
|
if current_verse.strip():
|
|
|
|
# Add collected verse to the lyrics
|
2017-02-26 21:14:49 +00:00
|
|
|
# Strip out chords if set up to
|
2020-01-26 17:12:45 +00:00
|
|
|
if not self.settings.value('songs/enable chords') or \
|
|
|
|
self.settings.value('songs/disable chords import'):
|
2017-02-26 21:14:49 +00:00
|
|
|
current_verse = re.sub(r'\[.*?\]', '', current_verse)
|
2016-09-25 19:27:46 +00:00
|
|
|
self.add_verse(current_verse.rstrip(), current_verse_type)
|
2016-09-25 09:30:00 +00:00
|
|
|
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():
|
2017-02-26 21:14:49 +00:00
|
|
|
# Strip out chords if set up to
|
2020-01-26 17:12:45 +00:00
|
|
|
if not self.settings.value('songs/enable chords') or \
|
|
|
|
self.settings.value('songs/disable chords import'):
|
2017-02-26 21:14:49 +00:00
|
|
|
current_verse = re.sub(r'\[.*?\]', '', current_verse)
|
2016-09-25 19:27:46 +00:00
|
|
|
self.add_verse(current_verse.rstrip(), current_verse_type)
|
2016-09-25 09:30:00 +00:00
|
|
|
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 == "['|]":
|
2016-11-15 21:13:52 +00:00
|
|
|
# Found a vertical bar
|
2016-09-25 09:30:00 +00:00
|
|
|
continue
|
|
|
|
else:
|
|
|
|
if skip_block:
|
|
|
|
continue
|
|
|
|
elif line == '' and current_verse.strip() and current_verse_type != 'c':
|
|
|
|
# Add collected verse to the lyrics
|
2017-02-26 21:14:49 +00:00
|
|
|
# Strip out chords if set up to
|
2020-01-26 17:12:45 +00:00
|
|
|
if not self.settings.value('songs/enable chords') or \
|
|
|
|
self.settings.value('songs/disable chords import'):
|
2017-02-26 21:14:49 +00:00
|
|
|
current_verse = re.sub(r'\[.*?\]', '', current_verse)
|
2016-09-25 19:27:46 +00:00
|
|
|
self.add_verse(current_verse.rstrip(), current_verse_type)
|
2016-09-25 09:30:00 +00:00
|
|
|
current_verse_type = 'v'
|
|
|
|
current_verse = ''
|
|
|
|
else:
|
2016-09-25 19:27:46 +00:00
|
|
|
if current_verse.strip() == '':
|
|
|
|
current_verse = line + '\n'
|
|
|
|
else:
|
|
|
|
current_verse += line + '\n'
|
2016-09-25 09:30:00 +00:00
|
|
|
if current_verse.strip():
|
2017-02-26 21:14:49 +00:00
|
|
|
# Strip out chords if set up to
|
2020-01-26 17:12:45 +00:00
|
|
|
if not self.settings.value('songs/enable chords') or self.settings.value(
|
2017-02-26 21:14:49 +00:00
|
|
|
'songs/disable chords import'):
|
|
|
|
current_verse = re.sub(r'\[.*?\]', '', current_verse)
|
2016-09-25 19:27:46 +00:00
|
|
|
self.add_verse(current_verse.rstrip(), current_verse_type)
|
2019-08-26 20:07:33 +00:00
|
|
|
# if no title was in directives, get it from the first line
|
2020-01-18 21:45:47 +00:00
|
|
|
if not self.title and self.verses:
|
2019-08-26 20:07:33 +00:00
|
|
|
(verse_def, verse_text, lang) = self.verses[0]
|
2019-08-28 19:28:27 +00:00
|
|
|
# strip any chords from the title
|
|
|
|
self.title = re.sub(r'\[.*?\]', '', verse_text.split('\n')[0])
|
|
|
|
# strip the last char if it a punctuation
|
|
|
|
self.title = re.sub(r'[^\w\s]$', '', self.title)
|
2016-09-25 09:30:00 +00:00
|
|
|
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]
|
2016-11-15 21:13:52 +00:00
|
|
|
tag_value = line[colon_idx + 1:-1].strip()
|
2016-09-25 09:30:00 +00:00
|
|
|
return tag_name, tag_value
|