forked from openlp/openlp
More updates
This commit is contained in:
parent
3ae188c287
commit
a0a2e5efbc
@ -107,7 +107,7 @@ class SongFormat(object):
|
|||||||
Name of the format, e.g. ``'OpenLyrics'``
|
Name of the format, e.g. ``'OpenLyrics'``
|
||||||
|
|
||||||
``'prefix'``
|
``'prefix'``
|
||||||
Prefix for Qt objects. Use mixedCase, e.g. ``'openLyrics'``
|
Prefix for Qt objects. Use mixedCase, e.g. ``'open_lyrics'``
|
||||||
See ``SongImportForm.add_file_select_item()``
|
See ``SongImportForm.add_file_select_item()``
|
||||||
|
|
||||||
Optional attributes for each song format:
|
Optional attributes for each song format:
|
||||||
@ -185,7 +185,7 @@ class SongFormat(object):
|
|||||||
OpenLyrics: {
|
OpenLyrics: {
|
||||||
'class': OpenLyricsImport,
|
'class': OpenLyricsImport,
|
||||||
'name': 'OpenLyrics',
|
'name': 'OpenLyrics',
|
||||||
'prefix': 'openLyrics',
|
'prefix': 'open_lyrics',
|
||||||
'filter': '%s (*.xml)' % translate('SongsPlugin.ImportWizardForm', 'OpenLyrics Files'),
|
'filter': '%s (*.xml)' % translate('SongsPlugin.ImportWizardForm', 'OpenLyrics Files'),
|
||||||
'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'OpenLyrics or OpenLP 2.0 Exported Song')
|
'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'OpenLyrics or OpenLP 2.0 Exported Song')
|
||||||
},
|
},
|
||||||
|
@ -97,7 +97,7 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
def add_end_header_bar(self):
|
def add_end_header_bar(self):
|
||||||
self.toolbar.addSeparator()
|
self.toolbar.addSeparator()
|
||||||
## Song Maintenance Button ##
|
## Song Maintenance Button ##
|
||||||
self.maintenanceAction = self.toolbar.add_toolbar_action('maintenanceAction',
|
self.maintenance_action = self.toolbar.add_toolbar_action('maintenance_action',
|
||||||
icon=':/songs/song_maintenance.png',
|
icon=':/songs/song_maintenance.png',
|
||||||
triggers=self.on_song_maintenance_click)
|
triggers=self.on_song_maintenance_click)
|
||||||
self.add_search_to_toolbar()
|
self.add_search_to_toolbar()
|
||||||
@ -105,13 +105,13 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
Registry().register_function('songs_load_list', self.on_song_list_load)
|
Registry().register_function('songs_load_list', self.on_song_list_load)
|
||||||
Registry().register_function('songs_preview', self.on_preview_click)
|
Registry().register_function('songs_preview', self.on_preview_click)
|
||||||
QtCore.QObject.connect(self.search_text_edit, QtCore.SIGNAL('cleared()'), self.on_clear_text_button_click)
|
QtCore.QObject.connect(self.search_text_edit, QtCore.SIGNAL('cleared()'), self.on_clear_text_button_click)
|
||||||
QtCore.QObject.connect(self.search_text_edit, QtCore.SIGNAL('searchTypeChanged(int)'),
|
QtCore.QObject.connect(
|
||||||
self.on_search_text_button_clicked)
|
self.search_text_edit, QtCore.SIGNAL('searchTypeChanged(int)'), self.on_search_text_button_clicked)
|
||||||
|
|
||||||
def add_custom_context_actions(self):
|
def add_custom_context_actions(self):
|
||||||
create_widget_action(self.list_view, separator=True)
|
create_widget_action(self.list_view, separator=True)
|
||||||
create_widget_action(self.list_view,
|
create_widget_action(
|
||||||
text=translate('OpenLP.MediaManagerItem', '&Clone'), icon=':/general/general_clone.png',
|
self.list_view, text=translate('OpenLP.MediaManagerItem', '&Clone'), icon=':/general/general_clone.png',
|
||||||
triggers=self.on_clone_click)
|
triggers=self.on_clone_click)
|
||||||
|
|
||||||
def on_focus(self):
|
def on_focus(self):
|
||||||
@ -123,14 +123,14 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
"""
|
"""
|
||||||
log.debug('config_updated')
|
log.debug('config_updated')
|
||||||
self.search_as_you_type = Settings().value(self.settings_section + '/search as type')
|
self.search_as_you_type = Settings().value(self.settings_section + '/search as type')
|
||||||
self.updateServiceOnEdit = Settings().value(self.settings_section + '/update service on edit')
|
self.update_service_on_edit = Settings().value(self.settings_section + '/update service on edit')
|
||||||
self.addSongFromService = Settings().value(self.settings_section + '/add song from service',)
|
self.add_song_from_service = Settings().value(self.settings_section + '/add song from service',)
|
||||||
|
|
||||||
def retranslateUi(self):
|
def retranslateUi(self):
|
||||||
self.search_text_label.setText('%s:' % UiStrings().Search)
|
self.search_text_label.setText('%s:' % UiStrings().Search)
|
||||||
self.search_text_button.setText(UiStrings().Search)
|
self.search_text_button.setText(UiStrings().Search)
|
||||||
self.maintenanceAction.setText(SongStrings.SongMaintenance)
|
self.maintenance_action.setText(SongStrings.SongMaintenance)
|
||||||
self.maintenanceAction.setToolTip(translate('SongsPlugin.MediaItem',
|
self.maintenance_action.setToolTip(translate('SongsPlugin.MediaItem',
|
||||||
'Maintain the lists of authors, topics and books.'))
|
'Maintain the lists of authors, topics and books.'))
|
||||||
|
|
||||||
def initialise(self):
|
def initialise(self):
|
||||||
@ -139,7 +139,7 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
"""
|
"""
|
||||||
self.song_maintenance_form = SongMaintenanceForm(self.plugin.manager, self)
|
self.song_maintenance_form = SongMaintenanceForm(self.plugin.manager, self)
|
||||||
self.edit_song_form = EditSongForm(self, self.main_window, self.plugin.manager)
|
self.edit_song_form = EditSongForm(self, self.main_window, self.plugin.manager)
|
||||||
self.openLyrics = OpenLyrics(self.plugin.manager)
|
self.open_lyrics = OpenLyrics(self.plugin.manager)
|
||||||
self.search_text_edit.set_search_types([
|
self.search_text_edit.set_search_types([
|
||||||
(SongSearch.Entire, ':/songs/song_search_all.png',
|
(SongSearch.Entire, ':/songs/song_search_all.png',
|
||||||
translate('SongsPlugin.MediaItem', 'Entire Song'),
|
translate('SongsPlugin.MediaItem', 'Entire Song'),
|
||||||
@ -154,8 +154,7 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
translate('SongsPlugin.MediaItem', 'Search Authors...')),
|
translate('SongsPlugin.MediaItem', 'Search Authors...')),
|
||||||
(SongSearch.Books, ':/songs/song_book_edit.png', SongStrings.SongBooks,
|
(SongSearch.Books, ':/songs/song_book_edit.png', SongStrings.SongBooks,
|
||||||
translate('SongsPlugin.MediaItem', 'Search Song Books...')),
|
translate('SongsPlugin.MediaItem', 'Search Song Books...')),
|
||||||
(SongSearch.Themes, ':/slides/slide_theme.png',
|
(SongSearch.Themes, ':/slides/slide_theme.png', UiStrings().Themes, UiStrings().SearchThemes)
|
||||||
UiStrings().Themes, UiStrings().SearchThemes)
|
|
||||||
])
|
])
|
||||||
self.search_text_edit.set_current_search_type(Settings().value('%s/last search type' % self.settings_section))
|
self.search_text_edit.set_current_search_type(Settings().value('%s/last search type' % self.settings_section))
|
||||||
self.config_update()
|
self.config_update()
|
||||||
@ -172,64 +171,65 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
self.display_results_song(search_results)
|
self.display_results_song(search_results)
|
||||||
elif search_type == SongSearch.Titles:
|
elif search_type == SongSearch.Titles:
|
||||||
log.debug('Titles Search')
|
log.debug('Titles Search')
|
||||||
search_results = self.plugin.manager.get_all_objects(Song,
|
search_string = '%' + clean_string(search_keywords) + '%'
|
||||||
Song.search_title.like('%' + clean_string(search_keywords) + '%'))
|
search_results = self.plugin.manager.get_all_objects(Song, Song.search_title.like(search_string))
|
||||||
self.display_results_song(search_results)
|
self.display_results_song(search_results)
|
||||||
elif search_type == SongSearch.Lyrics:
|
elif search_type == SongSearch.Lyrics:
|
||||||
log.debug('Lyrics Search')
|
log.debug('Lyrics Search')
|
||||||
search_results = self.plugin.manager.get_all_objects(Song,
|
search_string = '%' + clean_string(search_keywords) + '%'
|
||||||
Song.search_lyrics.like('%' + clean_string(search_keywords) + '%'))
|
search_results = self.plugin.manager.get_all_objects(Song, Song.search_lyrics.like(search_string))
|
||||||
self.display_results_song(search_results)
|
self.display_results_song(search_results)
|
||||||
elif search_type == SongSearch.Authors:
|
elif search_type == SongSearch.Authors:
|
||||||
log.debug('Authors Search')
|
log.debug('Authors Search')
|
||||||
search_results = self.plugin.manager.get_all_objects(Author,
|
search_string = '%' + search_keywords + '%'
|
||||||
Author.display_name.like('%' + search_keywords + '%'), Author.display_name.asc())
|
search_results = self.plugin.manager.get_all_objects(
|
||||||
|
Author, Author.display_name.like(search_string), Author.display_name.asc())
|
||||||
self.display_results_author(search_results)
|
self.display_results_author(search_results)
|
||||||
elif search_type == SongSearch.Books:
|
elif search_type == SongSearch.Books:
|
||||||
log.debug('Books Search')
|
log.debug('Books Search')
|
||||||
search_results = self.plugin.manager.get_all_objects(Book,
|
search_string = '%' + search_keywords + '%'
|
||||||
Book.name.like('%' + search_keywords + '%'), Book.name.asc())
|
search_results = self.plugin.manager.get_all_objects(Book, Book.name.like(search_string), Book.name.asc())
|
||||||
song_number = False
|
song_number = False
|
||||||
if not search_results:
|
if not search_results:
|
||||||
search_keywords = search_keywords.rpartition(' ')
|
search_keywords = search_keywords.rpartition(' ')
|
||||||
|
search_string = '%' + search_keywords + '%'
|
||||||
search_results = self.plugin.manager.get_all_objects(Book,
|
search_results = self.plugin.manager.get_all_objects(Book,
|
||||||
Book.name.like('%' + search_keywords[0] + '%'), Book.name.asc())
|
Book.name.like(search_string), Book.name.asc())
|
||||||
song_number = re.sub(r'[^0-9]', '', search_keywords[2])
|
song_number = re.sub(r'[^0-9]', '', search_keywords[2])
|
||||||
self.display_results_book(search_results, song_number)
|
self.display_results_book(search_results, song_number)
|
||||||
elif search_type == SongSearch.Themes:
|
elif search_type == SongSearch.Themes:
|
||||||
log.debug('Theme Search')
|
log.debug('Theme Search')
|
||||||
search_results = self.plugin.manager.get_all_objects(Song,
|
search_string = '%' + search_keywords + '%'
|
||||||
Song.theme_name.like('%' + search_keywords + '%'))
|
search_results = self.plugin.manager.get_all_objects(Song, Song.theme_name.like(search_string))
|
||||||
self.display_results_song(search_results)
|
self.display_results_song(search_results)
|
||||||
self.check_search_result()
|
self.check_search_result()
|
||||||
|
|
||||||
def search_entire(self, search_keywords):
|
def search_entire(self, search_keywords):
|
||||||
return self.plugin.manager.get_all_objects(Song,
|
search_string = '%' + clean_string(search_keywords) + '%'
|
||||||
or_(Song.search_title.like('%' + clean_string(search_keywords) + '%'),
|
return self.plugin.manager.get_all_objects(
|
||||||
Song.search_lyrics.like('%' + clean_string(search_keywords) + '%'),
|
Song, or_(Song.search_title.like(search_string), Song.search_lyrics.like(search_string),
|
||||||
Song.comments.like('%' + search_keywords.lower() + '%')))
|
Song.comments.like(search_string)))
|
||||||
|
|
||||||
def on_song_list_load(self):
|
def on_song_list_load(self):
|
||||||
"""
|
"""
|
||||||
Handle the exit from the edit dialog and trigger remote updates
|
Handle the exit from the edit dialog and trigger remote updates of songs
|
||||||
of songs
|
|
||||||
"""
|
"""
|
||||||
log.debug('on_song_list_load - start')
|
log.debug('on_song_list_load - start')
|
||||||
# Called to redisplay the song list screen edit from a search or from the exit of the Song edit dialog. If
|
# Called to redisplay the song list screen edit from a search or from the exit of the Song edit dialog. If
|
||||||
# remote editing is active Trigger it and clean up so it will not update again. Push edits to the service
|
# remote editing is active Trigger it and clean up so it will not update again. Push edits to the service
|
||||||
# manager to update items
|
# manager to update items
|
||||||
if self.edit_item and self.updateServiceOnEdit and not self.remote_triggered:
|
if self.edit_item and self.update_service_on_edit and not self.remote_triggered:
|
||||||
item = self.build_service_item(self.edit_item)
|
item = self.build_service_item(self.edit_item)
|
||||||
self.service_manager.replace_service_item(item)
|
self.service_manager.replace_service_item(item)
|
||||||
self.on_search_text_button_clicked()
|
self.on_search_text_button_clicked()
|
||||||
log.debug('on_song_list_load - finished')
|
log.debug('on_song_list_load - finished')
|
||||||
|
|
||||||
def display_results_song(self, searchresults):
|
def display_results_song(self, search_results):
|
||||||
log.debug('display results Song')
|
log.debug('display results Song')
|
||||||
self.save_auto_select_id()
|
self.save_auto_select_id()
|
||||||
self.list_view.clear()
|
self.list_view.clear()
|
||||||
searchresults.sort(key=lambda song: song.sort_key)
|
search_results.sort(key=lambda song: song.sort_key)
|
||||||
for song in searchresults:
|
for song in search_results:
|
||||||
# Do not display temporary songs
|
# Do not display temporary songs
|
||||||
if song.temporary:
|
if song.temporary:
|
||||||
continue
|
continue
|
||||||
@ -244,10 +244,10 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
self.list_view.setCurrentItem(song_name)
|
self.list_view.setCurrentItem(song_name)
|
||||||
self.auto_select_id = -1
|
self.auto_select_id = -1
|
||||||
|
|
||||||
def display_results_author(self, searchresults):
|
def display_results_author(self, search_results):
|
||||||
log.debug('display results Author')
|
log.debug('display results Author')
|
||||||
self.list_view.clear()
|
self.list_view.clear()
|
||||||
for author in searchresults:
|
for author in search_results:
|
||||||
for song in author.songs:
|
for song in author.songs:
|
||||||
# Do not display temporary songs
|
# Do not display temporary songs
|
||||||
if song.temporary:
|
if song.temporary:
|
||||||
@ -257,12 +257,11 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
song_name.setData(QtCore.Qt.UserRole, song.id)
|
song_name.setData(QtCore.Qt.UserRole, song.id)
|
||||||
self.list_view.addItem(song_name)
|
self.list_view.addItem(song_name)
|
||||||
|
|
||||||
def display_results_book(self, searchresults, song_number=False):
|
def display_results_book(self, search_results, song_number=False):
|
||||||
log.debug('display results Book')
|
log.debug('display results Book')
|
||||||
self.list_view.clear()
|
self.list_view.clear()
|
||||||
for book in searchresults:
|
for book in search_results:
|
||||||
songs = sorted(book.songs, key=lambda song:
|
songs = sorted(book.songs, key=lambda song: int(re.match(r'[0-9]+', '0' + song.song_number).group()))
|
||||||
int(re.match(r'[0-9]+', '0' + song.song_number).group()))
|
|
||||||
for song in songs:
|
for song in songs:
|
||||||
# Do not display temporary songs
|
# Do not display temporary songs
|
||||||
if song.temporary:
|
if song.temporary:
|
||||||
@ -305,9 +304,9 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
Registry().execute('songs_load_list')
|
Registry().execute('songs_load_list')
|
||||||
|
|
||||||
def on_export_click(self):
|
def on_export_click(self):
|
||||||
if not hasattr(self, 'exportWizard'):
|
if not hasattr(self, 'export_wizard'):
|
||||||
self.exportWizard = SongExportForm(self, self.plugin)
|
self.export_wizard = SongExportForm(self, self.plugin)
|
||||||
self.exportWizard.exec_()
|
self.export_wizard.exec_()
|
||||||
|
|
||||||
def on_new_click(self):
|
def on_new_click(self):
|
||||||
log.debug('on_new_click')
|
log.debug('on_new_click')
|
||||||
@ -362,8 +361,8 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
"""
|
"""
|
||||||
if check_item_selected(self.list_view, UiStrings().SelectDelete):
|
if check_item_selected(self.list_view, UiStrings().SelectDelete):
|
||||||
items = self.list_view.selectedIndexes()
|
items = self.list_view.selectedIndexes()
|
||||||
if QtGui.QMessageBox.question(self,
|
if QtGui.QMessageBox.question(
|
||||||
UiStrings().ConfirmDelete,
|
self, UiStrings().ConfirmDelete,
|
||||||
translate('SongsPlugin.MediaItem', 'Are you sure you want to delete the %n selected song(s)?', '',
|
translate('SongsPlugin.MediaItem', 'Are you sure you want to delete the %n selected song(s)?', '',
|
||||||
QtCore.QCoreApplication.CodecForTr, len(items)),
|
QtCore.QCoreApplication.CodecForTr, len(items)),
|
||||||
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No),
|
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No),
|
||||||
@ -388,17 +387,23 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
self.edit_item = self.list_view.currentItem()
|
self.edit_item = self.list_view.currentItem()
|
||||||
item_id = self.edit_item.data(QtCore.Qt.UserRole)
|
item_id = self.edit_item.data(QtCore.Qt.UserRole)
|
||||||
old_song = self.plugin.manager.get_object(Song, item_id)
|
old_song = self.plugin.manager.get_object(Song, item_id)
|
||||||
song_xml = self.openLyrics.song_to_xml(old_song)
|
song_xml = self.open_lyrics.song_to_xml(old_song)
|
||||||
new_song = self.openLyrics.xml_to_song(song_xml)
|
new_song = self.open_lyrics.xml_to_song(song_xml)
|
||||||
new_song.title = '%s <%s>' % (new_song.title,
|
new_song.title = '%s <%s>' % \
|
||||||
translate('SongsPlugin.MediaItem', 'copy', 'For song cloning'))
|
(new_song.title, translate('SongsPlugin.MediaItem', 'copy', 'For song cloning'))
|
||||||
self.plugin.manager.save_object(new_song)
|
self.plugin.manager.save_object(new_song)
|
||||||
self.on_song_list_load()
|
self.on_song_list_load()
|
||||||
|
|
||||||
def generate_slide_data(self, service_item, item=None, xmlVersion=False,
|
def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,
|
||||||
remote=False, context=ServiceItemContext.Service):
|
context=ServiceItemContext.Service):
|
||||||
"""
|
"""
|
||||||
Generate the slide data. Needs to be implemented by the plugin.
|
Generate the slide data. Needs to be implemented by the plugin.
|
||||||
|
|
||||||
|
:param service_item: The service item to be built on
|
||||||
|
:param item: The Song item to be used
|
||||||
|
:param xml_version: The xml version (not used)
|
||||||
|
:param remote: Triggered from remote
|
||||||
|
:param context: Why is it being generated
|
||||||
"""
|
"""
|
||||||
log.debug('generate_slide_data: %s, %s, %s' % (service_item, item, self.remote_song))
|
log.debug('generate_slide_data: %s, %s, %s' % (service_item, item, self.remote_song))
|
||||||
item_id = self._get_id_of_item_to_generate(item, self.remote_song)
|
item_id = self._get_id_of_item_to_generate(item, self.remote_song)
|
||||||
@ -411,7 +416,6 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
song = self.plugin.manager.get_object(Song, item_id)
|
song = self.plugin.manager.get_object(Song, item_id)
|
||||||
service_item.theme = song.theme_name
|
service_item.theme = song.theme_name
|
||||||
service_item.edit_id = item_id
|
service_item.edit_id = item_id
|
||||||
if song.lyrics.startswith('<?xml version='):
|
|
||||||
verse_list = SongXML().get_verses(song.lyrics)
|
verse_list = SongXML().get_verses(song.lyrics)
|
||||||
# no verse list or only 1 space (in error)
|
# no verse list or only 1 space (in error)
|
||||||
verse_tags_translated = False
|
verse_tags_translated = False
|
||||||
@ -438,8 +442,8 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
if not order:
|
if not order:
|
||||||
break
|
break
|
||||||
for verse in verse_list:
|
for verse in verse_list:
|
||||||
if verse[0]['type'][0].lower() == order[0] and (verse[0]['label'].lower() == order[1:] or \
|
if verse[0]['type'][0].lower() == \
|
||||||
not order[1:]):
|
order[0] and (verse[0]['label'].lower() == order[1:] or not order[1:]):
|
||||||
if verse_tags_translated:
|
if verse_tags_translated:
|
||||||
verse_index = VerseType.from_translated_tag(verse[0]['type'])
|
verse_index = VerseType.from_translated_tag(verse[0]['type'])
|
||||||
else:
|
else:
|
||||||
@ -447,14 +451,10 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
verse_tag = VerseType.translated_tags[verse_index]
|
verse_tag = VerseType.translated_tags[verse_index]
|
||||||
verse_def = '%s%s' % (verse_tag, verse[0]['label'])
|
verse_def = '%s%s' % (verse_tag, verse[0]['label'])
|
||||||
service_item.add_from_text(verse[1], verse_def)
|
service_item.add_from_text(verse[1], verse_def)
|
||||||
else:
|
|
||||||
verses = song.lyrics.split('\n\n')
|
|
||||||
for slide in verses:
|
|
||||||
service_item.add_from_text(str(slide))
|
|
||||||
service_item.title = song.title
|
service_item.title = song.title
|
||||||
author_list = self.generate_footer(service_item, song)
|
author_list = self.generate_footer(service_item, song)
|
||||||
service_item.data_string = {'title': song.search_title, 'authors': ', '.join(author_list)}
|
service_item.data_string = {'title': song.search_title, 'authors': ', '.join(author_list)}
|
||||||
service_item.xml_version = self.openLyrics.song_to_xml(song)
|
service_item.xml_version = self.open_lyrics.song_to_xml(song)
|
||||||
# Add the audio file to the service item.
|
# Add the audio file to the service item.
|
||||||
if song.media_files:
|
if song.media_files:
|
||||||
service_item.add_capability(ItemCapabilities.HasBackgroundAudio)
|
service_item.add_capability(ItemCapabilities.HasBackgroundAudio)
|
||||||
@ -466,11 +466,8 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
Generates the song footer based on a song and adds details to a service item.
|
Generates the song footer based on a song and adds details to a service item.
|
||||||
author_list is only required for initial song generation.
|
author_list is only required for initial song generation.
|
||||||
|
|
||||||
``item``
|
:param item: The service item to be amended
|
||||||
The service item to be amended
|
:param song: The song to be used to generate the footer
|
||||||
|
|
||||||
``song``
|
|
||||||
The song to be used to generate the footer
|
|
||||||
"""
|
"""
|
||||||
author_list = [str(author.display_name) for author in song.authors]
|
author_list = [str(author.display_name) for author in song.authors]
|
||||||
item.audit = [
|
item.audit = [
|
||||||
@ -481,8 +478,8 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
item.raw_footer.append(create_separated_list(author_list))
|
item.raw_footer.append(create_separated_list(author_list))
|
||||||
item.raw_footer.append(song.copyright)
|
item.raw_footer.append(song.copyright)
|
||||||
if Settings().value('core/ccli number'):
|
if Settings().value('core/ccli number'):
|
||||||
item.raw_footer.append(translate('SongsPlugin.MediaItem', 'CCLI License: ') +
|
item.raw_footer.append(translate('SongsPlugin.MediaItem',
|
||||||
Settings().value('core/ccli number'))
|
'CCLI License: ') + Settings().value('core/ccli number'))
|
||||||
return author_list
|
return author_list
|
||||||
|
|
||||||
def service_load(self, item):
|
def service_load(self, item):
|
||||||
@ -500,8 +497,8 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
Song.search_title == (re.compile(r'\W+', re.UNICODE).sub(' ',
|
Song.search_title == (re.compile(r'\W+', re.UNICODE).sub(' ',
|
||||||
item.data_string['title'].strip()) + '@').strip().lower(), Song.search_title.asc())
|
item.data_string['title'].strip()) + '@').strip().lower(), Song.search_title.asc())
|
||||||
else:
|
else:
|
||||||
search_results = self.plugin.manager.get_all_objects(Song,
|
search_results = self.plugin.manager.get_all_objects(
|
||||||
Song.search_title == item.data_string['title'], Song.search_title.asc())
|
Song, Song.search_title == item.data_string['title'], Song.search_title.asc())
|
||||||
edit_id = 0
|
edit_id = 0
|
||||||
add_song = True
|
add_song = True
|
||||||
if search_results:
|
if search_results:
|
||||||
@ -521,16 +518,16 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
# If there's any backing tracks, copy them over.
|
# If there's any backing tracks, copy them over.
|
||||||
if item.background_audio:
|
if item.background_audio:
|
||||||
self._update_background_audio(song, item)
|
self._update_background_audio(song, item)
|
||||||
if add_song and self.addSongFromService:
|
if add_song and self.add_song_from_service:
|
||||||
song = self.openLyrics.xml_to_song(item.xml_version)
|
song = self.open_lyrics.xml_to_song(item.xml_version)
|
||||||
# If there's any backing tracks, copy them over.
|
# If there's any backing tracks, copy them over.
|
||||||
if item.background_audio:
|
if item.background_audio:
|
||||||
self._update_background_audio(song, item)
|
self._update_background_audio(song, item)
|
||||||
editId = song.id
|
edit_id = song.id
|
||||||
self.on_search_text_button_clicked()
|
self.on_search_text_button_clicked()
|
||||||
elif add_song and not self.addSongFromService:
|
elif add_song and not self.add_song_from_service:
|
||||||
# Make sure we temporary import formatting tags.
|
# Make sure we temporary import formatting tags.
|
||||||
song = self.openLyrics.xml_to_song(item.xml_version, True)
|
song = self.open_lyrics.xml_to_song(item.xml_version, True)
|
||||||
# If there's any backing tracks, copy them over.
|
# If there's any backing tracks, copy them over.
|
||||||
if item.background_audio:
|
if item.background_audio:
|
||||||
self._update_background_audio(song, item)
|
self._update_background_audio(song, item)
|
||||||
@ -540,9 +537,11 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
self.generate_footer(item, song)
|
self.generate_footer(item, song)
|
||||||
return item
|
return item
|
||||||
|
|
||||||
def search(self, string, showError):
|
def search(self, string, show_error):
|
||||||
"""
|
"""
|
||||||
Search for some songs
|
Search for some songs
|
||||||
|
:param string: The string to show
|
||||||
|
:param show_error: Is this an error?
|
||||||
"""
|
"""
|
||||||
search_results = self.search_entire(string)
|
search_results = self.search_entire(string)
|
||||||
return [[song.id, song.title] for song in search_results]
|
return [[song.id, song.title] for song in search_results]
|
||||||
|
@ -37,6 +37,7 @@ from openlp.plugins.songs.lib.songimport import SongImport
|
|||||||
|
|
||||||
VERSE_TAGS = ['V', 'C', 'B', 'O', 'P', 'I', 'E']
|
VERSE_TAGS = ['V', 'C', 'B', 'O', 'P', 'I', 'E']
|
||||||
|
|
||||||
|
|
||||||
class MediaShoutImport(SongImport):
|
class MediaShoutImport(SongImport):
|
||||||
"""
|
"""
|
||||||
The :class:`MediaShoutImport` class provides the ability to import the
|
The :class:`MediaShoutImport` class provides the ability to import the
|
||||||
@ -53,38 +54,34 @@ class MediaShoutImport(SongImport):
|
|||||||
Receive a single file to import.
|
Receive a single file to import.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
conn = pyodbc.connect('DRIVER={Microsoft Access Driver (*.mdb)};'
|
conn = pyodbc.connect('DRIVER={Microsoft Access Driver (*.mdb)};DBQ=%s;PWD=6NOZ4eHK7k' %
|
||||||
'DBQ=%s;PWD=6NOZ4eHK7k' % self.import_source)
|
self.import_source)
|
||||||
except:
|
except:
|
||||||
# Unfortunately no specific exception type
|
# Unfortunately no specific exception type
|
||||||
self.log_error(self.import_source,
|
self.log_error(self.import_source, translate('SongsPlugin.MediaShoutImport',
|
||||||
translate('SongsPlugin.MediaShoutImport', 'Unable to open the MediaShout database.'))
|
'Unable to open the MediaShout database.'))
|
||||||
return
|
return
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute('SELECT Record, Title, Author, Copyright, '
|
cursor.execute('SELECT Record, Title, Author, Copyright, SongID, CCLI, Notes FROM Songs ORDER BY Title')
|
||||||
'SongID, CCLI, Notes FROM Songs ORDER BY Title')
|
|
||||||
songs = cursor.fetchall()
|
songs = cursor.fetchall()
|
||||||
self.import_wizard.progress_bar.setMaximum(len(songs))
|
self.import_wizard.progress_bar.setMaximum(len(songs))
|
||||||
for song in songs:
|
for song in songs:
|
||||||
if self.stop_import_flag:
|
if self.stop_import_flag:
|
||||||
break
|
break
|
||||||
cursor.execute('SELECT Type, Number, Text FROM Verses '
|
cursor.execute('SELECT Type, Number, Text FROM Verses WHERE Record = %s ORDER BY Type, Number'
|
||||||
'WHERE Record = %s ORDER BY Type, Number' % song.Record)
|
% song.Record)
|
||||||
verses = cursor.fetchall()
|
verses = cursor.fetchall()
|
||||||
cursor.execute('SELECT Type, Number, POrder FROM PlayOrder '
|
cursor.execute('SELECT Type, Number, POrder FROM PlayOrder WHERE Record = %s ORDER BY POrder' % song.Record)
|
||||||
'WHERE Record = %s ORDER BY POrder' % song.Record)
|
|
||||||
verse_order = cursor.fetchall()
|
verse_order = cursor.fetchall()
|
||||||
cursor.execute('SELECT Name FROM Themes INNER JOIN SongThemes '
|
cursor.execute('SELECT Name FROM Themes INNER JOIN SongThemes ON SongThemes.ThemeId = Themes.ThemeId '
|
||||||
'ON SongThemes.ThemeId = Themes.ThemeId '
|
|
||||||
'WHERE SongThemes.Record = %s' % song.Record)
|
'WHERE SongThemes.Record = %s' % song.Record)
|
||||||
topics = cursor.fetchall()
|
topics = cursor.fetchall()
|
||||||
cursor.execute('SELECT Name FROM Groups INNER JOIN SongGroups '
|
cursor.execute('SELECT Name FROM Groups INNER JOIN SongGroups ON SongGroups.GroupId = Groups.GroupId '
|
||||||
'ON SongGroups.GroupId = Groups.GroupId '
|
|
||||||
'WHERE SongGroups.Record = %s' % song.Record)
|
'WHERE SongGroups.Record = %s' % song.Record)
|
||||||
topics += cursor.fetchall()
|
topics += cursor.fetchall()
|
||||||
self.processSong(song, verses, verse_order, topics)
|
self.process_song(song, verses, verse_order, topics)
|
||||||
|
|
||||||
def processSong(self, song, verses, verse_order, topics):
|
def process_song(self, song, verses, verse_order, topics):
|
||||||
"""
|
"""
|
||||||
Create the song, i.e. title, verse etc.
|
Create the song, i.e. title, verse etc.
|
||||||
"""
|
"""
|
||||||
|
@ -45,6 +45,7 @@ from .songimport import SongImport
|
|||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class OpenLPSongImport(SongImport):
|
class OpenLPSongImport(SongImport):
|
||||||
"""
|
"""
|
||||||
The :class:`OpenLPSongImport` class provides OpenLP with the ability to
|
The :class:`OpenLPSongImport` class provides OpenLP with the ability to
|
||||||
@ -54,20 +55,17 @@ class OpenLPSongImport(SongImport):
|
|||||||
"""
|
"""
|
||||||
Initialise the import.
|
Initialise the import.
|
||||||
|
|
||||||
``manager``
|
:param manager: The song manager for the running OpenLP installation.
|
||||||
The song manager for the running OpenLP installation.
|
:param kwargs: The database providing the data to import.
|
||||||
|
|
||||||
``source_db``
|
|
||||||
The database providing the data to import.
|
|
||||||
"""
|
"""
|
||||||
SongImport.__init__(self, manager, **kwargs)
|
SongImport.__init__(self, manager, **kwargs)
|
||||||
self.sourceSession = None
|
self.source_session = None
|
||||||
|
|
||||||
def do_import(self, progressDialog=None):
|
def do_import(self, progress_dialog=None):
|
||||||
"""
|
"""
|
||||||
Run the import for an OpenLP version 2 song database.
|
Run the import for an OpenLP version 2 song database.
|
||||||
|
|
||||||
``progressDialog``
|
``progress_dialog``
|
||||||
The QProgressDialog used when importing songs from the FRW.
|
The QProgressDialog used when importing songs from the FRW.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -77,28 +75,24 @@ class OpenLPSongImport(SongImport):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class OldBook(BaseModel):
|
class OldBook(BaseModel):
|
||||||
"""
|
"""
|
||||||
Book model
|
Book model
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class OldMediaFile(BaseModel):
|
class OldMediaFile(BaseModel):
|
||||||
"""
|
"""
|
||||||
MediaFile model
|
MediaFile model
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class OldSong(BaseModel):
|
class OldSong(BaseModel):
|
||||||
"""
|
"""
|
||||||
Song model
|
Song model
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class OldTopic(BaseModel):
|
class OldTopic(BaseModel):
|
||||||
"""
|
"""
|
||||||
Topic model
|
Topic model
|
||||||
@ -107,15 +101,15 @@ class OpenLPSongImport(SongImport):
|
|||||||
|
|
||||||
# Check the file type
|
# Check the file type
|
||||||
if not self.import_source.endswith('.sqlite'):
|
if not self.import_source.endswith('.sqlite'):
|
||||||
self.log_error(self.import_source,
|
self.log_error(self.import_source, translate('SongsPlugin.OpenLPSongImport',
|
||||||
translate('SongsPlugin.OpenLPSongImport', 'Not a valid OpenLP 2.0 song database.'))
|
'Not a valid OpenLP 2.0 song database.'))
|
||||||
return
|
return
|
||||||
self.import_source = 'sqlite:///%s' % self.import_source
|
self.import_source = 'sqlite:///%s' % self.import_source
|
||||||
# Load the db file
|
# Load the db file
|
||||||
engine = create_engine(self.import_source)
|
engine = create_engine(self.import_source)
|
||||||
source_meta = MetaData()
|
source_meta = MetaData()
|
||||||
source_meta.reflect(engine)
|
source_meta.reflect(engine)
|
||||||
self.sourceSession = scoped_session(sessionmaker(bind=engine))
|
self.source_session = scoped_session(sessionmaker(bind=engine))
|
||||||
if 'media_files' in list(source_meta.tables.keys()):
|
if 'media_files' in list(source_meta.tables.keys()):
|
||||||
has_media_files = True
|
has_media_files = True
|
||||||
else:
|
else:
|
||||||
@ -143,12 +137,11 @@ class OpenLPSongImport(SongImport):
|
|||||||
}
|
}
|
||||||
if has_media_files:
|
if has_media_files:
|
||||||
if isinstance(source_media_files_songs_table, Table):
|
if isinstance(source_media_files_songs_table, Table):
|
||||||
song_props['media_files'] = relation(OldMediaFile,
|
song_props['media_files'] = relation(OldMediaFile, backref='songs',
|
||||||
backref='songs',
|
|
||||||
secondary=source_media_files_songs_table)
|
secondary=source_media_files_songs_table)
|
||||||
else:
|
else:
|
||||||
song_props['media_files'] = relation(OldMediaFile,
|
song_props['media_files'] = \
|
||||||
backref='songs',
|
relation(OldMediaFile, backref='songs',
|
||||||
foreign_keys=[source_media_files_table.c.song_id],
|
foreign_keys=[source_media_files_table.c.song_id],
|
||||||
primaryjoin=source_songs_table.c.id == source_media_files_table.c.song_id)
|
primaryjoin=source_songs_table.c.id == source_media_files_table.c.song_id)
|
||||||
try:
|
try:
|
||||||
@ -168,7 +161,7 @@ class OpenLPSongImport(SongImport):
|
|||||||
except UnmappedClassError:
|
except UnmappedClassError:
|
||||||
mapper(OldTopic, source_topics_table)
|
mapper(OldTopic, source_topics_table)
|
||||||
|
|
||||||
source_songs = self.sourceSession.query(OldSong).all()
|
source_songs = self.source_session.query(OldSong).all()
|
||||||
if self.import_wizard:
|
if self.import_wizard:
|
||||||
self.import_wizard.progress_bar.setMaximum(len(source_songs))
|
self.import_wizard.progress_bar.setMaximum(len(source_songs))
|
||||||
for song in source_songs:
|
for song in source_songs:
|
||||||
@ -212,17 +205,17 @@ class OpenLPSongImport(SongImport):
|
|||||||
if has_media_files:
|
if has_media_files:
|
||||||
if song.media_files:
|
if song.media_files:
|
||||||
for media_file in song.media_files:
|
for media_file in song.media_files:
|
||||||
existing_media_file = self.manager.get_object_filtered(MediaFile,
|
existing_media_file = self.manager.get_object_filtered(
|
||||||
MediaFile.file_name == media_file.file_name)
|
MediaFile, MediaFile.file_name == media_file.file_name)
|
||||||
if existing_media_file:
|
if existing_media_file:
|
||||||
new_song.media_files.append(existing_media_file)
|
new_song.media_files.append(existing_media_file)
|
||||||
else:
|
else:
|
||||||
new_song.media_files.append(MediaFile.populate(file_name=media_file.file_name))
|
new_song.media_files.append(MediaFile.populate(file_name=media_file.file_name))
|
||||||
clean_song(self.manager, new_song)
|
clean_song(self.manager, new_song)
|
||||||
self.manager.save_object(new_song)
|
self.manager.save_object(new_song)
|
||||||
if progressDialog:
|
if progress_dialog:
|
||||||
progressDialog.setValue(progressDialog.value() + 1)
|
progress_dialog.setValue(progress_dialog.value() + 1)
|
||||||
progressDialog.setLabelText(WizardStrings.ImportingType % new_song.title)
|
progress_dialog.setLabelText(WizardStrings.ImportingType % new_song.title)
|
||||||
else:
|
else:
|
||||||
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % new_song.title)
|
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % new_song.title)
|
||||||
if self.stop_import_flag:
|
if self.stop_import_flag:
|
||||||
|
@ -27,8 +27,8 @@
|
|||||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
###############################################################################
|
###############################################################################
|
||||||
"""
|
"""
|
||||||
The :mod:`openlyricsexport` module provides the functionality for exporting
|
The :mod:`openlyricsexport` module provides the functionality for exporting songs from the database to the OpenLyrics
|
||||||
songs from the database to the OpenLyrics format.
|
format.
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
@ -62,7 +62,7 @@ class OpenLyricsExport(object):
|
|||||||
Export the songs.
|
Export the songs.
|
||||||
"""
|
"""
|
||||||
log.debug('started OpenLyricsExport')
|
log.debug('started OpenLyricsExport')
|
||||||
openLyrics = OpenLyrics(self.manager)
|
open_lyrics = OpenLyrics(self.manager)
|
||||||
self.parent.progress_bar.setMaximum(len(self.songs))
|
self.parent.progress_bar.setMaximum(len(self.songs))
|
||||||
for song in self.songs:
|
for song in self.songs:
|
||||||
self.application.process_events()
|
self.application.process_events()
|
||||||
@ -70,7 +70,7 @@ class OpenLyricsExport(object):
|
|||||||
return False
|
return False
|
||||||
self.parent.increment_progress_bar(translate('SongsPlugin.OpenLyricsExport', 'Exporting "%s"...') %
|
self.parent.increment_progress_bar(translate('SongsPlugin.OpenLyricsExport', 'Exporting "%s"...') %
|
||||||
song.title)
|
song.title)
|
||||||
xml = openLyrics.song_to_xml(song)
|
xml = open_lyrics.song_to_xml(song)
|
||||||
tree = etree.ElementTree(etree.fromstring(xml.encode()))
|
tree = etree.ElementTree(etree.fromstring(xml.encode()))
|
||||||
filename = '%s (%s)' % (song.title, ', '.join([author.display_name for author in song.authors]))
|
filename = '%s (%s)' % (song.title, ', '.join([author.display_name for author in song.authors]))
|
||||||
filename = clean_filename(filename)
|
filename = clean_filename(filename)
|
||||||
@ -78,8 +78,8 @@ class OpenLyricsExport(object):
|
|||||||
filename = '%s.xml' % filename[0:250 - len(self.save_path)]
|
filename = '%s.xml' % filename[0:250 - len(self.save_path)]
|
||||||
# Pass a file object, because lxml does not cope with some special
|
# Pass a file object, because lxml does not cope with some special
|
||||||
# characters in the path (see lp:757673 and lp:744337).
|
# characters in the path (see lp:757673 and lp:744337).
|
||||||
tree.write(open(os.path.join(self.save_path, filename), 'wb'),
|
tree.write(open(os.path.join(self.save_path, filename), 'wb'), encoding='utf-8', xml_declaration=True,
|
||||||
encoding='utf-8', xml_declaration=True, pretty_print=True)
|
pretty_print=True)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _get_application(self):
|
def _get_application(self):
|
||||||
|
@ -54,7 +54,7 @@ class OpenLyricsImport(SongImport):
|
|||||||
"""
|
"""
|
||||||
log.debug('initialise OpenLyricsImport')
|
log.debug('initialise OpenLyricsImport')
|
||||||
SongImport.__init__(self, manager, **kwargs)
|
SongImport.__init__(self, manager, **kwargs)
|
||||||
self.openLyrics = OpenLyrics(self.manager)
|
self.open_lyrics = OpenLyrics(self.manager)
|
||||||
|
|
||||||
def do_import(self):
|
def do_import(self):
|
||||||
"""
|
"""
|
||||||
@ -71,11 +71,11 @@ class OpenLyricsImport(SongImport):
|
|||||||
# special characters in the path (see lp:757673 and lp:744337).
|
# special characters in the path (see lp:757673 and lp:744337).
|
||||||
parsed_file = etree.parse(open(file_path, 'r'), parser)
|
parsed_file = etree.parse(open(file_path, 'r'), parser)
|
||||||
xml = etree.tostring(parsed_file).decode()
|
xml = etree.tostring(parsed_file).decode()
|
||||||
self.openLyrics.xml_to_song(xml)
|
self.open_lyrics.xml_to_song(xml)
|
||||||
except etree.XMLSyntaxError:
|
except etree.XMLSyntaxError:
|
||||||
log.exception('XML syntax error in file %s' % file_path)
|
log.exception('XML syntax error in file %s' % file_path)
|
||||||
self.log_error(file_path, SongStrings.XMLSyntaxError)
|
self.log_error(file_path, SongStrings.XMLSyntaxError)
|
||||||
except OpenLyricsError as exception:
|
except OpenLyricsError as exception:
|
||||||
log.exception('OpenLyricsException %d in file %s: %s'
|
log.exception('OpenLyricsException %d in file %s: %s' %
|
||||||
% (exception.type, file_path, exception.log_message))
|
(exception.type, file_path, exception.log_message))
|
||||||
self.log_error(file_path, exception.display_message)
|
self.log_error(file_path, exception.display_message)
|
||||||
|
@ -115,10 +115,10 @@ class OpenSongImport(SongImport):
|
|||||||
if self.stop_import_flag:
|
if self.stop_import_flag:
|
||||||
return
|
return
|
||||||
song_file = open(filename)
|
song_file = open(filename)
|
||||||
self.doImportFile(song_file)
|
self.do_import_file(song_file)
|
||||||
song_file.close()
|
song_file.close()
|
||||||
|
|
||||||
def doImportFile(self, file):
|
def do_import_file(self, file):
|
||||||
"""
|
"""
|
||||||
Process the OpenSong file - pass in a file-like object, not a file path.
|
Process the OpenSong file - pass in a file-like object, not a file path.
|
||||||
"""
|
"""
|
||||||
@ -132,7 +132,7 @@ class OpenSongImport(SongImport):
|
|||||||
root = tree.getroot()
|
root = tree.getroot()
|
||||||
if root.tag != 'song':
|
if root.tag != 'song':
|
||||||
self.log_error(file.name, str(
|
self.log_error(file.name, str(
|
||||||
translate('SongsPlugin.OpenSongImport', ('Invalid OpenSong song file. Missing song tag.'))))
|
translate('SongsPlugin.OpenSongImport', 'Invalid OpenSong song file. Missing song tag.')))
|
||||||
return
|
return
|
||||||
fields = dir(root)
|
fields = dir(root)
|
||||||
decode = {
|
decode = {
|
||||||
@ -218,7 +218,7 @@ class OpenSongImport(SongImport):
|
|||||||
for (verse_tag, verse_num, inst) in our_verse_order:
|
for (verse_tag, verse_num, inst) in our_verse_order:
|
||||||
lines = '\n'.join(verses[verse_tag][verse_num][inst])
|
lines = '\n'.join(verses[verse_tag][verse_num][inst])
|
||||||
length = 0
|
length = 0
|
||||||
while(length < len(verse_num) and verse_num[length].isnumeric()):
|
while length < len(verse_num) and verse_num[length].isnumeric():
|
||||||
length += 1
|
length += 1
|
||||||
verse_def = '%s%s' % (verse_tag, verse_num[:length])
|
verse_def = '%s%s' % (verse_tag, verse_num[:length])
|
||||||
verse_joints[verse_def] = '%s\n[---]\n%s' % (verse_joints[verse_def], lines) \
|
verse_joints[verse_def] = '%s\n[---]\n%s' % (verse_joints[verse_def], lines) \
|
||||||
@ -248,7 +248,7 @@ class OpenSongImport(SongImport):
|
|||||||
if verse_num in verses.get(verse_tag, {}):
|
if verse_num in verses.get(verse_tag, {}):
|
||||||
self.verse_order_list.append(verse_def)
|
self.verse_order_list.append(verse_def)
|
||||||
else:
|
else:
|
||||||
log.info('Got order %s but not in verse tags, dropping'
|
log.info('Got order %s but not in verse tags, dropping this item from presentation order',
|
||||||
'this item from presentation order', verse_def)
|
verse_def)
|
||||||
if not self.finish():
|
if not self.finish():
|
||||||
self.log_error(file.name)
|
self.log_error(file.name)
|
||||||
|
@ -39,6 +39,7 @@ from openlp.plugins.songs.lib.songimport import SongImport
|
|||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class PowerSongImport(SongImport):
|
class PowerSongImport(SongImport):
|
||||||
"""
|
"""
|
||||||
The :class:`PowerSongImport` class provides the ability to import song files
|
The :class:`PowerSongImport` class provides the ability to import song files
|
||||||
@ -90,7 +91,7 @@ class PowerSongImport(SongImport):
|
|||||||
Receive either a list of files or a folder (unicode) to import.
|
Receive either a list of files or a folder (unicode) to import.
|
||||||
"""
|
"""
|
||||||
from .importer import SongFormat
|
from .importer import SongFormat
|
||||||
PS_string = SongFormat.get(SongFormat.PowerSong, 'name')
|
ps_string = SongFormat.get(SongFormat.PowerSong, 'name')
|
||||||
if isinstance(self.import_source, str):
|
if isinstance(self.import_source, str):
|
||||||
if os.path.isdir(self.import_source):
|
if os.path.isdir(self.import_source):
|
||||||
dir = self.import_source
|
dir = self.import_source
|
||||||
@ -102,7 +103,7 @@ class PowerSongImport(SongImport):
|
|||||||
self.import_source = ''
|
self.import_source = ''
|
||||||
if not self.import_source or not isinstance(self.import_source, list):
|
if not self.import_source or not isinstance(self.import_source, list):
|
||||||
self.log_error(translate('SongsPlugin.PowerSongImport', 'No songs to import.'),
|
self.log_error(translate('SongsPlugin.PowerSongImport', 'No songs to import.'),
|
||||||
translate('SongsPlugin.PowerSongImport', 'No %s files found.') % PS_string)
|
translate('SongsPlugin.PowerSongImport', 'No %s files found.') % ps_string)
|
||||||
return
|
return
|
||||||
self.import_wizard.progress_bar.setMaximum(len(self.import_source))
|
self.import_wizard.progress_bar.setMaximum(len(self.import_source))
|
||||||
for file in self.import_source:
|
for file in self.import_source:
|
||||||
@ -113,15 +114,15 @@ class PowerSongImport(SongImport):
|
|||||||
with open(file, 'rb') as song_data:
|
with open(file, 'rb') as song_data:
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
label = self._readString(song_data)
|
label = self._read_string(song_data)
|
||||||
if not label:
|
if not label:
|
||||||
break
|
break
|
||||||
field = self._readString(song_data)
|
field = self._read_string(song_data)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
parse_error = True
|
parse_error = True
|
||||||
self.log_error(os.path.basename(file), str(
|
self.log_error(os.path.basename(file), str(
|
||||||
translate('SongsPlugin.PowerSongImport', 'Invalid %s file. Unexpected byte value.')) %
|
translate('SongsPlugin.PowerSongImport', 'Invalid %s file. Unexpected byte value.')) %
|
||||||
PS_string)
|
ps_string)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
if label == 'TITLE':
|
if label == 'TITLE':
|
||||||
@ -130,7 +131,7 @@ class PowerSongImport(SongImport):
|
|||||||
self.parse_author(field)
|
self.parse_author(field)
|
||||||
elif label == 'COPYRIGHTLINE':
|
elif label == 'COPYRIGHTLINE':
|
||||||
found_copyright = True
|
found_copyright = True
|
||||||
self._parseCopyrightCCLI(field)
|
self._parse_copyright_cCCLI(field)
|
||||||
elif label == 'PART':
|
elif label == 'PART':
|
||||||
self.add_verse(field)
|
self.add_verse(field)
|
||||||
if parse_error:
|
if parse_error:
|
||||||
@ -138,13 +139,13 @@ class PowerSongImport(SongImport):
|
|||||||
# Check that file had TITLE field
|
# Check that file had TITLE field
|
||||||
if not self.title:
|
if not self.title:
|
||||||
self.log_error(os.path.basename(file), str(
|
self.log_error(os.path.basename(file), str(
|
||||||
translate('SongsPlugin.PowerSongImport', 'Invalid %s file. Missing "TITLE" header.')) % PS_string)
|
translate('SongsPlugin.PowerSongImport', 'Invalid %s file. Missing "TITLE" header.')) % ps_string)
|
||||||
continue
|
continue
|
||||||
# Check that file had COPYRIGHTLINE label
|
# Check that file had COPYRIGHTLINE label
|
||||||
if not found_copyright:
|
if not found_copyright:
|
||||||
self.log_error(self.title, str(
|
self.log_error(self.title, str(
|
||||||
translate('SongsPlugin.PowerSongImport', 'Invalid %s file. Missing "COPYRIGHTLINE" header.')) %
|
translate('SongsPlugin.PowerSongImport', 'Invalid %s file. Missing "COPYRIGHTLINE" header.')) %
|
||||||
PS_string)
|
ps_string)
|
||||||
continue
|
continue
|
||||||
# Check that file had at least one verse
|
# Check that file had at least one verse
|
||||||
if not self.verses:
|
if not self.verses:
|
||||||
@ -154,14 +155,14 @@ class PowerSongImport(SongImport):
|
|||||||
if not self.finish():
|
if not self.finish():
|
||||||
self.log_error(self.title)
|
self.log_error(self.title)
|
||||||
|
|
||||||
def _readString(self, file_object):
|
def _read_string(self, file_object):
|
||||||
"""
|
"""
|
||||||
Reads in next variable-length string.
|
Reads in next variable-length string.
|
||||||
"""
|
"""
|
||||||
string_len = self._read7BitEncodedInteger(file_object)
|
string_len = self._read_7_bit_encoded_integer(file_object)
|
||||||
return str(file_object.read(string_len), 'utf-8', 'ignore')
|
return str(file_object.read(string_len), 'utf-8', 'ignore')
|
||||||
|
|
||||||
def _read7BitEncodedInteger(self, file_object):
|
def _read_7_bit_encoded_integer(self, file_object):
|
||||||
"""
|
"""
|
||||||
Reads in a 32-bit integer in compressed 7-bit format.
|
Reads in a 32-bit integer in compressed 7-bit format.
|
||||||
|
|
||||||
@ -179,7 +180,7 @@ class PowerSongImport(SongImport):
|
|||||||
# Check for corrupted stream (since max 5 bytes per 32-bit integer)
|
# Check for corrupted stream (since max 5 bytes per 32-bit integer)
|
||||||
if i == 5:
|
if i == 5:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
byte = self._readByte(file_object)
|
byte = self._read_byte(file_object)
|
||||||
# Strip high bit and shift left
|
# Strip high bit and shift left
|
||||||
val += (byte & 0x7f) << shift
|
val += (byte & 0x7f) << shift
|
||||||
shift += 7
|
shift += 7
|
||||||
@ -189,7 +190,7 @@ class PowerSongImport(SongImport):
|
|||||||
i += 1
|
i += 1
|
||||||
return val
|
return val
|
||||||
|
|
||||||
def _readByte(self, file_object):
|
def _read_byte(self, file_object):
|
||||||
"""
|
"""
|
||||||
Reads in next byte as an unsigned integer
|
Reads in next byte as an unsigned integer
|
||||||
|
|
||||||
@ -202,7 +203,7 @@ class PowerSongImport(SongImport):
|
|||||||
else:
|
else:
|
||||||
return ord(byte_str)
|
return ord(byte_str)
|
||||||
|
|
||||||
def _parseCopyrightCCLI(self, field):
|
def _parse_copyright_cCCLI(self, field):
|
||||||
"""
|
"""
|
||||||
Look for CCLI song number, and get copyright
|
Look for CCLI song number, and get copyright
|
||||||
"""
|
"""
|
||||||
|
Loading…
Reference in New Issue
Block a user