# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2017 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 #
###############################################################################
import os
import re
import logging
from openlp.plugins.songs.lib import VerseType, retrieve_windows_encoding
from openlp.plugins.songs.lib import strip_rtf
from openlp.plugins.songs.lib.importers.songimport import SongImport
HOTKEY_TO_VERSE_TYPE = {
'1': 'v1',
'2': 'v2',
'3': 'v3',
'4': 'v4',
'5': 'v5',
'6': 'v6',
'7': 'v7',
'8': 'v8',
'9': 'v9',
'C': 'c',
'+': 'b',
'Z': 'o'}
[docs]class SundayPlusImport(SongImport):
"""
Import Sunday Plus songs
The format examples can be found attached to bug report at <http://support.openlp.org/issues/395>
"""
def __init__(self, manager, **kwargs):
"""
Initialise the class.
"""
super(SundayPlusImport, self).__init__(manager, **kwargs)
self.encoding = 'cp1252'
[docs] 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, 'rb')
self.do_import_file(song_file)
song_file.close()
[docs] def do_import_file(self, file):
"""
Process the Sunday Plus file object.
"""
self.set_defaults()
if not self.parse(file.read()):
self.log_error(file.name)
return
if self.title == '':
self.title = self.title_from_filename(file.name)
if not self.finish():
self.log_error(file.name)
[docs] def parse(self, data, cell=False):
"""
Process the records
:param data: The data to be processed
:param cell: ?
:return:
"""
if not cell and (len(data) == 0 or data[0:1] != b'[' or data.strip()[-1:] != b']'):
self.log_error('File is malformed')
return False
i = 1
verse_type = VerseType.tags[VerseType.Verse]
while i < len(data):
# Data is held as #name: value pairs inside groups marked as [].
# Now we are looking for the name.
if data[i:i + 1] == b'#':
name_end = data.find(b':', i + 1)
name = data[i + 1:name_end].decode(self.encoding).upper()
i = name_end + 1
while data[i:i + 1] == b' ':
i += 1
if data[i:i + 1] == b'"':
end = data.find(b'"', i + 1)
value = data[i + 1:end]
elif data[i:i + 1] == b'[':
j = i
inside_quotes = False
while j < len(data):
char = data[j:j + 1]
if char == b'"':
inside_quotes = not inside_quotes
elif not inside_quotes and char == b']':
end = j + 1
break
j += 1
value = data[i:end]
else:
end = data.find(b',', i + 1)
if data.find(b'(', i, end) != -1:
end = data.find(b')', i) + 1
value = data[i:end]
# If we are in the main group.
if not cell:
if name == 'TITLE':
self.title = self.decode(self.unescape(value))
elif name == 'AUTHOR':
author = self.decode(self.unescape(value))
if len(author):
self.add_author(author)
elif name == 'COPYRIGHT':
self.add_copyright(self.decode(self.unescape(value)))
elif name[0:4] == 'CELL':
self.parse(value, cell=name[4:])
# We are in a verse group.
else:
if name == 'MARKER_NAME':
value = self.decode(value).strip()
if len(value):
verse_type = VerseType.tags[VerseType.from_loose_input(value[0])]
if len(value) >= 2 and value[-1] in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']:
verse_type = "{verse}{value}".format(verse=verse_type, value=value[-1])
elif name == 'HOTKEY':
value = self.decode(value).strip()
# HOTKEY always appears after MARKER_NAME, so it
# effectively overrides MARKER_NAME, if present.
if len(value) and value in list(HOTKEY_TO_VERSE_TYPE.keys()):
verse_type = HOTKEY_TO_VERSE_TYPE[value]
if name == 'RTF':
value = self.unescape(value)
value = self.decode(value)
result = strip_rtf(value, self.encoding)
if result is None:
return False
verse, self.encoding = result
lines = verse.strip().split('\n')
# If any line inside any verse contains CCLI or
# only Public Domain, we treat this as special data:
# we remove that line and add data to specific field.
processed_lines = []
for i in range(len(lines)):
line = lines[i].strip()
if line[:3].lower() == 'ccl':
m = re.search(r'[0-9]+', line)
if m:
self.ccli_number = int(m.group(0))
continue
elif line.lower() == 'public domain':
self.add_copyright('Public Domain')
continue
processed_lines.append(line)
self.add_verse('\n'.join(processed_lines).strip(), verse_type)
if end == -1:
break
i = end + 1
i += 1
return True
[docs] def title_from_filename(self, filename):
"""
Extract the title from the filename
:param filename: File name
:return:
"""
title = os.path.split(filename)[1]
if title.endswith('.ptf'):
title = title[:-4]
# For some strange reason all example files names ended with 1-7.
if title.endswith('1-7'):
title = title[:-3]
return title.replace('_', ' ')
[docs] def decode(self, blob):
while True:
try:
return blob.decode(self.encoding)
except Exception as e:
self.encoding = retrieve_windows_encoding()
[docs] def unescape(self, text):
text = text.replace(b'^^', b'"')
text = text.replace(b'^', b'\'')
return text.strip()