openlp-core team mailing list archive
-
openlp-core team
-
Mailing list archive
-
Message #22820
[Merge] lp:~tomasgroth/openlp/ews-import into lp:openlp
Tomas Groth has proposed merging lp:~tomasgroth/openlp/ews-import into lp:openlp.
Requested reviews:
OpenLP Core (openlp-core)
Related bugs:
Bug #1290248 in OpenLP: "Support for importing songs from EWS files"
https://bugs.launchpad.net/openlp/+bug/1290248
For more details, see:
https://code.launchpad.net/~tomasgroth/openlp/ews-import/+merge/212940
Support for importing ews files (EasyWorship Service files).
--
https://code.launchpad.net/~tomasgroth/openlp/ews-import/+merge/212940
Your team OpenLP Core is requested to review the proposed merge of lp:~tomasgroth/openlp/ews-import into lp:openlp.
=== modified file 'openlp/plugins/songs/lib/ewimport.py'
--- openlp/plugins/songs/lib/ewimport.py 2014-03-06 20:56:31 +0000
+++ openlp/plugins/songs/lib/ewimport.py 2014-03-26 19:53:23 +0000
@@ -34,13 +34,13 @@
import os
import struct
import re
+import zlib
from openlp.core.lib import translate
from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib import retrieve_windows_encoding, strip_rtf
from .songimport import SongImport
-RTF_STRIPPING_REGEX = re.compile(r'\{\\tx[^}]*\}')
# regex: at least two newlines, can have spaces between them
SLIDE_BREAK_REGEX = re.compile(r'\n *?\n[\n ]*')
NUMBER_REGEX = re.compile(r'[0-9]+')
@@ -77,7 +77,91 @@
def do_import(self):
"""
- Import the songs
+ Determines the type of file to import and calls the appropiate method
+
+ :return:
+ """
+ if self.import_source.lower().endswith('ews'):
+ self.import_ews()
+ else:
+ self.import_db()
+
+ def import_ews(self):
+ """
+ Import the songs from service file
+ The full spec of the ews files can be found here:
+ https://github.com/meinders/lithium-ews/blob/master/docs/ews%20file%20format.md
+
+ :return:
+ """
+ # Open ews file if it exists
+ if not os.path.isfile(self.import_source):
+ return
+ # Make sure there is room for at least a header and one entry
+ if os.path.getsize(self.import_source) < 892:
+ return
+ # Take a stab at how text is encoded
+ self.encoding = 'cp1252'
+ self.ews_file = open(self.import_source, 'rb')
+ # Get file version
+ type, = struct.unpack('<38s', self.ews_file.read(38))
+ version = type.decode()[-3:]
+ # Set fileposition based on filetype/version
+ file_pos = 0
+ if version == ' 5':
+ file_pos = 56
+ elif version == ' 3':
+ file_pos = 48
+ elif version == '1.6':
+ file_pos = 40
+ else:
+ return
+ entry_count = self.get_i32(file_pos)
+ entry_length = self.get_i16(file_pos+4)
+ file_pos += 6
+ self.import_wizard.progress_bar.setMaximum(entry_count)
+ # Loop over songs
+ for x in range(1, entry_count):
+ # Load entry metadata
+ self.set_defaults()
+ self.title = self.get_string(file_pos, 50)
+ resource = self.get_string(file_pos + 51, 255)
+ authors = self.get_string(file_pos + 307, 50)
+ copyright = self.get_string(file_pos + 358, 100)
+ admin = self.get_string(file_pos + 459, 50)
+ cont_ptr = self.get_i32(file_pos + 800)
+ cont_type = self.get_i32(file_pos + 820)
+ notes = self.get_string(file_pos + 1155, 160)
+ self.ccli_number = self.get_string(file_pos + 1410, 10)
+ # Only handle content type 1 (songs)
+ if cont_type != 1:
+ file_pos += entry_length
+ continue
+ # Load song content
+ content_length = self.get_i32(cont_ptr)
+ deflated_content = self.get_bytes(cont_ptr + 4, content_length - 10)
+ deflated_length = self.get_i32(cont_ptr + 4 + content_length - 6)
+ inflated_content = zlib.decompress(deflated_content, 15, deflated_length)
+ if copyright:
+ self.copyright = copyright
+ if admin:
+ if copyright:
+ self.copyright += ', '
+ self.copyright += translate('SongsPlugin.EasyWorshipSongImport',
+ 'Administered by %s') % admin
+ # Set the SongImport object members.
+ self.set_song_import_object(authors, inflated_content)
+ if self.stop_import_flag:
+ break
+ if not self.finish():
+ self.log_error(self.import_source)
+ # Set file_pos for next entry
+ file_pos += entry_length
+ self.ews_file.close()
+
+ def import_db(self):
+ """
+ Import the songs from the database
:return:
"""
@@ -176,7 +260,6 @@
ccli = self.get_field(fi_ccli)
authors = self.get_field(fi_author)
words = self.get_field(fi_words)
- # Set the SongImport object members.
if copy:
self.copyright = copy.decode()
if admin:
@@ -187,55 +270,11 @@
if ccli:
self.ccli_number = ccli.decode()
if authors:
- # Split up the authors
- author_list = authors.split(b'/')
- if len(author_list) < 2:
- author_list = authors.split(b';')
- if len(author_list) < 2:
- author_list = authors.split(b',')
- for author_name in author_list:
- self.add_author(author_name.decode().strip())
- if words:
- # Format the lyrics
- result = strip_rtf(words.decode(), self.encoding)
- if result is None:
- return
- words, self.encoding = result
- verse_type = VerseType.tags[VerseType.Verse]
- for verse in SLIDE_BREAK_REGEX.split(words):
- verse = verse.strip()
- if not verse:
- continue
- verse_split = verse.split('\n', 1)
- first_line_is_tag = False
- # EW tags: verse, chorus, pre-chorus, bridge, tag,
- # intro, ending, slide
- for tag in VerseType.tags + ['tag', 'slide']:
- tag = tag.lower()
- ew_tag = verse_split[0].strip().lower()
- if ew_tag.startswith(tag):
- verse_type = tag[0]
- if tag == 'tag' or tag == 'slide':
- verse_type = VerseType.tags[VerseType.Other]
- first_line_is_tag = True
- number_found = False
- # check if tag is followed by number and/or note
- if len(ew_tag) > len(tag):
- match = NUMBER_REGEX.search(ew_tag)
- if match:
- number = match.group()
- verse_type += number
- number_found = True
- match = NOTE_REGEX.search(ew_tag)
- if match:
- self.comments += ew_tag + '\n'
- if not number_found:
- verse_type += '1'
- break
- self.add_verse(verse_split[-1].strip() if first_line_is_tag else verse, verse_type)
- if len(self.comments) > 5:
- self.comments += str(translate('SongsPlugin.EasyWorshipSongImport',
- '\n[above are Song Tags with notes imported from EasyWorship]'))
+ authors = authors.decode()
+ else:
+ authors = ''
+ # Set the SongImport object members.
+ self.set_song_import_object(authors, words)
if self.stop_import_flag:
break
if not self.finish():
@@ -243,6 +282,61 @@
db_file.close()
self.memo_file.close()
+ def set_song_import_object(self, authors, words):
+ """
+ Set the SongImport object members.
+ """
+ if authors:
+ # Split up the authors
+ author_list = authors.split('/')
+ if len(author_list) < 2:
+ author_list = authors.split(';')
+ if len(author_list) < 2:
+ author_list = authors.split(',')
+ for author_name in author_list:
+ self.add_author(author_name.strip())
+ if words:
+ # Format the lyrics
+ result = strip_rtf(words.decode(), self.encoding)
+ if result is None:
+ return
+ words, self.encoding = result
+ verse_type = VerseType.tags[VerseType.Verse]
+ for verse in SLIDE_BREAK_REGEX.split(words):
+ verse = verse.strip()
+ if not verse:
+ continue
+ verse_split = verse.split('\n', 1)
+ first_line_is_tag = False
+ # EW tags: verse, chorus, pre-chorus, bridge, tag,
+ # intro, ending, slide
+ for tag in VerseType.tags + ['tag', 'slide']:
+ tag = tag.lower()
+ ew_tag = verse_split[0].strip().lower()
+ if ew_tag.startswith(tag):
+ verse_type = tag[0]
+ if tag == 'tag' or tag == 'slide':
+ verse_type = VerseType.tags[VerseType.Other]
+ first_line_is_tag = True
+ number_found = False
+ # check if tag is followed by number and/or note
+ if len(ew_tag) > len(tag):
+ match = NUMBER_REGEX.search(ew_tag)
+ if match:
+ number = match.group()
+ verse_type += number
+ number_found = True
+ match = NOTE_REGEX.search(ew_tag)
+ if match:
+ self.comments += ew_tag + '\n'
+ if not number_found:
+ verse_type += '1'
+ break
+ self.add_verse(verse_split[-1].strip() if first_line_is_tag else verse, verse_type)
+ if len(self.comments) > 5:
+ self.comments += str(translate('SongsPlugin.EasyWorshipSongImport',
+ '\n[above are Song Tags with notes imported from EasyWorship]'))
+
def find_field(self, field_name):
"""
Find a field in the descriptions
@@ -323,3 +417,38 @@
return self.memo_file.read(blob_size)
else:
return 0
+
+ def get_bytes(self, pos, length):
+ """
+ Get bytes from ews_file
+ """
+ self.ews_file.seek(pos)
+ return self.ews_file.read(length)
+
+ def get_string(self, pos, length):
+ """
+ Get string from ews_file
+ """
+ bytes = self.get_bytes(pos, length)
+ mask = '<' + str(length) + 's'
+ byte_str, = struct.unpack(mask, bytes)
+ return byte_str.decode('unicode-escape').replace('\0', '').strip()
+
+ def get_i16(self, pos):
+ """
+ Get short int from ews_file
+ """
+
+ bytes = self.get_bytes(pos, 2)
+ mask = '<h'
+ number, = struct.unpack(mask, bytes)
+ return number
+
+ def get_i32(self, pos):
+ """
+ Get long int from ews_file
+ """
+ bytes = self.get_bytes(pos, 4)
+ mask = '<i'
+ number, = struct.unpack(mask, bytes)
+ return number
=== modified file 'openlp/plugins/songs/lib/importer.py'
--- openlp/plugins/songs/lib/importer.py 2014-03-17 19:05:55 +0000
+++ openlp/plugins/songs/lib/importer.py 2014-03-26 19:53:23 +0000
@@ -229,7 +229,10 @@
'name': 'EasyWorship',
'prefix': 'ew',
'selectMode': SongFormatSelect.SingleFile,
- 'filter': '%s (*.db)' % translate('SongsPlugin.ImportWizardForm', 'EasyWorship Song Database')
+ 'filter': '%s (*.db);; %s (*.ews)' % (translate('SongsPlugin.ImportWizardForm',
+ 'EasyWorship Song Database'),
+ translate('SongsPlugin.ImportWizardForm',
+ 'EasyWorship Service File'))
},
FoilPresenter: {
'class': FoilPresenterImport,
References