forked from openlp/openlp
Head1047
This commit is contained in:
commit
b1d312626a
@ -109,6 +109,9 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
|
||||
QtCore.QObject.connect(self.genericRemoveButton,
|
||||
QtCore.SIGNAL(u'clicked()'),
|
||||
self.onGenericRemoveButtonClicked)
|
||||
QtCore.QObject.connect(self.ewBrowseButton,
|
||||
QtCore.SIGNAL(u'clicked()'),
|
||||
self.onEWBrowseButtonClicked)
|
||||
QtCore.QObject.connect(self.cancelButton,
|
||||
QtCore.SIGNAL(u'clicked(bool)'),
|
||||
self.onCancelButtonClicked)
|
||||
@ -214,6 +217,16 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
|
||||
'presentation file to import from.'))
|
||||
self.genericAddButton.setFocus()
|
||||
return False
|
||||
elif source_format == SongFormat.EasyWorship:
|
||||
if self.ewFilenameEdit.text().isEmpty():
|
||||
QtGui.QMessageBox.critical(self,
|
||||
translate('SongsPlugin.ImportWizardForm',
|
||||
'No EasyWorship Song Database Selected'),
|
||||
translate('SongsPlugin.ImportWizardForm',
|
||||
'You need to select an EasyWorship song database '
|
||||
'file to import from.'))
|
||||
self.ewBrowseButton.setFocus()
|
||||
return False
|
||||
return True
|
||||
elif self.currentId() == 2:
|
||||
# Progress page
|
||||
@ -322,6 +335,13 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
|
||||
def onGenericRemoveButtonClicked(self):
|
||||
self.removeSelectedItems(self.genericFileListWidget)
|
||||
|
||||
def onEWBrowseButtonClicked(self):
|
||||
self.getFileName(
|
||||
translate('SongsPlugin.ImportWizardForm',
|
||||
'Select EasyWorship Database File'),
|
||||
self.ewFilenameEdit
|
||||
)
|
||||
|
||||
def onCancelButtonClicked(self, checked):
|
||||
"""
|
||||
Stop the import on pressing the cancel button.
|
||||
@ -350,6 +370,7 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
|
||||
self.ccliFileListWidget.clear()
|
||||
self.songsOfFellowshipFileListWidget.clear()
|
||||
self.genericFileListWidget.clear()
|
||||
self.ewFilenameEdit.setText(u'')
|
||||
#self.csvFilenameEdit.setText(u'')
|
||||
|
||||
def incrementProgressBar(self, status_text, increment=1):
|
||||
@ -420,6 +441,11 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
|
||||
importer = self.plugin.importSongs(SongFormat.Generic,
|
||||
filenames=self.getListOfFiles(self.genericFileListWidget)
|
||||
)
|
||||
elif source_format == SongFormat.EasyWorship:
|
||||
# Import an OpenLP 2.0 database
|
||||
importer = self.plugin.importSongs(SongFormat.EasyWorship,
|
||||
filename=unicode(self.ewFilenameEdit.text())
|
||||
)
|
||||
success = importer.do_import()
|
||||
if success:
|
||||
# reload songs
|
||||
|
@ -96,6 +96,7 @@ class Ui_SongImportWizard(object):
|
||||
self.formatComboBox.addItem(u'')
|
||||
self.formatComboBox.addItem(u'')
|
||||
self.formatComboBox.addItem(u'')
|
||||
self.formatComboBox.addItem(u'')
|
||||
# self.formatComboBox.addItem(u'')
|
||||
self.formatLayout.addWidget(self.formatComboBox)
|
||||
self.formatSpacer = QtGui.QSpacerItem(40, 20,
|
||||
@ -413,6 +414,30 @@ class Ui_SongImportWizard(object):
|
||||
self.genericImportLayout.addLayout(self.genericButtonLayout)
|
||||
self.genericLayout.addWidget(self.genericImportWidget)
|
||||
self.formatStackedWidget.addWidget(self.genericPage)
|
||||
# EasyWorship
|
||||
self.ewPage = QtGui.QWidget()
|
||||
self.ewPage.setObjectName(u'ewPage')
|
||||
self.ewLayout = QtGui.QFormLayout(self.ewPage)
|
||||
self.ewLayout.setMargin(0)
|
||||
self.ewLayout.setSpacing(8)
|
||||
self.ewLayout.setObjectName(u'ewLayout')
|
||||
self.ewFilenameLabel = QtGui.QLabel(self.ewPage)
|
||||
self.ewFilenameLabel.setObjectName(u'ewFilenameLabel')
|
||||
self.ewLayout.setWidget(0, QtGui.QFormLayout.LabelRole,
|
||||
self.ewFilenameLabel)
|
||||
self.ewFileLayout = QtGui.QHBoxLayout()
|
||||
self.ewFileLayout.setSpacing(8)
|
||||
self.ewFileLayout.setObjectName(u'ewFileLayout')
|
||||
self.ewFilenameEdit = QtGui.QLineEdit(self.ewPage)
|
||||
self.ewFilenameEdit.setObjectName(u'ewFilenameEdit')
|
||||
self.ewFileLayout.addWidget(self.ewFilenameEdit)
|
||||
self.ewBrowseButton = QtGui.QToolButton(self.ewPage)
|
||||
self.ewBrowseButton.setIcon(openIcon)
|
||||
self.ewBrowseButton.setObjectName(u'ewBrowseButton')
|
||||
self.ewFileLayout.addWidget(self.ewBrowseButton)
|
||||
self.ewLayout.setLayout(0, QtGui.QFormLayout.FieldRole,
|
||||
self.ewFileLayout)
|
||||
self.formatStackedWidget.addWidget(self.ewPage)
|
||||
# Commented out for future use.
|
||||
# self.csvPage = QtGui.QWidget()
|
||||
# self.csvPage.setObjectName(u'CSVPage')
|
||||
@ -497,7 +522,9 @@ class Ui_SongImportWizard(object):
|
||||
self.formatComboBox.setItemText(7,
|
||||
translate('SongsPlugin.ImportWizardForm',
|
||||
'Generic Document/Presentation'))
|
||||
# self.formatComboBox.setItemText(8,
|
||||
self.formatComboBox.setItemText(8,
|
||||
translate('SongsPlugin.ImportWizardForm', 'EasyWorship'))
|
||||
# self.formatComboBox.setItemText(9,
|
||||
# translate('SongsPlugin.ImportWizardForm', 'CSV'))
|
||||
self.openLP2FilenameLabel.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'Filename:'))
|
||||
@ -549,6 +576,10 @@ class Ui_SongImportWizard(object):
|
||||
translate('SongsPlugin.ImportWizardForm', 'The generic document/'
|
||||
'presentation importer has been disabled because OpenLP cannot '
|
||||
'find OpenOffice.org on your computer.'))
|
||||
self.ewFilenameLabel.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'Filename:'))
|
||||
self.ewBrowseButton.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'Browse...'))
|
||||
# self.csvFilenameLabel.setText(
|
||||
# translate('SongsPlugin.ImportWizardForm', 'Filename:'))
|
||||
# self.csvBrowseButton.setText(
|
||||
|
255
openlp/plugins/songs/lib/ewimport.py
Normal file
255
openlp/plugins/songs/lib/ewimport.py
Normal file
@ -0,0 +1,255 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||
|
||||
"""
|
||||
The :mod:`ewimport` module provides the functionality for importing
|
||||
EasyWorship song databases into the current installation database.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import struct
|
||||
|
||||
from songimport import SongImport
|
||||
|
||||
def strip_rtf(blob):
|
||||
depth = 0
|
||||
control = False
|
||||
clear_text = []
|
||||
control_word = []
|
||||
for c in blob:
|
||||
if control:
|
||||
# for delimiters, set control to False
|
||||
if c == '{':
|
||||
if len(control_word) > 0:
|
||||
depth += 1
|
||||
control = False
|
||||
elif c == '}':
|
||||
if len(control_word) > 0:
|
||||
depth -= 1
|
||||
control = False
|
||||
elif c == '\\':
|
||||
new_control = (len(control_word) > 0)
|
||||
control = False
|
||||
elif c.isspace():
|
||||
control = False
|
||||
else:
|
||||
control_word.append(c)
|
||||
if len(control_word) == 3 and control_word[0] == '\'':
|
||||
control = False
|
||||
if not control:
|
||||
if len(control_word) == 0:
|
||||
if c == '{' or c == '}' or c == '\\':
|
||||
clear_text.append(c)
|
||||
else:
|
||||
control_str = ''.join(control_word)
|
||||
if control_str == 'par' or control_str == 'line':
|
||||
clear_text.append(u'\n')
|
||||
elif control_str == 'tab':
|
||||
clear_text.append(u'\n')
|
||||
elif control_str[0] == '\'':
|
||||
# Really should take RTF character set into account but
|
||||
# for now assume ANSI (Windows-1252) and call it good
|
||||
s = chr(int(control_str[1:3], 16))
|
||||
clear_text.append(s.decode(u'windows-1252'))
|
||||
del control_word[:]
|
||||
if c == '\\' and new_control:
|
||||
control = True
|
||||
elif c == '{':
|
||||
depth += 1
|
||||
elif c == '}':
|
||||
depth -= 1
|
||||
elif depth > 2:
|
||||
continue
|
||||
elif c == '\n' or c == '\r':
|
||||
continue
|
||||
elif c == '\\':
|
||||
control = True
|
||||
else:
|
||||
clear_text.append(c)
|
||||
return u''.join(clear_text)
|
||||
|
||||
class FieldDescEntry:
|
||||
def __init__(self, name, type, size):
|
||||
self.name = name
|
||||
self.type = type
|
||||
self.size = size
|
||||
|
||||
class EasyWorshipSongImport(SongImport):
|
||||
"""
|
||||
The :class:`EasyWorshipSongImport` class provides OpenLP with the
|
||||
ability to import EasyWorship song files.
|
||||
"""
|
||||
def __init__(self, manager, **kwargs):
|
||||
self.import_source = kwargs[u'filename']
|
||||
SongImport.__init__(self, manager)
|
||||
|
||||
def do_import(self):
|
||||
# Open the DB and MB files if they exist
|
||||
import_source_mb = self.import_source.replace('.DB', '.MB')
|
||||
if not os.path.isfile(self.import_source):
|
||||
return False
|
||||
if not os.path.isfile(import_source_mb):
|
||||
return False
|
||||
db_size = os.path.getsize(self.import_source)
|
||||
if db_size < 0x800:
|
||||
return False
|
||||
db_file = open(self.import_source, 'rb')
|
||||
self.memo_file = open(import_source_mb, 'rb')
|
||||
# Don't accept files that are clearly not paradox files
|
||||
record_size, header_size, block_size, first_block, num_fields \
|
||||
= struct.unpack('<hhxb8xh17xh', db_file.read(35))
|
||||
if header_size != 0x800 or block_size < 1 or block_size > 4:
|
||||
db_file.close()
|
||||
self.memo_file.close()
|
||||
return False
|
||||
# There does not appear to be a _reliable_ way of getting the number
|
||||
# of songs/records, so let's use file blocks for measuring progress.
|
||||
total_blocks = (db_size - header_size) / (block_size * 1024)
|
||||
self.import_wizard.importProgressBar.setMaximum(total_blocks)
|
||||
# Read the field description information
|
||||
db_file.seek(120)
|
||||
field_info = db_file.read(num_fields * 2)
|
||||
db_file.seek(4 + (num_fields * 4) + 261, os.SEEK_CUR)
|
||||
field_names = db_file.read(header_size - db_file.tell()).split('\0',
|
||||
num_fields)
|
||||
field_names.pop()
|
||||
field_descs = []
|
||||
for i,field_name in enumerate(field_names):
|
||||
field_type, field_size = struct.unpack_from('BB', field_info, i * 2)
|
||||
field_descs.append(FieldDescEntry(field_name, field_type,
|
||||
field_size))
|
||||
self.set_record_struct(field_descs)
|
||||
# Pick out the field description indexes we will need
|
||||
success = True
|
||||
try:
|
||||
fi_title = self.find_field(u'Title')
|
||||
fi_author = self.find_field(u'Author')
|
||||
fi_copy = self.find_field(u'Copyright')
|
||||
fi_admin = self.find_field(u'Administrator')
|
||||
fi_words = self.find_field(u'Words')
|
||||
fi_ccli = self.find_field(u'Song Number')
|
||||
except IndexError:
|
||||
# This is the wrong table
|
||||
success = False
|
||||
# Loop through each block of the file
|
||||
cur_block = first_block
|
||||
while cur_block != 0 and success:
|
||||
db_file.seek(header_size + ((cur_block - 1) * 1024 * block_size))
|
||||
cur_block, rec_count = struct.unpack('<h2xh', db_file.read(6))
|
||||
rec_count = (rec_count + record_size) / record_size
|
||||
# Loop through each record within the current block
|
||||
for i in range(rec_count):
|
||||
if self.stop_import_flag:
|
||||
success = False
|
||||
break
|
||||
raw_record = db_file.read(record_size)
|
||||
self.fields = self.record_struct.unpack(raw_record)
|
||||
self.set_defaults()
|
||||
self.title = self.get_field(fi_title)
|
||||
self.import_wizard.incrementProgressBar(
|
||||
u'Importing "%s"...' % self.title, 0)
|
||||
self.copyright = self.get_field(fi_copy) + \
|
||||
u', Administered by ' + self.get_field(fi_admin)
|
||||
self.ccli_number = self.get_field(fi_ccli)
|
||||
# Format the lyrics
|
||||
if self.stop_import_flag:
|
||||
success = False
|
||||
break
|
||||
words = self.get_field(fi_words)
|
||||
words = strip_rtf(words)
|
||||
for verse in words.split(u'\n\n'):
|
||||
self.add_verse(verse.strip(), u'V')
|
||||
# Split up the authors
|
||||
authors = self.get_field(fi_author)
|
||||
author_list = authors.split(u'/')
|
||||
if len(author_list) < 2:
|
||||
author_list = authors.split(u',')
|
||||
for author_name in author_list:
|
||||
self.add_author(author_name.strip())
|
||||
if self.stop_import_flag:
|
||||
success = False
|
||||
break
|
||||
self.finish()
|
||||
if not self.stop_import_flag:
|
||||
self.import_wizard.incrementProgressBar(u'')
|
||||
db_file.close()
|
||||
self.memo_file.close()
|
||||
return success
|
||||
|
||||
def find_field(self, field_name):
|
||||
return [i for i,x in enumerate(self.field_descs) \
|
||||
if x.name == field_name][0]
|
||||
|
||||
def set_record_struct(self, field_descs):
|
||||
# Begin with empty field struct list
|
||||
fsl = ['>']
|
||||
for field_desc in field_descs:
|
||||
if field_desc.type == 1:
|
||||
# string
|
||||
fsl.append('%ds' % field_desc.size)
|
||||
elif field_desc.type == 3:
|
||||
# 16-bit int
|
||||
fsl.append('H')
|
||||
elif field_desc.type == 4:
|
||||
# 32-bit int
|
||||
fsl.append('I')
|
||||
elif field_desc.type == 9:
|
||||
# Logical
|
||||
fsl.append('B')
|
||||
elif field_desc.type == 0x0c:
|
||||
# Memo
|
||||
fsl.append('%ds' % field_desc.size)
|
||||
elif field_desc.type == 0x0d:
|
||||
# Blob
|
||||
fsl.append('%ds' % field_desc.size)
|
||||
elif field_desc.type == 0x15:
|
||||
# Timestamp
|
||||
fsl.append('Q')
|
||||
else:
|
||||
fsl.append('%ds' % field_desc.size)
|
||||
self.record_struct = struct.Struct(''.join(fsl))
|
||||
self.field_descs = field_descs
|
||||
|
||||
def get_field(self, field_desc_index):
|
||||
field = self.fields[field_desc_index]
|
||||
field_desc = self.field_descs[field_desc_index]
|
||||
# Check for 'blank' entries
|
||||
if isinstance(field, str):
|
||||
if len(field.rstrip('\0')) == 0:
|
||||
return u''
|
||||
elif field == 0:
|
||||
return 0
|
||||
# Format the field depending on the field type
|
||||
if field_desc.type == 1:
|
||||
# string
|
||||
return field.rstrip('\0').decode(u'windows-1252')
|
||||
elif field_desc.type == 3:
|
||||
# 16-bit int
|
||||
return field ^ 0x8000
|
||||
elif field_desc.type == 4:
|
||||
# 32-bit int
|
||||
return field ^ 0x80000000
|
||||
elif field_desc.type == 9:
|
||||
# Logical
|
||||
return (field ^ 0x80 == 1)
|
||||
elif field_desc.type == 0x0c or field_desc.type == 0x0d:
|
||||
# Memo or Blob
|
||||
sub_block, block_start, blob_size = \
|
||||
struct.unpack_from('<bhxi', field, len(field)-10)
|
||||
self.memo_file.seek(block_start * 256)
|
||||
memo_block_type, = struct.unpack('b', self.memo_file.read(1))
|
||||
if memo_block_type == 2:
|
||||
self.memo_file.seek(8, os.SEEK_CUR)
|
||||
elif memo_block_type == 3:
|
||||
if sub_block < 0 or sub_block > 63:
|
||||
return u'';
|
||||
self.memo_file.seek(11 + (5 * sub_block), os.SEEK_CUR)
|
||||
sub_block_start, = struct.unpack('B', self.memo_file.read(1))
|
||||
self.memo_file.seek((block_start * 256) +
|
||||
(sub_block_start * 16))
|
||||
else:
|
||||
return u'';
|
||||
return self.memo_file.read(blob_size)
|
||||
else:
|
||||
return 0
|
@ -28,6 +28,7 @@ from opensongimport import OpenSongImport
|
||||
from olpimport import OpenLPSongImport
|
||||
from wowimport import WowImport
|
||||
from cclifileimport import CCLIFileImport
|
||||
from ewimport import EasyWorshipSongImport
|
||||
# Imports that might fail
|
||||
try:
|
||||
from olp1import import OpenLP1SongImport
|
||||
@ -61,7 +62,8 @@ class SongFormat(object):
|
||||
CCLI = 5
|
||||
SongsOfFellowship = 6
|
||||
Generic = 7
|
||||
CSV = 8
|
||||
#CSV = 8
|
||||
EasyWorship = 8
|
||||
|
||||
@staticmethod
|
||||
def get_class(format):
|
||||
@ -85,6 +87,8 @@ class SongFormat(object):
|
||||
return OooImport
|
||||
elif format == SongFormat.CCLI:
|
||||
return CCLIFileImport
|
||||
elif format == SongFormat.EasyWorship:
|
||||
return EasyWorshipSongImport
|
||||
# else:
|
||||
return None
|
||||
|
||||
@ -101,7 +105,8 @@ class SongFormat(object):
|
||||
SongFormat.WordsOfWorship,
|
||||
SongFormat.CCLI,
|
||||
SongFormat.SongsOfFellowship,
|
||||
SongFormat.Generic
|
||||
SongFormat.Generic,
|
||||
SongFormat.EasyWorship
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
|
Loading…
Reference in New Issue
Block a user