openlp/openlp/plugins/songs/lib/mediaitem.py

570 lines
28 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
2013-01-06 17:25:49 +00:00
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
2013-12-24 08:56:50 +00:00
# Copyright (c) 2008-2014 Raoul Snyman #
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
2012-11-11 21:16:14 +00:00
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
2012-10-21 13:16:22 +00:00
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import logging
import re
import os
import shutil
from PyQt4 import QtCore, QtGui
2010-12-28 21:12:20 +00:00
from sqlalchemy.sql import or_
2013-12-13 17:44:05 +00:00
from openlp.core.common import Registry, AppLocation, Settings, check_directory_exists, UiStrings, translate
2014-03-06 20:40:08 +00:00
from openlp.core.lib import MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItemContext, check_item_selected, \
create_separated_list
from openlp.core.lib.ui import create_widget_action
2013-03-07 12:34:35 +00:00
from openlp.plugins.songs.forms.editsongform import EditSongForm
from openlp.plugins.songs.forms.songmaintenanceform import SongMaintenanceForm
from openlp.plugins.songs.forms.songimportform import SongImportForm
from openlp.plugins.songs.forms.songexportform import SongExportForm
2013-06-14 20:20:26 +00:00
from openlp.plugins.songs.lib import VerseType, clean_string, delete_song
from openlp.plugins.songs.lib.db import Author, Song, Book, MediaFile
2011-02-11 21:17:56 +00:00
from openlp.plugins.songs.lib.ui import SongStrings
2013-03-07 12:34:35 +00:00
from openlp.plugins.songs.lib.xml import OpenLyrics, SongXML
2010-02-27 15:31:23 +00:00
log = logging.getLogger(__name__)
2012-11-08 21:28:42 +00:00
2011-02-12 22:01:18 +00:00
class SongSearch(object):
"""
An enumeration for song search methods.
"""
Entire = 1
Titles = 2
Lyrics = 3
Authors = 4
Books = 5
Themes = 6
2011-02-12 22:01:18 +00:00
2011-02-25 17:27:06 +00:00
class SongMediaItem(MediaManagerItem):
"""
This is the custom media manager item for Songs.
"""
2013-08-31 18:17:38 +00:00
log.info('Song Media Item loaded')
2013-03-07 13:14:31 +00:00
def __init__(self, parent, plugin):
2013-08-31 18:17:38 +00:00
self.icon_path = 'songs/song'
2013-07-18 19:38:37 +00:00
super(SongMediaItem, self).__init__(parent, plugin)
def setup_item(self):
"""
Do some additional setup.
"""
2013-03-19 22:00:50 +00:00
self.single_service_item = False
2013-04-18 09:17:00 +00:00
# Holds information about whether the edit is remotely triggered and which Song is required.
2013-04-18 09:22:20 +00:00
self.remote_song = -1
self.edit_item = None
2013-03-19 22:00:50 +00:00
self.quick_preview_allowed = True
2013-03-23 06:46:41 +00:00
self.has_search = True
2013-04-18 09:20:56 +00:00
def _update_background_audio(self, song, item):
song.media_files = []
for i, bga in enumerate(item.background_audio):
dest_file = os.path.join(
2013-08-31 18:17:38 +00:00
AppLocation.get_section_data_path(self.plugin.name), 'audio', str(song.id), os.path.split(bga)[1])
2012-07-07 14:54:14 +00:00
check_directory_exists(os.path.split(dest_file)[0])
2013-08-31 18:17:38 +00:00
shutil.copyfile(os.path.join(AppLocation.get_section_data_path('servicemanager'), bga), dest_file)
2013-04-18 09:17:00 +00:00
song.media_files.append(MediaFile.populate(weight=i, file_name=dest_file))
self.plugin.manager.save_object(song, True)
2013-03-19 22:00:50 +00:00
def add_end_header_bar(self):
self.toolbar.addSeparator()
## Song Maintenance Button ##
2014-03-06 22:05:15 +00:00
self.maintenance_action = self.toolbar.add_toolbar_action('maintenance_action',
icon=':/songs/song_maintenance.png',
triggers=self.on_song_maintenance_click)
2013-03-19 22:00:50 +00:00
self.add_search_to_toolbar()
# Signals and slots
2013-08-31 18:17:38 +00:00
Registry().register_function('songs_load_list', self.on_song_list_load)
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)
2014-03-06 22:05:15 +00:00
QtCore.QObject.connect(
self.search_text_edit, QtCore.SIGNAL('searchTypeChanged(int)'), self.on_search_text_button_clicked)
2013-03-19 22:00:50 +00:00
def add_custom_context_actions(self):
create_widget_action(self.list_view, separator=True)
2014-03-06 22:05:15 +00:00
create_widget_action(
self.list_view, text=translate('OpenLP.MediaManagerItem', '&Clone'), icon=':/general/general_clone.png',
2013-04-18 09:30:55 +00:00
triggers=self.on_clone_click)
2013-04-18 09:27:11 +00:00
def on_focus(self):
2013-03-19 22:00:50 +00:00
self.search_text_edit.setFocus()
2013-02-07 07:08:35 +00:00
def config_update(self):
2013-03-17 09:21:18 +00:00
"""
2013-04-18 09:17:00 +00:00
Is triggered when the songs config is updated
2013-03-17 09:21:18 +00:00
"""
2013-08-31 18:17:38 +00:00
log.debug('config_updated')
self.search_as_you_type = Settings().value(self.settings_section + '/search as type')
2014-03-06 22:05:15 +00:00
self.update_service_on_edit = Settings().value(self.settings_section + '/update service on edit')
self.add_song_from_service = Settings().value(self.settings_section + '/add song from service',)
2009-08-26 05:00:19 +00:00
def retranslateUi(self):
2013-08-31 18:17:38 +00:00
self.search_text_label.setText('%s:' % UiStrings().Search)
2013-03-19 22:00:50 +00:00
self.search_text_button.setText(UiStrings().Search)
2014-03-06 22:05:15 +00:00
self.maintenance_action.setText(SongStrings.SongMaintenance)
self.maintenance_action.setToolTip(translate('SongsPlugin.MediaItem',
'Maintain the lists of authors, topics and books.'))
def initialise(self):
2013-04-23 19:08:07 +00:00
"""
Initialise variables when they cannot be initialised in the constructor.
"""
2013-05-23 16:15:15 +00:00
self.song_maintenance_form = SongMaintenanceForm(self.plugin.manager, self)
self.edit_song_form = EditSongForm(self, self.main_window, self.plugin.manager)
2014-03-06 22:05:15 +00:00
self.open_lyrics = OpenLyrics(self.plugin.manager)
2013-03-19 22:00:50 +00:00
self.search_text_edit.set_search_types([
2013-08-31 18:17:38 +00:00
(SongSearch.Entire, ':/songs/song_search_all.png',
translate('SongsPlugin.MediaItem', 'Entire Song'),
translate('SongsPlugin.MediaItem', 'Search Entire Song...')),
2013-08-31 18:17:38 +00:00
(SongSearch.Titles, ':/songs/song_search_title.png',
translate('SongsPlugin.MediaItem', 'Titles'),
translate('SongsPlugin.MediaItem', 'Search Titles...')),
2013-08-31 18:17:38 +00:00
(SongSearch.Lyrics, ':/songs/song_search_lyrics.png',
translate('SongsPlugin.MediaItem', 'Lyrics'),
translate('SongsPlugin.MediaItem', 'Search Lyrics...')),
2013-08-31 18:17:38 +00:00
(SongSearch.Authors, ':/songs/song_search_author.png', SongStrings.Authors,
translate('SongsPlugin.MediaItem', 'Search Authors...')),
2013-08-31 18:17:38 +00:00
(SongSearch.Books, ':/songs/song_book_edit.png', SongStrings.SongBooks,
translate('SongsPlugin.MediaItem', 'Search Song Books...')),
2014-03-06 22:05:15 +00:00
(SongSearch.Themes, ':/slides/slide_theme.png', UiStrings().Themes, UiStrings().SearchThemes)
2010-12-28 21:12:20 +00:00
])
2013-08-31 18:17:38 +00:00
self.search_text_edit.set_current_search_type(Settings().value('%s/last search type' % self.settings_section))
2013-02-07 07:08:35 +00:00
self.config_update()
2013-03-19 22:00:50 +00:00
def on_search_text_button_clicked(self):
# Save the current search type to the configuration.
2013-08-31 18:17:38 +00:00
Settings().setValue('%s/last search type' % self.settings_section, self.search_text_edit.current_search_type())
# Reload the list considering the new search type.
2013-08-31 18:17:38 +00:00
search_keywords = str(self.search_text_edit.displayText())
2013-03-19 22:00:50 +00:00
search_type = self.search_text_edit.current_search_type()
2011-02-12 22:01:18 +00:00
if search_type == SongSearch.Entire:
2013-08-31 18:17:38 +00:00
log.debug('Entire Song Search')
2013-04-18 09:28:21 +00:00
search_results = self.search_entire(search_keywords)
self.display_results_song(search_results)
2011-02-12 22:01:18 +00:00
elif search_type == SongSearch.Titles:
2013-08-31 18:17:38 +00:00
log.debug('Titles Search')
2014-03-06 22:05:15 +00:00
search_string = '%' + clean_string(search_keywords) + '%'
search_results = self.plugin.manager.get_all_objects(Song, Song.search_title.like(search_string))
2013-04-18 09:28:21 +00:00
self.display_results_song(search_results)
2011-02-12 22:01:18 +00:00
elif search_type == SongSearch.Lyrics:
2013-08-31 18:17:38 +00:00
log.debug('Lyrics Search')
2014-03-06 22:05:15 +00:00
search_string = '%' + clean_string(search_keywords) + '%'
search_results = self.plugin.manager.get_all_objects(Song, Song.search_lyrics.like(search_string))
2013-04-18 09:28:21 +00:00
self.display_results_song(search_results)
2011-02-12 22:01:18 +00:00
elif search_type == SongSearch.Authors:
2013-08-31 18:17:38 +00:00
log.debug('Authors Search')
2014-03-06 22:05:15 +00:00
search_string = '%' + search_keywords + '%'
search_results = self.plugin.manager.get_all_objects(
Author, Author.display_name.like(search_string), Author.display_name.asc())
2013-04-18 09:28:21 +00:00
self.display_results_author(search_results)
elif search_type == SongSearch.Books:
2013-08-31 18:17:38 +00:00
log.debug('Books Search')
2014-03-06 22:05:15 +00:00
search_string = '%' + search_keywords + '%'
search_results = self.plugin.manager.get_all_objects(Book, Book.name.like(search_string), Book.name.asc())
song_number = False
if not search_results:
search_keywords = search_keywords.rpartition(' ')
2014-03-06 22:05:15 +00:00
search_string = '%' + search_keywords + '%'
search_results = self.plugin.manager.get_all_objects(Book,
2014-03-06 22:05:15 +00:00
Book.name.like(search_string), Book.name.asc())
2013-08-31 18:17:38 +00:00
song_number = re.sub(r'[^0-9]', '', search_keywords[2])
2013-04-18 09:28:21 +00:00
self.display_results_book(search_results, song_number)
2011-02-12 22:01:18 +00:00
elif search_type == SongSearch.Themes:
2013-08-31 18:17:38 +00:00
log.debug('Theme Search')
2014-03-06 22:05:15 +00:00
search_string = '%' + search_keywords + '%'
search_results = self.plugin.manager.get_all_objects(Song, Song.theme_name.like(search_string))
2013-04-18 09:28:21 +00:00
self.display_results_song(search_results)
2013-03-19 22:00:50 +00:00
self.check_search_result()
2013-04-18 09:28:21 +00:00
def search_entire(self, search_keywords):
2014-03-06 22:05:15 +00:00
search_string = '%' + clean_string(search_keywords) + '%'
return self.plugin.manager.get_all_objects(
Song, or_(Song.search_title.like(search_string), Song.search_lyrics.like(search_string),
Song.comments.like(search_string)))
2013-02-07 11:33:47 +00:00
def on_song_list_load(self):
"""
2014-03-06 22:05:15 +00:00
Handle the exit from the edit dialog and trigger remote updates of songs
"""
2013-08-31 18:17:38 +00:00
log.debug('on_song_list_load - start')
2013-04-18 09:17:00 +00:00
# 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
# manager to update items
2014-03-06 22:05:15 +00:00
if self.edit_item and self.update_service_on_edit and not self.remote_triggered:
2013-04-18 09:22:20 +00:00
item = self.build_service_item(self.edit_item)
2013-01-27 20:36:18 +00:00
self.service_manager.replace_service_item(item)
2013-03-19 22:00:50 +00:00
self.on_search_text_button_clicked()
2013-08-31 18:17:38 +00:00
log.debug('on_song_list_load - finished')
2009-06-21 17:45:59 +00:00
2014-03-06 22:05:15 +00:00
def display_results_song(self, search_results):
2013-08-31 18:17:38 +00:00
log.debug('display results Song')
2013-03-19 22:00:50 +00:00
self.save_auto_select_id()
self.list_view.clear()
2014-03-06 22:05:15 +00:00
search_results.sort(key=lambda song: song.sort_key)
for song in search_results:
2011-12-03 09:05:01 +00:00
# Do not display temporary songs
2011-12-03 19:08:02 +00:00
if song.temporary:
2011-12-03 17:01:57 +00:00
continue
2011-02-25 16:29:03 +00:00
author_list = [author.display_name for author in song.authors]
2013-08-31 18:17:38 +00:00
song_title = str(song.title)
song_detail = '%s (%s)' % (song_title, create_separated_list(author_list))
song_name = QtGui.QListWidgetItem(song_detail)
2012-05-17 15:13:09 +00:00
song_name.setData(QtCore.Qt.UserRole, song.id)
2013-03-19 22:00:50 +00:00
self.list_view.addItem(song_name)
# Auto-select the item if name has been set
2013-03-20 18:35:28 +00:00
if song.id == self.auto_select_id:
2013-03-19 22:00:50 +00:00
self.list_view.setCurrentItem(song_name)
2013-03-20 18:35:28 +00:00
self.auto_select_id = -1
2014-03-06 22:05:15 +00:00
def display_results_author(self, search_results):
2013-08-31 18:17:38 +00:00
log.debug('display results Author')
2013-03-19 22:00:50 +00:00
self.list_view.clear()
2014-03-06 22:05:15 +00:00
for author in search_results:
2009-06-21 17:45:59 +00:00
for song in author.songs:
2011-12-03 09:05:01 +00:00
# Do not display temporary songs
2011-12-03 19:08:02 +00:00
if song.temporary:
2011-12-03 17:01:57 +00:00
continue
2013-08-31 18:17:38 +00:00
song_detail = '%s (%s)' % (author.display_name, song.title)
2009-06-21 17:45:59 +00:00
song_name = QtGui.QListWidgetItem(song_detail)
2012-05-17 15:13:09 +00:00
song_name.setData(QtCore.Qt.UserRole, song.id)
2013-03-19 22:00:50 +00:00
self.list_view.addItem(song_name)
2014-03-06 22:05:15 +00:00
def display_results_book(self, search_results, song_number=False):
2013-08-31 18:17:38 +00:00
log.debug('display results Book')
2013-03-19 22:00:50 +00:00
self.list_view.clear()
2014-03-06 22:05:15 +00:00
for book in search_results:
songs = sorted(book.songs, key=lambda song: int(re.match(r'[0-9]+', '0' + song.song_number).group()))
for song in songs:
# Do not display temporary songs
if song.temporary:
continue
if song_number and not song_number in song.song_number:
continue
2013-08-31 18:17:38 +00:00
song_detail = '%s - %s (%s)' % (book.name, song.song_number, song.title)
song_name = QtGui.QListWidgetItem(song_detail)
2012-05-17 15:13:09 +00:00
song_name.setData(QtCore.Qt.UserRole, song.id)
2013-03-19 22:00:50 +00:00
self.list_view.addItem(song_name)
2009-06-21 17:45:59 +00:00
2013-04-18 17:10:26 +00:00
def on_clear_text_button_click(self):
"""
Clear the search text.
"""
2013-03-19 22:00:50 +00:00
self.search_text_edit.clear()
self.on_search_text_button_clicked()
2013-03-19 22:00:50 +00:00
def on_search_text_edit_changed(self, text):
2009-11-03 06:15:35 +00:00
"""
2013-04-18 09:17:00 +00:00
If search as type enabled invoke the search on each key press. If the Lyrics are being searched do not start
till 7 characters have been entered.
2009-11-03 06:15:35 +00:00
"""
2013-04-18 09:27:11 +00:00
if self.search_as_you_type:
2009-10-17 05:47:17 +00:00
search_length = 1
2013-03-19 22:00:50 +00:00
if self.search_text_edit.current_search_type() == SongSearch.Entire:
2011-03-11 18:03:16 +00:00
search_length = 4
2013-03-19 22:00:50 +00:00
elif self.search_text_edit.current_search_type() == SongSearch.Lyrics:
2011-03-11 18:03:16 +00:00
search_length = 3
2009-10-17 05:47:17 +00:00
if len(text) > search_length:
2013-03-19 22:00:50 +00:00
self.on_search_text_button_clicked()
elif not text:
2013-04-18 17:10:26 +00:00
self.on_clear_text_button_click()
2013-03-19 20:05:13 +00:00
def on_import_click(self):
2013-08-31 18:17:38 +00:00
if not hasattr(self, 'import_wizard'):
2013-03-07 08:05:43 +00:00
self.import_wizard = SongImportForm(self, self.plugin)
self.import_wizard.exec_()
# Run song load as list may have been cancelled but some songs loaded
2013-08-31 18:17:38 +00:00
Registry().execute('songs_load_list')
2010-04-02 12:23:40 +00:00
2013-03-19 20:05:13 +00:00
def on_export_click(self):
2014-03-06 22:05:15 +00:00
if not hasattr(self, 'export_wizard'):
self.export_wizard = SongExportForm(self, self.plugin)
self.export_wizard.exec_()
2011-01-23 16:05:46 +00:00
2013-03-19 22:00:50 +00:00
def on_new_click(self):
2013-08-31 18:17:38 +00:00
log.debug('on_new_click')
2013-04-18 09:20:56 +00:00
self.edit_song_form.new_song()
self.edit_song_form.exec_()
2013-04-18 17:10:26 +00:00
self.on_clear_text_button_click()
2013-03-19 22:00:50 +00:00
self.on_selection_change()
2013-03-20 18:35:28 +00:00
self.auto_select_id = -1
2013-04-18 09:30:55 +00:00
def on_song_maintenance_click(self):
2013-04-18 09:22:20 +00:00
self.song_maintenance_form.exec_()
2013-04-18 09:32:48 +00:00
def on_remote_edit(self, song_id, preview=False):
2009-11-03 06:15:35 +00:00
"""
2013-04-18 09:17:00 +00:00
Called by ServiceManager or SlideController by event passing the Song Id in the payload along with an indicator
to say which type of display is required.
2009-11-03 06:15:35 +00:00
"""
2013-08-31 18:17:38 +00:00
log.debug('on_remote_edit for song %s' % song_id)
2011-04-10 18:44:27 +00:00
song_id = int(song_id)
2011-05-28 09:53:37 +00:00
valid = self.plugin.manager.get_object(Song, song_id)
2009-11-03 19:01:53 +00:00
if valid:
2013-04-18 09:20:56 +00:00
self.edit_song_form.load_song(song_id, preview)
if self.edit_song_form.exec_() == QtGui.QDialog.Accepted:
2013-03-20 18:35:28 +00:00
self.auto_select_id = -1
2013-02-07 11:33:47 +00:00
self.on_song_list_load()
2013-04-18 09:22:20 +00:00
self.remote_song = song_id
2013-03-19 22:00:50 +00:00
self.remote_triggered = True
item = self.build_service_item(remote=True)
2013-04-18 09:22:20 +00:00
self.remote_song = -1
2013-03-19 22:00:50 +00:00
self.remote_triggered = None
2013-01-27 09:57:03 +00:00
if item:
return item
return None
2009-10-29 09:18:26 +00:00
2013-03-19 22:00:50 +00:00
def on_edit_click(self):
"""
Edit a song
"""
2013-08-31 18:17:38 +00:00
log.debug('on_edit_click')
2013-03-19 22:00:50 +00:00
if check_item_selected(self.list_view, UiStrings().SelectEdit):
2013-04-18 09:22:20 +00:00
self.edit_item = self.list_view.currentItem()
item_id = self.edit_item.data(QtCore.Qt.UserRole)
2013-04-18 09:20:56 +00:00
self.edit_song_form.load_song(item_id, False)
self.edit_song_form.exec_()
2013-03-20 18:35:28 +00:00
self.auto_select_id = -1
2013-02-07 11:33:47 +00:00
self.on_song_list_load()
2013-04-18 09:22:20 +00:00
self.edit_item = None
2013-03-19 22:00:50 +00:00
def on_delete_click(self):
"""
Remove a song from the list and database
"""
2013-03-19 22:00:50 +00:00
if check_item_selected(self.list_view, UiStrings().SelectDelete):
items = self.list_view.selectedIndexes()
2014-03-06 22:05:15 +00:00
if QtGui.QMessageBox.question(
self, UiStrings().ConfirmDelete,
2013-04-18 09:17:00 +00:00
translate('SongsPlugin.MediaItem', 'Are you sure you want to delete the %n selected song(s)?', '',
2014-03-06 22:05:15 +00:00
QtCore.QCoreApplication.CodecForTr, len(items)),
2013-04-18 09:17:00 +00:00
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No),
QtGui.QMessageBox.Yes) == QtGui.QMessageBox.No:
2010-04-04 13:53:39 +00:00
return
2013-02-03 19:23:12 +00:00
self.application.set_busy_cursor()
2013-03-16 11:05:52 +00:00
self.main_window.display_progress_bar(len(items))
2010-04-03 19:17:37 +00:00
for item in items:
2012-05-19 09:13:32 +00:00
item_id = item.data(QtCore.Qt.UserRole)
2013-06-14 20:20:26 +00:00
delete_song(item_id, self.plugin)
2013-03-07 08:05:43 +00:00
self.main_window.increment_progress_bar()
2013-03-16 11:05:52 +00:00
self.main_window.finished_progress_bar()
2013-02-03 19:23:12 +00:00
self.application.set_normal_cursor()
2013-03-19 22:00:50 +00:00
self.on_search_text_button_clicked()
2013-04-18 09:30:55 +00:00
def on_clone_click(self):
"""
Clone a Song
"""
2013-08-31 18:17:38 +00:00
log.debug('on_clone_click')
2013-03-19 22:00:50 +00:00
if check_item_selected(self.list_view, UiStrings().SelectEdit):
2013-04-18 09:22:20 +00:00
self.edit_item = self.list_view.currentItem()
item_id = self.edit_item.data(QtCore.Qt.UserRole)
old_song = self.plugin.manager.get_object(Song, item_id)
2014-03-06 22:05:15 +00:00
song_xml = self.open_lyrics.song_to_xml(old_song)
new_song = self.open_lyrics.xml_to_song(song_xml)
new_song.title = '%s <%s>' % \
(new_song.title, translate('SongsPlugin.MediaItem', 'copy', 'For song cloning'))
self.plugin.manager.save_object(new_song)
2013-02-07 11:33:47 +00:00
self.on_song_list_load()
2014-03-06 22:05:15 +00:00
def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,
context=ServiceItemContext.Service):
2013-03-19 22:00:50 +00:00
"""
Generate the slide data. Needs to be implemented by the plugin.
2014-03-06 22:05:15 +00:00
: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
2013-03-19 22:00:50 +00:00
"""
2013-08-31 18:17:38 +00:00
log.debug('generate_slide_data: %s, %s, %s' % (service_item, item, self.remote_song))
2013-04-18 09:22:20 +00:00
item_id = self._get_id_of_item_to_generate(item, self.remote_song)
2011-08-28 17:45:13 +00:00
service_item.add_capability(ItemCapabilities.CanEdit)
service_item.add_capability(ItemCapabilities.CanPreview)
service_item.add_capability(ItemCapabilities.CanLoop)
2010-09-30 05:12:06 +00:00
service_item.add_capability(ItemCapabilities.OnLoadUpdate)
service_item.add_capability(ItemCapabilities.AddIfNewItem)
service_item.add_capability(ItemCapabilities.CanSoftBreak)
2011-05-28 09:53:37 +00:00
song = self.plugin.manager.get_object(Song, item_id)
service_item.theme = song.theme_name
service_item.edit_id = item_id
2014-03-06 22:05:15 +00:00
verse_list = SongXML().get_verses(song.lyrics)
# no verse list or only 1 space (in error)
verse_tags_translated = False
if VerseType.from_translated_string(str(verse_list[0][0]['type'])) is not None:
verse_tags_translated = True
if not song.verse_order.strip():
for verse in verse_list:
# We cannot use from_loose_input() here, because database is supposed to contain English lowercase
# singlechar tags.
verse_tag = verse[0]['type']
verse_index = None
if len(verse_tag) > 1:
verse_index = VerseType.from_translated_string(verse_tag)
if verse_index is None:
2014-03-06 22:05:15 +00:00
verse_index = VerseType.from_string(verse_tag, None)
if verse_index is None:
verse_index = VerseType.from_tag(verse_tag)
verse_tag = VerseType.translated_tags[verse_index].upper()
verse_def = '%s%s' % (verse_tag, verse[0]['label'])
service_item.add_from_text(str(verse[1]), verse_def)
else:
2014-03-06 22:05:15 +00:00
# Loop through the verse list and expand the song accordingly.
for order in song.verse_order.lower().split():
if not order:
break
for verse in verse_list:
if verse[0]['type'][0].lower() == \
order[0] and (verse[0]['label'].lower() == order[1:] or not order[1:]):
if verse_tags_translated:
verse_index = VerseType.from_translated_tag(verse[0]['type'])
else:
verse_index = VerseType.from_tag(verse[0]['type'])
verse_tag = VerseType.translated_tags[verse_index]
verse_def = '%s%s' % (verse_tag, verse[0]['label'])
service_item.add_from_text(verse[1], verse_def)
service_item.title = song.title
2013-04-21 15:53:51 +00:00
author_list = self.generate_footer(service_item, song)
2013-08-31 18:17:38 +00:00
service_item.data_string = {'title': song.search_title, 'authors': ', '.join(author_list)}
2014-03-06 22:05:15 +00:00
service_item.xml_version = self.open_lyrics.song_to_xml(song)
2011-08-28 17:45:13 +00:00
# Add the audio file to the service item.
if song.media_files:
2011-08-28 17:45:13 +00:00
service_item.add_capability(ItemCapabilities.HasBackgroundAudio)
2013-01-06 17:25:49 +00:00
service_item.background_audio = [m.file_name for m in song.media_files]
2010-07-27 09:32:52 +00:00
return True
2010-09-30 05:12:06 +00:00
2013-04-21 15:53:51 +00:00
def generate_footer(self, item, song):
"""
Generates the song footer based on a song and adds details to a service item.
2014-03-06 22:05:15 +00:00
:param item: The service item to be amended
:param song: The song to be used to generate the footer
:return List of all authors (only required for initial song generation)
2013-04-21 15:53:51 +00:00
"""
authors_words = []
authors_music = []
authors_translation = []
authors_none = []
for author_song in song.authors_songs:
if author_song.author_type == Author.TYPE_WORDS:
authors_words.append(author_song.author.display_name)
elif author_song.author_type == Author.TYPE_MUSIC:
authors_music.append(author_song.author.display_name)
elif author_song.author_type == Author.TYPE_TRANSLATION:
authors_translation.append(author_song.author.display_name)
else:
authors_none.append(author_song.author.display_name)
authors_all = authors_words + authors_music + authors_translation + authors_none
2013-04-21 15:53:51 +00:00
item.audit = [
song.title, authors_all, song.copyright, str(song.ccli_number)
2013-04-21 15:53:51 +00:00
]
item.raw_footer = []
item.raw_footer.append(song.title)
if authors_none:
2014-03-29 10:08:52 +00:00
item.raw_footer.append("%s: %s"%(translate('OpenLP.Ui', 'Written by'), create_separated_list(authors_none)))
if authors_words:
item.raw_footer.append("%s: %s"%(Author.Types[Author.TYPE_WORDS], create_separated_list(authors_words)))
if authors_music:
item.raw_footer.append("%s: %s"%(Author.Types[Author.TYPE_MUSIC], create_separated_list(authors_music)))
if authors_translation:
item.raw_footer.append("%s: %s"%(Author.Types[Author.TYPE_TRANSLATION], create_separated_list(authors_translation)))
if not authors_all: #No authors defined
item.raw_footer.append(SongStrings.AuthorUnknown)
2013-04-21 15:53:51 +00:00
item.raw_footer.append(song.copyright)
2013-08-31 18:17:38 +00:00
if Settings().value('core/ccli number'):
2014-03-06 22:05:15 +00:00
item.raw_footer.append(translate('SongsPlugin.MediaItem',
'CCLI License: ') + Settings().value('core/ccli number'))
return authors_all
2013-04-21 15:53:51 +00:00
2013-03-19 22:00:50 +00:00
def service_load(self, item):
2010-09-30 05:12:06 +00:00
"""
2011-03-14 18:59:59 +00:00
Triggered by a song being loaded by the service manager.
2010-09-30 05:12:06 +00:00
"""
2013-08-31 18:17:38 +00:00
log.debug('service_load')
2011-02-05 23:52:05 +00:00
if self.plugin.status != PluginStatus.Active or not item.data_string:
return
2013-08-31 18:17:38 +00:00
if item.data_string['title'].find('@') == -1:
2013-04-18 09:17:00 +00:00
# FIXME: This file seems to be an old one (prior to 1.9.5), which means, that the search title
# (data_string[u'title']) is probably wrong. We add "@" to search title and hope that we do not add any
# duplicate. This should work for songs without alternate title.
2014-03-21 21:38:08 +00:00
temp = (re.compile(r'\W+', re.UNICODE).sub(' ', item.data_string['title'].strip()) + '@').strip().lower()
search_results = \
self.plugin.manager.get_all_objects(Song, Song.search_title == temp, Song.search_title.asc())
else:
2014-03-06 22:05:15 +00:00
search_results = self.plugin.manager.get_all_objects(
Song, Song.search_title == item.data_string['title'], Song.search_title.asc())
2013-04-21 15:53:51 +00:00
edit_id = 0
2011-02-05 23:52:05 +00:00
add_song = True
if search_results:
for song in search_results:
2013-08-31 18:17:38 +00:00
author_list = item.data_string['authors']
2011-02-05 23:52:05 +00:00
same_authors = True
for author in song.authors:
if author.display_name in author_list:
2013-08-31 18:17:38 +00:00
author_list = author_list.replace(author.display_name, '', 1)
else:
same_authors = False
break
2013-08-31 18:17:38 +00:00
if same_authors and author_list.strip(', ') == '':
2011-02-05 23:52:05 +00:00
add_song = False
2013-04-21 15:53:51 +00:00
edit_id = song.id
2011-02-05 23:52:05 +00:00
break
# If there's any backing tracks, copy them over.
if item.background_audio:
2013-04-18 09:20:56 +00:00
self._update_background_audio(song, item)
2014-03-06 22:05:15 +00:00
if add_song and self.add_song_from_service:
song = self.open_lyrics.xml_to_song(item.xml_version)
# If there's any backing tracks, copy them over.
if item.background_audio:
2013-04-18 09:20:56 +00:00
self._update_background_audio(song, item)
2014-03-06 22:05:15 +00:00
edit_id = song.id
2013-03-19 22:00:50 +00:00
self.on_search_text_button_clicked()
2014-03-06 22:05:15 +00:00
elif add_song and not self.add_song_from_service:
# Make sure we temporary import formatting tags.
2014-03-06 22:05:15 +00:00
song = self.open_lyrics.xml_to_song(item.xml_version, True)
2011-12-05 19:47:50 +00:00
# If there's any backing tracks, copy them over.
if item.background_audio:
2013-04-18 09:20:56 +00:00
self._update_background_audio(song, item)
2013-04-21 15:53:51 +00:00
edit_id = song.id
# Update service with correct song id and return it to caller.
item.edit_id = edit_id
self.generate_footer(item, song)
return item
2010-12-09 15:08:04 +00:00
2014-03-06 22:05:15 +00:00
def search(self, string, show_error):
"""
Search for some songs
2014-03-06 22:05:15 +00:00
:param string: The string to show
:param show_error: Is this an error?
"""
2013-04-18 09:28:21 +00:00
search_results = self.search_entire(string)
return [[song.id, song.title] for song in search_results]