openlp/openlp/core/lib/serviceitem.py

505 lines
18 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
2011-12-27 10:33:55 +00:00
# Copyright (c) 2008-2012 Raoul Snyman #
# Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan #
2011-05-26 16:25:54 +00:00
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
2011-05-26 17:11:22 +00:00
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
2011-06-12 16:02:52 +00:00
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
2011-06-12 15:41:01 +00:00
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
# --------------------------------------------------------------------------- #
# 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 #
###############################################################################
2010-06-10 21:30:50 +00:00
"""
The :mod:`serviceitem` provides the service item functionality including the
type and capability of an item.
"""
2012-05-20 19:11:27 +00:00
import time
2011-05-24 15:06:38 +00:00
import cgi
2011-02-13 10:37:27 +00:00
import datetime
2009-05-02 11:16:08 +00:00
import logging
import os
2009-11-04 17:48:46 +00:00
import uuid
2009-07-10 13:16:15 +00:00
2011-07-10 11:59:23 +00:00
from openlp.core.lib import build_icon, clean_tags, expand_tags, translate
2009-07-10 13:16:15 +00:00
2010-02-27 15:31:23 +00:00
log = logging.getLogger(__name__)
2012-05-20 19:11:27 +00:00
COUNT = 0
TIME_ = datetime.timedelta()
class ServiceItemType(object):
2009-09-29 04:58:26 +00:00
"""
Defines the type of service item
"""
Text = 1
Image = 2
Command = 3
2010-04-03 07:10:31 +00:00
class ItemCapabilities(object):
2010-06-10 21:30:50 +00:00
"""
Provides an enumeration of a serviceitem's capabilities
"""
2011-08-28 17:45:13 +00:00
CanPreview = 1
CanEdit = 2
CanMaintain = 3
2010-05-24 22:37:20 +00:00
RequiresMedia = 4
2011-08-28 17:45:13 +00:00
CanLoop = 5
CanAppend = 6
2010-08-03 19:23:05 +00:00
NoLineBreaks = 7
2010-09-30 05:12:06 +00:00
OnLoadUpdate = 8
AddIfNewItem = 9
ProvidesOwnDisplay = 10
2011-08-28 17:45:13 +00:00
HasDetailedTitleDisplay = 11
HasVariableStartTime = 12
CanSoftBreak = 13
CanWordSplit = 14
2011-08-28 17:45:13 +00:00
HasBackgroundAudio = 15
2010-04-03 07:10:31 +00:00
2011-01-12 19:31:46 +00:00
2009-07-10 13:16:15 +00:00
class ServiceItem(object):
"""
The service item is a base class for the plugins to use to interact with
the service manager, the slide controller, and the projection screen
compositor.
"""
2009-05-02 11:16:08 +00:00
log.info(u'Service Item created')
2009-11-14 08:40:14 +00:00
def __init__(self, plugin=None):
"""
2009-07-10 13:16:15 +00:00
Set up the service item.
2009-11-14 08:40:14 +00:00
``plugin``
2009-07-10 13:16:15 +00:00
The plugin that this service item belongs to.
"""
2009-11-14 08:40:14 +00:00
if plugin:
2011-03-28 18:56:39 +00:00
self.renderer = plugin.renderer
2009-11-14 08:40:14 +00:00
self.name = plugin.name
self.title = u''
2010-06-12 20:22:58 +00:00
self.shortname = u''
self.audit = u''
2009-05-02 11:16:08 +00:00
self.items = []
self.iconic_representation = None
2011-03-04 17:31:23 +00:00
self.raw_footer = []
self.foot_text = u''
2009-05-02 11:16:08 +00:00
self.theme = None
self.service_item_type = None
2009-11-14 09:06:25 +00:00
self._raw_frames = []
self._display_frames = []
2010-08-02 19:08:55 +00:00
self._uuid = 0
2010-03-02 19:44:55 +00:00
self.notes = u''
2010-03-28 15:56:49 +00:00
self.from_plugin = False
2010-04-03 07:10:31 +00:00
self.capabilities = []
2010-05-07 18:29:17 +00:00
self.is_valid = True
2010-06-12 20:22:58 +00:00
self.icon = None
2010-07-17 08:59:15 +00:00
self.themedata = None
2010-07-25 08:58:08 +00:00
self.main = None
self.footer = None
2010-09-16 21:19:51 +00:00
self.bg_image_bytes = None
2010-09-30 17:07:27 +00:00
self.search_string = u''
self.data_string = u''
self.edit_id = None
self.xml_version = None
2011-02-13 13:11:15 +00:00
self.start_time = 0
2011-03-19 11:50:48 +00:00
self.end_time = 0
2011-02-14 17:54:09 +00:00
self.media_length = 0
self.from_service = False
2011-08-20 11:45:06 +00:00
self.image_border = u'#000000'
2011-08-28 17:45:13 +00:00
self.background_audio = []
2011-11-04 20:09:35 +00:00
self.theme_overwritten = False
2011-12-10 08:45:17 +00:00
self.temporary_edit = False
self._new_item()
2010-04-02 19:02:38 +00:00
2010-08-02 19:08:55 +00:00
def _new_item(self):
2010-08-21 08:24:14 +00:00
"""
Method to set the internal id of the item. This is used to compare
service items to see if they are the same.
2010-08-21 08:24:14 +00:00
"""
2010-08-02 19:23:39 +00:00
self._uuid = unicode(uuid.uuid1())
2010-08-02 19:08:55 +00:00
2010-04-03 07:10:31 +00:00
def add_capability(self, capability):
2010-06-10 21:30:50 +00:00
"""
Add an ItemCapability to a ServiceItem
``capability``
The capability to add
"""
2010-04-03 07:10:31 +00:00
self.capabilities.append(capability)
2010-04-02 19:02:38 +00:00
2010-04-03 07:10:31 +00:00
def is_capable(self, capability):
2010-06-10 21:30:50 +00:00
"""
Tell the caller if a ServiceItem has a capability
``capability``
The capability to test for
"""
2010-04-03 07:10:31 +00:00
return capability in self.capabilities
2009-05-02 11:16:08 +00:00
2010-06-12 20:22:58 +00:00
def add_icon(self, icon):
2009-07-10 13:16:15 +00:00
"""
Add an icon to the service item. This is used when displaying the
service item in the service manager.
``icon``
2010-07-10 01:21:03 +00:00
A string to an icon in the resources or on disk.
2009-07-10 13:16:15 +00:00
"""
2009-06-08 19:06:42 +00:00
self.icon = icon
self.iconic_representation = build_icon(icon)
2012-05-20 19:11:27 +00:00
def render(self, provides_own_theme_data=False):
"""
2010-07-14 17:36:48 +00:00
The render method is what generates the frames for the screen and
2011-10-16 14:40:46 +00:00
obtains the display information from the renderer. At this point all
slides are built for the given display size.
"""
start = time.time()
2009-05-02 11:16:08 +00:00
log.debug(u'Render called')
2009-11-14 09:06:25 +00:00
self._display_frames = []
2010-09-16 21:19:51 +00:00
self.bg_image_bytes = None
2012-05-20 19:11:27 +00:00
if not provides_own_theme_data:
self.renderer.set_override_theme(self.theme)
self.themedata, self.main, self.footer = self.renderer.post_render()
if self.service_item_type == ServiceItemType.Text:
2009-09-13 07:39:48 +00:00
log.debug(u'Formatting slides')
2009-11-14 09:06:25 +00:00
for slide in self._raw_frames:
2011-06-13 08:16:50 +00:00
pages = self.renderer.format_slide(slide[u'raw_slide'], self)
for page in pages:
page = page.replace(u'<br>', u'{br}')
html = expand_tags(cgi.escape(page.rstrip()))
2011-04-26 17:03:19 +00:00
self._display_frames.append({
u'title': clean_tags(page),
2010-08-21 19:17:56 +00:00
u'text': clean_tags(page.rstrip()),
u'html': html.replace(u'&amp;nbsp;', u'&nbsp;'),
2011-04-26 17:03:19 +00:00
u'verseTag': slide[u'verseTag']
})
2010-10-23 17:37:10 +00:00
elif self.service_item_type == ServiceItemType.Image or \
self.service_item_type == ServiceItemType.Command:
pass
else:
log.error(u'Invalid value renderer: %s' % self.service_item_type)
2010-08-24 17:12:47 +00:00
self.title = clean_tags(self.title)
2011-03-19 16:06:04 +00:00
# The footer should never be None, but to be compatible with a few
2011-03-19 16:11:52 +00:00
# nightly builds between 1.9.4 and 1.9.5, we have to correct this to
# avoid tracebacks.
2011-03-13 15:01:01 +00:00
if self.raw_footer is None:
self.raw_footer = []
2012-05-02 18:25:37 +00:00
self.foot_text = u'<br>'.join(filter(None, self.raw_footer))
2012-05-20 19:11:27 +00:00
global COUNT
COUNT += 1
global TIME_
TIME_ += datetime.timedelta(seconds=(time.time() - start))
print u'%s (average %s)' % (
unicode(datetime.timedelta(seconds=(time.time() - start))),
unicode(TIME_ / COUNT))
2011-08-20 11:45:06 +00:00
def add_from_image(self, path, title, background=None):
2009-07-10 13:16:15 +00:00
"""
Add an image slide to the service item.
``path``
The directory in which the image file is located.
2009-11-14 08:40:14 +00:00
``title``
2009-07-10 13:16:15 +00:00
A title for the slide in the service item.
"""
2011-08-20 11:45:06 +00:00
if background:
self.image_border = background
self.service_item_type = ServiceItemType.Image
2011-03-02 18:20:47 +00:00
self._raw_frames.append({u'title': title, u'path': path})
2011-08-20 11:45:06 +00:00
self.renderer.imageManager.add_image(title, path, u'image',
self.image_border)
2010-08-02 19:08:55 +00:00
self._new_item()
2010-06-12 20:22:58 +00:00
def add_from_text(self, title, raw_slide, verse_tag=None):
2009-07-10 13:16:15 +00:00
"""
Add a text slide to the service item.
``frame_title``
The title of the slide in the service item.
``raw_slide``
The raw text of the slide.
"""
2011-05-11 22:32:25 +00:00
if verse_tag:
verse_tag = verse_tag.upper()
self.service_item_type = ServiceItemType.Text
2009-11-14 08:40:14 +00:00
title = title.split(u'\n')[0]
2009-11-14 09:06:25 +00:00
self._raw_frames.append(
2010-11-04 16:26:30 +00:00
{u'title': title, u'raw_slide': raw_slide, u'verseTag': verse_tag})
2010-08-02 19:08:55 +00:00
self._new_item()
2009-11-14 08:40:14 +00:00
def add_from_command(self, path, file_name, image):
2009-07-10 13:16:15 +00:00
"""
Add a slide from a command.
2009-11-14 08:40:14 +00:00
``path``
2009-07-10 13:16:15 +00:00
The title of the slide in the service item.
2009-11-14 08:40:14 +00:00
``file_name``
2009-07-10 13:16:15 +00:00
The title of the slide in the service item.
``image``
2009-07-10 13:16:15 +00:00
The command of/for the slide.
"""
self.service_item_type = ServiceItemType.Command
2009-11-14 09:06:25 +00:00
self._raw_frames.append(
2010-11-04 16:26:30 +00:00
{u'title': file_name, u'image': image, u'path': path})
2010-08-02 19:08:55 +00:00
self._new_item()
2009-05-02 11:16:08 +00:00
2009-09-19 21:45:50 +00:00
def get_service_repr(self):
"""
2009-09-19 21:45:50 +00:00
This method returns some text which can be saved into the service
2009-07-10 13:16:15 +00:00
file to represent this item.
"""
2009-09-19 21:45:50 +00:00
service_header = {
u'name': self.name,
2009-11-14 08:40:14 +00:00
u'plugin': self.name,
2010-11-04 16:26:30 +00:00
u'theme': self.theme,
u'title': self.title,
u'icon': self.icon,
u'footer': self.raw_footer,
u'type': self.service_item_type,
u'audit': self.audit,
u'notes': self.notes,
u'from_plugin': self.from_plugin,
u'capabilities': self.capabilities,
u'search': self.search_string,
u'data': self.data_string,
u'xml_version': self.xml_version,
2011-02-14 17:54:09 +00:00
u'start_time': self.start_time,
2011-03-19 11:50:48 +00:00
u'end_time': self.end_time,
2011-08-28 17:45:13 +00:00
u'media_length': self.media_length,
2011-11-04 20:09:35 +00:00
u'background_audio': self.background_audio,
u'theme_overwritten': self.theme_overwritten
2009-07-10 13:16:15 +00:00
}
2009-09-19 21:45:50 +00:00
service_data = []
if self.service_item_type == ServiceItemType.Text:
service_data = [slide for slide in self._raw_frames]
elif self.service_item_type == ServiceItemType.Image:
service_data = [slide[u'title'] for slide in self._raw_frames]
elif self.service_item_type == ServiceItemType.Command:
2009-11-14 09:06:25 +00:00
for slide in self._raw_frames:
2010-05-26 16:01:45 +00:00
service_data.append(
2010-11-04 16:26:30 +00:00
{u'title': slide[u'title'], u'image': slide[u'image']})
2009-09-19 21:45:50 +00:00
return {u'header': service_header, u'data': service_data}
2009-09-19 21:45:50 +00:00
def set_from_service(self, serviceitem, path=None):
"""
2009-07-10 13:16:15 +00:00
This method takes a service item from a saved service file (passed
from the ServiceManager) and extracts the data actually required.
``serviceitem``
The item to extract data from.
``path``
Defaults to *None*. Any path data, usually for images.
"""
2012-03-17 14:44:23 +00:00
log.debug(u'set_from_service called with path %s' % path)
header = serviceitem[u'serviceitem'][u'header']
self.title = header[u'title']
2009-09-05 13:30:09 +00:00
self.name = header[u'name']
self.service_item_type = header[u'type']
self.shortname = header[u'plugin']
self.theme = header[u'theme']
2010-06-12 20:22:58 +00:00
self.add_icon(header[u'icon'])
self.raw_footer = header[u'footer']
self.audit = header[u'audit']
2010-03-02 19:44:55 +00:00
self.notes = header[u'notes']
2010-03-28 15:56:49 +00:00
self.from_plugin = header[u'from_plugin']
2010-04-03 07:10:31 +00:00
self.capabilities = header[u'capabilities']
2010-09-30 17:07:27 +00:00
# Added later so may not be present in older services.
2012-05-05 12:24:25 +00:00
self.search_string = header.get(u'search', u'')
self.data_string = header.get(u'data', u'')
self.xml_version = header.get(u'xml_version')
self.start_time = header.get(u'start_time', 0)
self.end_time = header.get(u'end_time', 0)
self.media_length = header.get(u'media_length', 0)
2011-08-28 17:45:13 +00:00
if u'background_audio' in header:
self.background_audio = []
for filename in header[u'background_audio']:
# Give them real file paths
self.background_audio.append(os.path.join(path, filename))
2011-11-13 08:57:29 +00:00
self.theme_overwritten = header.get(u'theme_overwritten', False)
if self.service_item_type == ServiceItemType.Text:
for slide in serviceitem[u'serviceitem'][u'data']:
2009-11-14 09:06:25 +00:00
self._raw_frames.append(slide)
elif self.service_item_type == ServiceItemType.Image:
for text_image in serviceitem[u'serviceitem'][u'data']:
filename = os.path.join(path, text_image)
2010-10-23 17:37:10 +00:00
self.add_from_image(filename, text_image)
elif self.service_item_type == ServiceItemType.Command:
2009-08-31 18:27:36 +00:00
for text_image in serviceitem[u'serviceitem'][u'data']:
filename = os.path.join(path, text_image[u'title'])
2010-05-26 16:01:45 +00:00
self.add_from_command(
2011-02-16 03:28:06 +00:00
path, text_image[u'title'], text_image[u'image'])
self._new_item()
2009-11-05 17:03:37 +00:00
2011-01-29 07:53:21 +00:00
def get_display_title(self):
2011-01-28 19:46:55 +00:00
"""
Returns the title of the service item.
"""
if self.is_text():
return self.title
else:
2011-08-28 17:45:13 +00:00
if ItemCapabilities.HasDetailedTitleDisplay in self.capabilities:
2011-01-29 07:53:21 +00:00
return self._raw_frames[0][u'title']
elif len(self._raw_frames) > 1:
2011-01-28 19:46:55 +00:00
return self.title
else:
return self._raw_frames[0][u'title']
2009-11-05 17:03:37 +00:00
def merge(self, other):
"""
2009-11-14 09:06:25 +00:00
Updates the _uuid with the value from the original one
2010-06-25 00:25:21 +00:00
The _uuid is unique for a given service item but this allows one to
2009-11-05 17:03:37 +00:00
replace an original version.
``other``
The service item to be merged with
2009-11-05 17:03:37 +00:00
"""
2009-11-14 09:06:25 +00:00
self._uuid = other._uuid
self.notes = other.notes
2011-12-10 08:52:18 +00:00
self.temporary_edit = other.temporary_edit
2011-10-03 16:53:54 +00:00
# Copy theme over if present.
if other.theme is not None:
2011-10-03 16:53:54 +00:00
self.theme = other.theme
self._new_item()
self.render()
2011-09-02 19:04:07 +00:00
if self.is_capable(ItemCapabilities.HasBackgroundAudio):
log.debug(self.background_audio)
2009-11-05 17:03:37 +00:00
def __eq__(self, other):
"""
Confirms the service items are for the same instance
"""
if not other:
return False
2009-11-14 09:06:25 +00:00
return self._uuid == other._uuid
2009-11-05 17:03:37 +00:00
def __ne__(self, other):
"""
Confirms the service items are not for the same instance
"""
2009-11-14 09:06:25 +00:00
return self._uuid != other._uuid
2009-11-05 17:03:37 +00:00
def is_media(self):
2010-06-10 21:30:50 +00:00
"""
Confirms if the ServiceItem is media
"""
2010-04-03 07:10:31 +00:00
return ItemCapabilities.RequiresMedia in self.capabilities
2009-11-11 19:10:38 +00:00
def is_command(self):
2010-06-10 21:30:50 +00:00
"""
Confirms if the ServiceItem is a command
"""
return self.service_item_type == ServiceItemType.Command
2009-11-11 19:10:38 +00:00
def is_image(self):
2010-06-10 21:30:50 +00:00
"""
Confirms if the ServiceItem is an image
"""
return self.service_item_type == ServiceItemType.Image
def uses_file(self):
2010-06-10 21:30:50 +00:00
"""
Confirms if the ServiceItem uses a file
"""
return self.service_item_type == ServiceItemType.Image or \
self.service_item_type == ServiceItemType.Command
def is_text(self):
2010-06-10 21:30:50 +00:00
"""
Confirms if the ServiceItem is text
"""
return self.service_item_type == ServiceItemType.Text
2009-11-14 08:40:14 +00:00
def get_frames(self):
2010-06-10 21:30:50 +00:00
"""
Returns the frames for the ServiceItem
"""
2009-11-14 09:02:30 +00:00
if self.service_item_type == ServiceItemType.Text:
2009-11-14 09:06:25 +00:00
return self._display_frames
2009-11-14 09:02:30 +00:00
else:
2009-11-14 09:06:25 +00:00
return self._raw_frames
2009-11-14 09:02:30 +00:00
def get_rendered_frame(self, row):
2009-11-14 09:06:25 +00:00
"""
Returns the correct frame for a given list and
renders it if required.
"""
2009-11-14 09:02:30 +00:00
if self.service_item_type == ServiceItemType.Text:
2010-10-23 17:37:10 +00:00
return self._display_frames[row][u'html'].split(u'\n')[0]
2010-10-24 06:26:07 +00:00
elif self.service_item_type == ServiceItemType.Image:
2010-10-23 17:37:10 +00:00
return self._raw_frames[row][u'title']
2010-10-24 06:26:07 +00:00
else:
return self._raw_frames[row][u'image']
def get_frame_title(self, row=0):
"""
Returns the title of the raw frame
"""
try:
return self._raw_frames[row][u'title']
2011-01-22 12:48:16 +00:00
except IndexError:
return u''
2010-03-30 19:01:23 +00:00
def get_frame_path(self, row=0):
"""
2010-10-15 08:35:00 +00:00
Returns the path of the raw frame
2010-03-30 19:01:23 +00:00
"""
try:
return self._raw_frames[row][u'path']
2011-01-22 12:48:16 +00:00
except IndexError:
return u''
2011-02-13 10:37:27 +00:00
def get_media_time(self):
"""
Returns the start and finish time for a media item
"""
start = None
end = None
2011-02-13 13:11:15 +00:00
if self.start_time != 0:
2011-07-10 11:59:23 +00:00
start = unicode(translate('OpenLP.ServiceItem',
'<strong>Start</strong>: %s')) % \
2011-02-13 13:11:15 +00:00
unicode(datetime.timedelta(seconds=self.start_time))
2011-02-14 17:54:09 +00:00
if self.media_length != 0:
2011-07-10 11:59:23 +00:00
end = unicode(translate('OpenLP.ServiceItem',
'<strong>Length</strong>: %s')) % \
2011-02-14 18:32:46 +00:00
unicode(datetime.timedelta(seconds=self.media_length))
2011-02-13 10:37:27 +00:00
if not start and not end:
return u''
2011-02-13 10:37:27 +00:00
elif start and not end:
return start
elif not start and end:
return end
else:
return u'%s <br>%s' % (start, end)
2011-04-26 17:03:19 +00:00
def update_theme(self, theme):
"""
updates the theme in the service item
``theme``
The new theme to be replaced in the service item
"""
2011-11-13 08:57:29 +00:00
self.theme_overwritten = (theme == None)
self.theme = theme
self._new_item()
self.render()
2011-04-26 17:03:19 +00:00