This commit is contained in:
rimach 2010-12-08 22:06:00 +01:00
commit 79bb7457d3
5 changed files with 202 additions and 57 deletions

View File

@ -23,11 +23,34 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 # # with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
import logging
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from openlp.core.lib import SettingsTab, Receiver, translate from openlp.core.lib import SettingsTab, Receiver, translate
log = logging.getLogger(__name__)
class ValidEdit(QtGui.QLineEdit):
"""
Only allow numeric characters to be edited
"""
def __init__(self, parent):
"""
Set up Override and Validator
"""
QtGui.QLineEdit.__init__(self, parent)
self.setValidator(QtGui.QIntValidator(0, 9999, self))
def validText(self):
"""
Only return Integers. Space is 0
"""
if self.text().isEmpty():
return QtCore.QString(u'0')
else:
return self.text()
class GeneralTab(SettingsTab): class GeneralTab(SettingsTab):
""" """
GeneralTab is the general settings tab in the settings dialog. GeneralTab is the general settings tab in the settings dialog.
@ -240,7 +263,7 @@ class GeneralTab(SettingsTab):
self.customXLabel.setAlignment(QtCore.Qt.AlignCenter) self.customXLabel.setAlignment(QtCore.Qt.AlignCenter)
self.customXLabel.setObjectName(u'customXLabel') self.customXLabel.setObjectName(u'customXLabel')
self.customXLayout.addWidget(self.customXLabel) self.customXLayout.addWidget(self.customXLabel)
self.customXValueEdit = QtGui.QLineEdit(self.displayGroupBox) self.customXValueEdit = ValidEdit(self.displayGroupBox)
self.customXValueEdit.setObjectName(u'customXValueEdit') self.customXValueEdit.setObjectName(u'customXValueEdit')
self.customXLayout.addWidget(self.customXValueEdit) self.customXLayout.addWidget(self.customXValueEdit)
self.customLayout.addLayout(self.customXLayout) self.customLayout.addLayout(self.customXLayout)
@ -252,7 +275,7 @@ class GeneralTab(SettingsTab):
self.customYLabel.setAlignment(QtCore.Qt.AlignCenter) self.customYLabel.setAlignment(QtCore.Qt.AlignCenter)
self.customYLabel.setObjectName(u'customYLabel') self.customYLabel.setObjectName(u'customYLabel')
self.customYLayout.addWidget(self.customYLabel) self.customYLayout.addWidget(self.customYLabel)
self.customYValueEdit = QtGui.QLineEdit(self.displayGroupBox) self.customYValueEdit = ValidEdit(self.displayGroupBox)
self.customYValueEdit.setObjectName(u'customYValueEdit') self.customYValueEdit.setObjectName(u'customYValueEdit')
self.customYLayout.addWidget(self.customYValueEdit) self.customYLayout.addWidget(self.customYValueEdit)
self.customLayout.addLayout(self.customYLayout) self.customLayout.addLayout(self.customYLayout)
@ -265,7 +288,7 @@ class GeneralTab(SettingsTab):
self.customWidthLabel.setAlignment(QtCore.Qt.AlignCenter) self.customWidthLabel.setAlignment(QtCore.Qt.AlignCenter)
self.customWidthLabel.setObjectName(u'customWidthLabel') self.customWidthLabel.setObjectName(u'customWidthLabel')
self.customWidthLayout.addWidget(self.customWidthLabel) self.customWidthLayout.addWidget(self.customWidthLabel)
self.customWidthValueEdit = QtGui.QLineEdit(self.displayGroupBox) self.customWidthValueEdit = ValidEdit(self.displayGroupBox)
self.customWidthValueEdit.setObjectName(u'customWidthValueEdit') self.customWidthValueEdit.setObjectName(u'customWidthValueEdit')
self.customWidthLayout.addWidget(self.customWidthValueEdit) self.customWidthLayout.addWidget(self.customWidthValueEdit)
self.customLayout.addLayout(self.customWidthLayout) self.customLayout.addLayout(self.customWidthLayout)
@ -277,7 +300,7 @@ class GeneralTab(SettingsTab):
self.customHeightLabel.setAlignment(QtCore.Qt.AlignCenter) self.customHeightLabel.setAlignment(QtCore.Qt.AlignCenter)
self.customHeightLabel.setObjectName(u'customHeightLabel') self.customHeightLabel.setObjectName(u'customHeightLabel')
self.customHeightLayout.addWidget(self.customHeightLabel) self.customHeightLayout.addWidget(self.customHeightLabel)
self.customHeightValueEdit = QtGui.QLineEdit(self.displayGroupBox) self.customHeightValueEdit = ValidEdit(self.displayGroupBox)
self.customHeightValueEdit.setObjectName(u'customHeightValueEdit') self.customHeightValueEdit.setObjectName(u'customHeightValueEdit')
self.customHeightLayout.addWidget(self.customHeightValueEdit) self.customHeightLayout.addWidget(self.customHeightValueEdit)
self.customLayout.addLayout(self.customHeightLayout) self.customLayout.addLayout(self.customHeightLayout)
@ -465,10 +488,10 @@ class GeneralTab(SettingsTab):
# Reset screens after initial definition # Reset screens after initial definition
if self.overrideChanged: if self.overrideChanged:
self.screens.override[u'size'] = QtCore.QRect( self.screens.override[u'size'] = QtCore.QRect(
int(self.customXValueEdit.text()), int(self.customXValueEdit.validText()),
int(self.customYValueEdit.text()), int(self.customYValueEdit.validText()),
int(self.customWidthValueEdit.text()), int(self.customWidthValueEdit.validText()),
int(self.customHeightValueEdit.text())) int(self.customHeightValueEdit.validText()))
if self.overrideCheckBox.isChecked(): if self.overrideCheckBox.isChecked():
self.screens.set_override_display() self.screens.set_override_display()
else: else:

View File

@ -536,7 +536,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
return False return False
if self.song.verse_order: if self.song.verse_order:
order = [] order = []
order_names = self.song.verse_order.split(u' ') order_names = self.song.verse_order.split()
for item in order_names: for item in order_names:
if len(item) == 1: if len(item) == 1:
order.append(item.lower() + u'1') order.append(item.lower() + u'1')

40
openlp/plugins/songs/lib/cclifileimport.py Executable file → Normal file
View File

@ -76,7 +76,12 @@ class CCLIFileImport(SongImport):
lines = [] lines = []
if os.path.isfile(filename): if os.path.isfile(filename):
detect_file = open(filename, u'r') detect_file = open(filename, u'r')
details = chardet.detect(detect_file.read(2048)) detect_content = detect_file.read(2048)
try:
unicode(detect_content, u'utf-8')
details = {'confidence': 1, 'encoding': 'utf-8'}
except UnicodeDecodeError:
details = chardet.detect(detect_content)
detect_file.close() detect_file.close()
infile = codecs.open(filename, u'r', details['encoding']) infile = codecs.open(filename, u'r', details['encoding'])
lines = infile.readlines() lines = infile.readlines()
@ -165,6 +170,7 @@ class CCLIFileImport(SongImport):
song_words = line[6:].strip() song_words = line[6:].strip()
#Unhandled usr keywords:Type,Version,Admin,Themes,Keys #Unhandled usr keywords:Type,Version,Admin,Themes,Keys
#Process Fields and words sections #Process Fields and words sections
check_first_verse_line = False
field_list = song_fields.split(u'/t') field_list = song_fields.split(u'/t')
words_list = song_words.split(u'/t') words_list = song_words.split(u'/t')
for counter in range(0, len(field_list)): for counter in range(0, len(field_list)):
@ -176,10 +182,25 @@ class CCLIFileImport(SongImport):
verse_type = u'B' verse_type = u'B'
else: #Other else: #Other
verse_type = u'O' verse_type = u'O'
check_first_verse_line = True
verse_text = unicode(words_list[counter]) verse_text = unicode(words_list[counter])
verse_text = verse_text.replace("/n", "\n") verse_text = verse_text.replace("/n", "\n")
verse_lines = verse_text.split(u'\n', 1)
if check_first_verse_line:
if verse_lines[0].startswith(u'(PRE-CHORUS'):
verse_type = u'P'
log.debug(u'USR verse PRE-CHORUS: %s', verse_lines[0] )
verse_text = verse_lines[1]
elif verse_lines[0].startswith(u'(BRIDGE'):
verse_type = u'B'
log.debug(u'USR verse BRIDGE')
verse_text = verse_lines[1]
elif verse_lines[0].startswith(u'('):
verse_type = u'O'
verse_text = verse_lines[1]
if len(verse_text) > 0: if len(verse_text) > 0:
self.add_verse(verse_text, verse_type) self.add_verse(verse_text, verse_type)
check_first_verse_line = False
#Handle multiple authors #Handle multiple authors
author_list = song_author.split(u'/') author_list = song_author.split(u'/')
if len(author_list) < 2: if len(author_list) < 2:
@ -228,6 +249,7 @@ class CCLIFileImport(SongImport):
log.debug(u'TXT file text: %s', textList) log.debug(u'TXT file text: %s', textList)
self.set_defaults() self.set_defaults()
line_number = 0 line_number = 0
check_first_verse_line = False
verse_text = u'' verse_text = u''
song_comments = u'' song_comments = u''
song_copyright = u'' song_copyright = u''
@ -265,12 +287,28 @@ class CCLIFileImport(SongImport):
elif verse_desc_parts[0].startswith(u'Br'): elif verse_desc_parts[0].startswith(u'Br'):
verse_type = u'B' verse_type = u'B'
else: else:
#we need to analyse the next line for
#verse type, so set flag
verse_type = u'O' verse_type = u'O'
check_first_verse_line = True
verse_number = verse_desc_parts[1] verse_number = verse_desc_parts[1]
else: else:
verse_type = u'O' verse_type = u'O'
verse_number = 1 verse_number = 1
verse_start = True verse_start = True
else:
#check first line for verse type
if check_first_verse_line:
if line.startswith(u'(PRE-CHORUS'):
verse_type = u'P'
elif line.startswith(u'(BRIDGE'):
verse_type = u'B'
# Handle all other misc types
elif line.startswith(u'('):
verse_type = u'O'
else:
verse_text = verse_text + line
check_first_verse_line = False
else: else:
# We have verse content or the start of the # We have verse content or the start of the
# last part. Add l so as to keep the CRLF # last part. Add l so as to keep the CRLF

View File

@ -366,7 +366,7 @@ class SongMediaItem(MediaManagerItem):
verse[1][:30], unicode(verse[1]), verseTag) verse[1][:30], unicode(verse[1]), verseTag)
else: else:
# Loop through the verse list and expand the song accordingly. # Loop through the verse list and expand the song accordingly.
for order in song.verse_order.upper().split(u' '): for order in song.verse_order.upper().split():
if len(order) == 0: if len(order) == 0:
break break
for verse in verseList: for verse in verseList:
@ -390,7 +390,7 @@ class SongMediaItem(MediaManagerItem):
raw_footer.append(author_list) raw_footer.append(author_list)
raw_footer.append(song.copyright ) raw_footer.append(song.copyright )
raw_footer.append(unicode( raw_footer.append(unicode(
translate('SongsPlugin.MediaItem', 'CCLI Licence: ') + translate('SongsPlugin.MediaItem', 'CCLI License: ') +
QtCore.QSettings().value(u'general/ccli number', QtCore.QSettings().value(u'general/ccli number',
QtCore.QVariant(u'')).toString())) QtCore.QVariant(u'')).toString()))
service_item.raw_footer = raw_footer service_item.raw_footer = raw_footer

View File

@ -27,8 +27,11 @@
The :mod:`olp1import` module provides the functionality for importing The :mod:`olp1import` module provides the functionality for importing
openlp.org 1.x song databases into the current installation database. openlp.org 1.x song databases into the current installation database.
""" """
from PyQt4 import QtGui
import logging import logging
import chardet from chardet.universaldetector import UniversalDetector
import sqlite import sqlite
from openlp.core.lib import translate from openlp.core.lib import translate
@ -56,60 +59,38 @@ class OpenLP1SongImport(SongImport):
SongImport.__init__(self, manager) SongImport.__init__(self, manager)
self.import_source = kwargs[u'filename'] self.import_source = kwargs[u'filename']
def decode_string(self, raw, guess):
"""
Use chardet to detect the encoding of the raw string, and convert it
to unicode.
``raw``
The raw bytestring to decode.
``guess``
What chardet guessed the encoding to be.
"""
if guess[u'confidence'] < 0.8:
codec = u'windows-1252'
else:
codec = guess[u'encoding']
try:
decoded = unicode(raw, codec)
self.last_encoding = codec
except UnicodeDecodeError:
log.exception(
u'Error in detecting openlp.org 1.x database encoding.')
try:
decoded = unicode(raw, self.last_encoding)
except UnicodeDecodeError:
# possibly show an error form
#self.import_wizard.showError(u'There was a problem '
# u'detecting the encoding of a string')
decoded = raw
return decoded
def do_import(self): def do_import(self):
""" """
Run the import for an openlp.org 1.x song database. Run the import for an openlp.org 1.x song database.
""" """
# Connect to the database # Connect to the database
connection = sqlite.connect(self.import_source) encoding = self.get_encoding()
if not encoding:
return False
connection = sqlite.connect(self.import_source, mode=0444,
encoding=(encoding, 'replace'))
cursor = connection.cursor() cursor = connection.cursor()
# Determine if we're using a new or an old DB # Determine if we're using a new or an old DB
cursor.execute(u'SELECT name FROM sqlite_master ' cursor.execute(u'SELECT name FROM sqlite_master '
u'WHERE type = \'table\' AND name = \'tracks\'') u'WHERE type = \'table\' AND name = \'tracks\'')
table_list = cursor.fetchall() new_db = len(cursor.fetchall()) > 0
new_db = len(table_list) > 0
# Count the number of records we need to import, for the progress bar # Count the number of records we need to import, for the progress bar
cursor.execute(u'-- types int')
cursor.execute(u'SELECT COUNT(songid) FROM songs') cursor.execute(u'SELECT COUNT(songid) FROM songs')
count = int(cursor.fetchone()[0]) count = cursor.fetchone()[0]
success = True success = True
self.import_wizard.importProgressBar.setMaximum(count) self.import_wizard.importProgressBar.setMaximum(count)
# "cache" our list of authors # "cache" our list of authors
cursor.execute(u'-- types int, unicode')
cursor.execute(u'SELECT authorid, authorname FROM authors') cursor.execute(u'SELECT authorid, authorname FROM authors')
authors = cursor.fetchall() authors = cursor.fetchall()
if new_db: if new_db:
# "cache" our list of tracks # "cache" our list of tracks
cursor.execute(u'-- types int, unicode')
cursor.execute(u'SELECT trackid, fulltrackname FROM tracks') cursor.execute(u'SELECT trackid, fulltrackname FROM tracks')
tracks = cursor.fetchall() tracks = cursor.fetchall()
# Import the songs # Import the songs
cursor.execute(u'-- types int, unicode, unicode, unicode')
cursor.execute(u'SELECT songid, songtitle, lyrics || \'\' AS lyrics, ' cursor.execute(u'SELECT songid, songtitle, lyrics || \'\' AS lyrics, '
u'copyrightinfo FROM songs') u'copyrightinfo FROM songs')
songs = cursor.fetchall() songs = cursor.fetchall()
@ -119,16 +100,19 @@ class OpenLP1SongImport(SongImport):
success = False success = False
break break
song_id = song[0] song_id = song[0]
guess = chardet.detect(song[2]) title = song[1]
title = self.decode_string(song[1], guess) lyrics = song[2].replace(u'\r\n', u'\n')
lyrics = self.decode_string(song[2], guess).replace(u'\r', u'') copyright = song[3]
copyright = self.decode_string(song[3], guess)
self.import_wizard.incrementProgressBar( self.import_wizard.incrementProgressBar(
unicode(translate('SongsPlugin.ImportWizardForm', unicode(translate('SongsPlugin.ImportWizardForm',
'Importing "%s"...')) % title) 'Importing "%s"...')) % title)
self.title = title self.title = title
self.process_song_text(lyrics) verses = lyrics.split(u'\n\n')
for verse in verses:
if verse.strip() != u'':
self.add_verse(verse.strip())
self.add_copyright(copyright) self.add_copyright(copyright)
cursor.execute(u'-- types int')
cursor.execute(u'SELECT authorid FROM songauthors ' cursor.execute(u'SELECT authorid FROM songauthors '
u'WHERE songid = %s' % song_id) u'WHERE songid = %s' % song_id)
author_ids = cursor.fetchall() author_ids = cursor.fetchall()
@ -138,12 +122,13 @@ class OpenLP1SongImport(SongImport):
break break
for author in authors: for author in authors:
if author[0] == author_id[0]: if author[0] == author_id[0]:
self.parse_author(self.decode_string(author[1], guess)) self.parse_author(author[1])
break break
if self.stop_import_flag: if self.stop_import_flag:
success = False success = False
break break
if new_db: if new_db:
cursor.execute(u'-- types int')
cursor.execute(u'SELECT trackid FROM songtracks ' cursor.execute(u'SELECT trackid FROM songtracks '
u'WHERE songid = %s ORDER BY listindex' % song_id) u'WHERE songid = %s ORDER BY listindex' % song_id)
track_ids = cursor.fetchall() track_ids = cursor.fetchall()
@ -153,8 +138,7 @@ class OpenLP1SongImport(SongImport):
break break
for track in tracks: for track in tracks:
if track[0] == track_id[0]: if track[0] == track_id[0]:
self.add_media_file(self.decode_string(track[1], self.add_media_file(track[1])
guess))
break break
if self.stop_import_flag: if self.stop_import_flag:
success = False success = False
@ -162,3 +146,103 @@ class OpenLP1SongImport(SongImport):
self.finish() self.finish()
return success return success
def get_encoding(self):
"""
Detect character encoding of an openlp.org 1.x song database.
"""
# Connect to the database
connection = sqlite.connect(self.import_source, mode=0444)
cursor = connection.cursor()
detector = UniversalDetector()
# detect charset by authors
cursor.execute(u'SELECT authorname FROM authors')
authors = cursor.fetchall()
for author in authors:
detector.feed(author[0])
if detector.done:
detector.close()
return detector.result[u'encoding']
# detect charset by songs
cursor.execute(u'SELECT songtitle, copyrightinfo, '
u'lyrics || \'\' AS lyrics FROM songs')
songs = cursor.fetchall()
for index in [0, 1, 2]:
for song in songs:
detector.feed(song[index])
if detector.done:
detector.close()
return detector.result[u'encoding']
# detect charset by songs
cursor.execute(u'SELECT name FROM sqlite_master '
u'WHERE type = \'table\' AND name = \'tracks\'')
if len(cursor.fetchall()) > 0:
cursor.execute(u'SELECT fulltrackname FROM tracks')
tracks = cursor.fetchall()
for track in tracks:
detector.feed(track[0])
if detector.done:
detector.close()
return detector.result[u'encoding']
detector.close()
guess = detector.result[u'encoding']
# map chardet result to compatible windows standard code page
codepage_mapping = {'IBM866': u'cp866', 'TIS-620': u'cp874',
'SHIFT_JIS': u'cp932', 'GB2312': u'cp936', 'HZ-GB-2312': u'cp936',
'EUC-KR': u'cp949', 'Big5': u'cp950', 'ISO-8859-2': u'cp1250',
'windows-1250': u'cp1250', 'windows-1251': u'cp1251',
'windows-1252': u'cp1252', 'ISO-8859-7': u'cp1253',
'windows-1253': u'cp1253', 'ISO-8859-8': u'cp1255',
'windows-1255': u'cp1255'}
if guess in codepage_mapping:
guess = codepage_mapping[guess]
else:
guess = u'cp1252'
# Show dialog for encoding selection
encodings = [(u'cp1256', translate('SongsPlugin.OpenLP1SongImport',
'Arabic (CP-1256)')),
(u'cp1257', translate('SongsPlugin.OpenLP1SongImport',
'Baltic (CP-1257)')),
(u'cp1250', translate('SongsPlugin.OpenLP1SongImport',
'Central European (CP-1250)')),
(u'cp1251', translate('SongsPlugin.OpenLP1SongImport',
'Cyrillic (CP-1251)')),
(u'cp1253', translate('SongsPlugin.OpenLP1SongImport',
'Greek (CP-1253)')),
(u'cp1255', translate('SongsPlugin.OpenLP1SongImport',
'Hebrew (CP-1255)')),
(u'cp932', translate('SongsPlugin.OpenLP1SongImport',
'Japanese (CP-932)')),
(u'cp949', translate('SongsPlugin.OpenLP1SongImport',
'Korean (CP-949)')),
(u'cp936', translate('SongsPlugin.OpenLP1SongImport',
'Simplified Chinese (CP-936)')),
(u'cp874', translate('SongsPlugin.OpenLP1SongImport',
'Thai (CP-874)')),
(u'cp950', translate('SongsPlugin.OpenLP1SongImport',
'Traditional Chinese (CP-950)')),
(u'cp1254', translate('SongsPlugin.OpenLP1SongImport',
'Turkish (CP-1254)')),
(u'cp1258', translate('SongsPlugin.OpenLP1SongImport',
'Vietnam (CP-1258)')),
(u'cp1252', translate('SongsPlugin.OpenLP1SongImport',
'Western European (CP-1252)'))]
encoding_index = 0
for index in range(len(encodings)):
if guess == encodings[index][0]:
encoding_index = index
break
chosen_encoding = QtGui.QInputDialog.getItem(None,
translate('SongsPlugin.OpenLP1SongImport',
'Database Character Encoding'),
translate('SongsPlugin.OpenLP1SongImport',
'The codepage setting is responsible\n'
'for the correct character representation.\n'
'Usually you are fine with the preselected choise.'),
[pair[1] for pair in encodings], encoding_index, False)
if not chosen_encoding[1]:
return None
return filter(lambda item: item[1] == chosen_encoding[0],
encodings)[0][0]