openlp/openlp/core/lib/serviceitem.py

620 lines
24 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
2012-12-07 21:57:35 +00:00
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
2019-04-13 13:00:22 +00:00
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2019 OpenLP Developers #
# ---------------------------------------------------------------------- #
# 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, either version 3 of the License, or #
# (at your option) any later version. #
# #
# 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, see <https://www.gnu.org/licenses/>. #
##########################################################################
2010-06-10 21:30:50 +00:00
"""
The :mod:`serviceitem` provides the service item functionality including the
type and capability of an item.
"""
2011-02-13 10:37:27 +00:00
import datetime
2009-05-02 11:16:08 +00:00
import logging
2017-12-28 08:27:44 +00:00
import ntpath
import os
2009-11-04 17:48:46 +00:00
import uuid
from copy import deepcopy
2009-07-10 13:16:15 +00:00
2015-11-07 00:49:40 +00:00
from PyQt5 import QtGui
2009-07-10 13:16:15 +00:00
from openlp.core.state import State
2017-10-07 07:05:07 +00:00
from openlp.core.common import md5_hash
from openlp.core.common.applocation import AppLocation
2018-04-10 19:26:56 +00:00
from openlp.core.common.i18n import translate
2017-10-23 22:09:57 +00:00
from openlp.core.common.mixins import RegistryProperties
2018-01-13 07:24:20 +00:00
from openlp.core.common.path import Path
2017-10-07 07:05:07 +00:00
from openlp.core.common.settings import Settings
from openlp.core.display.render import remove_tags, render_tags
from openlp.core.lib import ItemCapabilities
2018-10-02 04:39:42 +00:00
from openlp.core.ui.icons import UiIcons
2009-07-10 13:16:15 +00:00
2010-02-27 15:31:23 +00:00
log = logging.getLogger(__name__)
2013-02-01 19:58:18 +00:00
class ServiceItemType(object):
2009-09-29 04:58:26 +00:00
"""
Defines the type of service item
"""
Text = 1
Image = 2
Command = 3
2014-03-16 21:25:23 +00:00
class ServiceItem(RegistryProperties):
"""
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.
"""
2013-08-31 18:17:38 +00:00
log.info('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.
2014-03-17 19:05:55 +00:00
:param plugin: The plugin that this service item belongs to.
"""
2009-11-14 08:40:14 +00:00
if plugin:
self.name = plugin.name
self._rendered_slides = None
self._display_slides = None
2013-08-31 18:17:38 +00:00
self.title = ''
self.slides = []
self.processor = None
2013-08-31 18:17:38 +00:00
self.audit = ''
2009-05-02 11:16:08 +00:00
self.items = []
2018-04-21 05:47:20 +00:00
self.icon = UiIcons().default
2011-03-04 17:31:23 +00:00
self.raw_footer = []
# Plugins can set footer_html themselves. If they don't, it will be generated from raw_footer.
self.footer_html = ''
2009-05-02 11:16:08 +00:00
self.theme = None
self.service_item_type = None
2013-01-21 07:34:50 +00:00
self.unique_identifier = 0
2013-08-31 18:17:38 +00:00
self.notes = ''
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
2013-12-26 08:56:53 +00:00
self.theme_data = 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
2013-08-31 18:17:38 +00:00
self.search_string = ''
self.data_string = ''
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
2013-08-31 18:17:38 +00:00
self.image_border = '#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
2012-11-14 11:47:15 +00:00
self.auto_play_slides_once = False
self.auto_play_slides_loop = False
self.timed_slide_interval = 0
self.will_auto_start = False
2012-12-08 16:20:52 +00:00
self.has_original_files = True
self._new_item()
2018-02-18 16:16:52 +00:00
self.metadata = []
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
"""
2014-03-17 19:05:55 +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
"""
2013-08-31 18:17:38 +00:00
self.unique_identifier = str(uuid.uuid1())
self.validate_item()
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
2014-03-17 19:05:55 +00:00
:param capability: The capability to add
2010-06-10 21:30:50 +00:00
"""
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
2014-03-17 19:05:55 +00:00
:param capability: The capability to test for
2010-06-10 21:30:50 +00:00
"""
2010-04-03 07:10:31 +00:00
return capability in self.capabilities
2009-05-02 11:16:08 +00:00
2018-04-21 05:47:20 +00:00
def add_icon(self):
2009-07-10 13:16:15 +00:00
"""
2014-03-17 19:05:55 +00:00
Add an icon to the service item. This is used when displaying the service item in the service manager.
2009-07-10 13:16:15 +00:00
"""
2018-04-08 17:24:31 +00:00
if self.name == 'songs':
2018-04-21 05:47:20 +00:00
self.icon = UiIcons().music
2018-04-08 17:24:31 +00:00
elif self.name == 'bibles':
2018-04-21 05:47:20 +00:00
self.icon = UiIcons().bible
2018-04-08 17:24:31 +00:00
elif self.name == 'presentations':
2018-04-21 05:47:20 +00:00
self.icon = UiIcons().presentation
2018-04-08 17:24:31 +00:00
elif self.name == 'images':
2018-04-21 05:47:20 +00:00
self.icon = UiIcons().picture
2018-04-22 06:59:35 +00:00
elif self.name == 'media':
2018-04-21 05:47:20 +00:00
self.icon = UiIcons().video
2018-04-08 17:24:31 +00:00
else:
2018-04-21 05:47:20 +00:00
self.icon = UiIcons().clone
def _create_slides(self):
"""
Create frames for rendering and display
"""
self._rendered_slides = []
self._display_slides = []
# Save rendered pages to this dict. In the case that a slide is used twice we can use the pages saved to
# the dict instead of rendering them again.
previous_pages = {}
2019-02-02 21:02:33 +00:00
index = 0
if not self.footer_html:
self.footer_html = '<br>'.join([_f for _f in self.raw_footer if _f])
2019-02-02 21:02:33 +00:00
for raw_slide in self.slides:
verse_tag = raw_slide['verse']
2019-02-02 21:02:33 +00:00
if verse_tag in previous_pages and previous_pages[verse_tag][0] == raw_slide:
pages = previous_pages[verse_tag][1]
else:
2019-02-02 21:02:33 +00:00
pages = self.renderer.format_slide(raw_slide['text'], self)
previous_pages[verse_tag] = (raw_slide, pages)
for page in pages:
rendered_slide = {
'title': raw_slide['title'],
'text': render_tags(page),
'verse': index,
2019-02-21 21:26:36 +00:00
'footer': self.footer_html,
2019-02-02 21:02:33 +00:00
}
self._rendered_slides.append(rendered_slide)
display_slide = {
'title': raw_slide['title'],
'text': remove_tags(page),
'verse': verse_tag,
}
self._display_slides.append(display_slide)
index += 1
@property
def rendered_slides(self):
"""
Render the frames and return them
"""
if not self._rendered_slides:
self._create_slides()
return self._rendered_slides
@property
def display_slides(self):
"""
Render the frames and return them
"""
if not self._display_slides:
self._create_slides()
return self._display_slides
def add_from_image(self, path, title, background=None, thumbnail=None):
2009-07-10 13:16:15 +00:00
"""
Add an image slide to the service item.
2014-03-17 19:05:55 +00:00
:param path: The directory in which the image file is located.
:param title: A title for the slide in the service item.
:param background: The background colour
:param thumbnail: Optional alternative thumbnail, used for remote thumbnails.
2009-07-10 13:16:15 +00:00
"""
2011-08-20 11:45:06 +00:00
if background:
self.image_border = background
self.service_item_type = ServiceItemType.Image
2018-12-01 05:52:49 +00:00
slide = {'title': title, 'path': path}
if thumbnail:
slide['thumbnail'] = thumbnail
self.slides.append(slide)
# self.image_manager.add_image(path, ImageSource.ImagePlugin, self.image_border)
2010-08-02 19:08:55 +00:00
self._new_item()
def add_from_text(self, text, verse_tag=None):
2009-07-10 13:16:15 +00:00
"""
Add a text slide to the service item.
:param text: The raw text of the slide.
2014-03-17 19:05:55 +00:00
:param verse_tag:
2009-07-10 13:16:15 +00:00
"""
2011-05-11 22:32:25 +00:00
if verse_tag:
verse_tag = verse_tag.upper()
else:
# For items that don't have a verse tag, autoincrement the slide numbers
verse_tag = str(len(self.slides))
self.service_item_type = ServiceItemType.Text
title = text[:30].split('\n')[0]
self.slides.append({'title': title, 'text': text, 'verse': verse_tag})
2010-08-02 19:08:55 +00:00
self._new_item()
2013-10-28 02:33:28 +00:00
def add_from_command(self, path, file_name, image, display_title=None, notes=None):
2009-07-10 13:16:15 +00:00
"""
Add a slide from a command.
2014-03-17 19:05:55 +00:00
:param path: The title of the slide in the service item.
:param file_name: The title of the slide in the service item.
:param image: The command of/for the slide.
:param display_title: Title to show in gui/webinterface, optional.
:param notes: Notes to show in the webinteface, optional.
2009-07-10 13:16:15 +00:00
"""
self.service_item_type = ServiceItemType.Command
# If the item should have a display title but this frame doesn't have one, we make one up
if self.is_capable(ItemCapabilities.HasDisplayTitle) and not display_title:
display_title = translate('OpenLP.ServiceItem',
'[slide {frame:d}]').format(frame=len(self.slides) + 1)
# Update image path to match servicemanager location if file was loaded from service
if image and not self.has_original_files and self.name == 'presentations':
file_location = os.path.join(path, file_name)
file_location_hash = md5_hash(file_location.encode('utf-8'))
2017-08-01 20:59:41 +00:00
image = os.path.join(str(AppLocation.get_section_data_path(self.name)), 'thumbnails',
2019-03-17 10:36:12 +00:00
file_location_hash, ntpath.basename(image)) # TODO: Pathlib
self.slides.append({'title': file_name, 'image': image, 'path': path, 'display_title': display_title,
'notes': notes, 'thumbnail': image})
# if self.is_capable(ItemCapabilities.HasThumbnails):
# self.image_manager.add_image(image, ImageSource.CommandPlugins, '#000000')
self._new_item()
2012-10-15 16:20:23 +00:00
def get_service_repr(self, lite_save):
"""
2014-03-17 19:05:55 +00:00
This method returns some text which can be saved into the service file to represent this item.
"""
2009-09-19 21:45:50 +00:00
service_header = {
2013-08-31 18:17:38 +00:00
'name': self.name,
'plugin': self.name,
'theme': self.theme,
'title': self.title,
'footer': self.raw_footer,
'type': self.service_item_type,
'audit': self.audit,
'notes': self.notes,
'from_plugin': self.from_plugin,
'capabilities': self.capabilities,
'search': self.search_string,
'data': self.data_string,
'xml_version': self.xml_version,
'auto_play_slides_once': self.auto_play_slides_once,
'auto_play_slides_loop': self.auto_play_slides_loop,
'timed_slide_interval': self.timed_slide_interval,
'start_time': self.start_time,
'end_time': self.end_time,
'media_length': self.media_length,
'background_audio': self.background_audio,
'theme_overwritten': self.theme_overwritten,
'will_auto_start': self.will_auto_start,
2018-02-18 16:16:52 +00:00
'processor': self.processor,
'metadata': self.metadata
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:
for slide in self.slides:
data_slide = deepcopy(slide)
data_slide['raw_slide'] = data_slide.pop('text')
data_slide['verseTag'] = data_slide.pop('verse')
service_data.append(data_slide)
elif self.service_item_type == ServiceItemType.Image:
2012-10-15 16:20:23 +00:00
if lite_save:
2018-10-28 16:34:17 +00:00
for slide in self.slides:
2013-08-31 18:17:38 +00:00
service_data.append({'title': slide['title'], 'path': slide['path']})
2012-10-15 16:20:23 +00:00
else:
2018-10-28 16:34:17 +00:00
service_data = [slide['title'] for slide in self.slides]
elif self.service_item_type == ServiceItemType.Command:
2018-10-28 16:34:17 +00:00
for slide in self.slides:
2013-12-30 08:35:05 +00:00
service_data.append({'title': slide['title'], 'image': slide['image'], 'path': slide['path'],
'display_title': slide['display_title'], 'notes': slide['notes']})
2013-08-31 18:17:38 +00:00
return {'header': service_header, 'data': service_data}
2014-03-17 19:05:55 +00:00
def set_from_service(self, service_item, path=None):
"""
2014-03-17 19:05:55 +00:00
This method takes a service item from a saved service file (passed from the ServiceManager) and extracts the
data actually required.
2009-07-10 13:16:15 +00:00
2014-03-17 19:05:55 +00:00
:param service_item: The item to extract data from.
:param path: Defaults to *None*. This is the service manager path for things which have their files saved
2015-09-08 19:13:59 +00:00
with them or None when the saved service is lite and the original file paths need to be preserved.
"""
log.debug('set_from_service called with path {path}'.format(path=path))
2014-03-17 19:14:22 +00:00
header = service_item['serviceitem']['header']
2013-08-31 18:17:38 +00:00
self.title = header['title']
self.name = header['name']
self.service_item_type = header['type']
self.theme = header['theme']
2018-04-21 05:47:20 +00:00
self.add_icon()
2013-08-31 18:17:38 +00:00
self.raw_footer = header['footer']
self.audit = header['audit']
self.notes = header['notes']
self.from_plugin = header['from_plugin']
self.capabilities = header['capabilities']
2010-09-30 17:07:27 +00:00
# Added later so may not be present in older services.
2013-08-31 18:17:38 +00:00
self.search_string = header.get('search', '')
self.data_string = header.get('data', '')
self.xml_version = header.get('xml_version')
self.start_time = header.get('start_time', 0)
self.end_time = header.get('end_time', 0)
self.media_length = header.get('media_length', 0)
self.auto_play_slides_once = header.get('auto_play_slides_once', False)
self.auto_play_slides_loop = header.get('auto_play_slides_loop', False)
self.timed_slide_interval = header.get('timed_slide_interval', 0)
self.will_auto_start = header.get('will_auto_start', False)
self.processor = header.get('processor', None)
2012-12-07 21:57:35 +00:00
self.has_original_files = True
2018-02-18 16:16:52 +00:00
self.metadata = header.get('item_meta_data', [])
if 'background_audio' in header and State().check_preconditions('media'):
self.background_audio = []
for file_path in header['background_audio']:
2018-01-13 07:24:20 +00:00
# In OpenLP 3.0 we switched to storing Path objects in JSON files
if isinstance(file_path, str):
# Handle service files prior to OpenLP 3.0
# Windows can handle both forward and backward slashes, so we use ntpath to get the basename
2019-03-10 21:01:39 +00:00
file_path = path / ntpath.basename(file_path)
self.background_audio.append(file_path)
2013-08-31 18:17:38 +00:00
self.theme_overwritten = header.get('theme_overwritten', False)
if self.service_item_type == ServiceItemType.Text:
2014-03-17 19:14:22 +00:00
for slide in service_item['serviceitem']['data']:
2018-10-28 16:34:17 +00:00
self.add_from_text(slide['raw_slide'], slide['verseTag'])
2018-11-13 05:42:43 +00:00
self._create_slides()
elif self.service_item_type == ServiceItemType.Image:
2014-03-17 19:14:22 +00:00
settings_section = service_item['serviceitem']['header']['name']
2013-08-31 18:17:38 +00:00
background = QtGui.QColor(Settings().value(settings_section + '/background color'))
2012-10-15 16:20:23 +00:00
if path:
2012-12-07 21:57:35 +00:00
self.has_original_files = False
2014-03-17 19:14:22 +00:00
for text_image in service_item['serviceitem']['data']:
2019-03-10 21:01:39 +00:00
file_path = path / text_image
2018-01-13 07:24:20 +00:00
self.add_from_image(file_path, text_image, background)
2012-10-15 16:20:23 +00:00
else:
2014-03-17 19:14:22 +00:00
for text_image in service_item['serviceitem']['data']:
2013-08-31 18:17:38 +00:00
self.add_from_image(text_image['path'], text_image['title'], background)
elif self.service_item_type == ServiceItemType.Command:
2014-03-17 19:14:22 +00:00
for text_image in service_item['serviceitem']['data']:
if not self.title:
2013-08-31 18:17:38 +00:00
self.title = text_image['title']
if self.is_capable(ItemCapabilities.IsOptical):
self.has_original_files = False
self.add_from_command(text_image['path'], text_image['title'], text_image['image'])
elif path:
2012-12-07 21:57:35 +00:00
self.has_original_files = False
self.add_from_command(path, text_image['title'], text_image['image'],
2014-07-11 13:35:44 +00:00
text_image.get('display_title', ''), text_image.get('notes', ''))
else:
2018-12-01 05:52:49 +00:00
self.add_from_command(Path(text_image['path']), text_image['title'], text_image['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.
"""
2014-04-20 13:57:12 +00:00
if self.is_text() or self.is_capable(ItemCapabilities.IsOptical) \
2014-04-20 14:36:56 +00:00
or self.is_capable(ItemCapabilities.CanEditTitle):
2011-01-28 19:46:55 +00:00
return self.title
else:
if len(self.slides) > 1:
2011-01-28 19:46:55 +00:00
return self.title
else:
return self.slides[0]['title']
2011-01-28 19:46:55 +00:00
2009-11-05 17:03:37 +00:00
def merge(self, other):
"""
2013-01-21 07:34:50 +00:00
Updates the unique_identifier with the value from the original one
2014-03-17 19:05:55 +00:00
The unique_identifier is unique for a given service item but this allows one to replace an original version.
2014-03-17 19:05:55 +00:00
:param other: The service item to be merged with
2009-11-05 17:03:37 +00:00
"""
2013-01-21 07:34:50 +00:00
self.unique_identifier = other.unique_identifier
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()
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
2013-01-21 07:34:50 +00:00
return self.unique_identifier == other.unique_identifier
2009-11-05 17:03:37 +00:00
def __ne__(self, other):
"""
Confirms the service items are not for the same instance
"""
2013-01-21 07:34:50 +00:00
return self.unique_identifier != other.unique_identifier
2009-11-05 17:03:37 +00:00
2013-06-30 18:33:41 +00:00
def __hash__(self):
"""
Return the hash for the service item.
"""
return self.unique_identifier
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 \
2014-04-22 20:42:07 +00:00
(self.service_item_type == ServiceItemType.Command and not self.is_capable(ItemCapabilities.IsOptical))
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 set_media_length(self, length):
"""
Stores the media length of the item
2014-03-17 19:05:55 +00:00
:param length: The length of the media item
"""
self.media_length = length
if length > 0:
self.add_capability(ItemCapabilities.HasVariableStartTime)
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:
2018-11-13 05:42:43 +00:00
return self.display_slides
2009-11-14 09:02:30 +00:00
else:
return self.slides
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.
2014-03-17 19:05:55 +00:00
:param row: The service item slide to be returned
2009-11-14 09:06:25 +00:00
"""
2009-11-14 09:02:30 +00:00
if self.service_item_type == ServiceItemType.Text:
2018-11-13 05:42:43 +00:00
# return self.display_frames[row]['html'].split('\n')[0]
return self.rendered_slides[row]['text']
2010-10-24 06:26:07 +00:00
elif self.service_item_type == ServiceItemType.Image:
return self.slides[row]['path']
2010-10-24 06:26:07 +00:00
else:
return self.slides[row]['image']
def get_frame_title(self, row=0):
"""
Returns the title of the raw frame
"""
try:
2018-11-16 04:34:49 +00:00
return self.get_frames()[row]['title']
2011-01-22 12:48:16 +00:00
except IndexError:
2013-08-31 18:17:38 +00:00
return ''
def get_frame_path(self, row=0, frame=None):
2010-03-30 19:01:23 +00:00
"""
2010-10-15 08:35:00 +00:00
Returns the path of the raw frame
2010-03-30 19:01:23 +00:00
"""
if not frame:
try:
frame = self.slides[row]
except IndexError:
2013-08-31 18:17:38 +00:00
return ''
if self.is_image() or self.is_capable(ItemCapabilities.IsOptical):
2013-08-31 18:17:38 +00:00
path_from = frame['path']
else:
2013-08-31 18:17:38 +00:00
path_from = os.path.join(frame['path'], frame['title'])
2018-11-01 20:51:42 +00:00
if isinstance(path_from, str):
# Handle service files prior to OpenLP 3.0
# Windows can handle both forward and backward slashes, so we use ntpath to get the basename
path_from = Path(path_from)
return path_from
def remove_frame(self, frame):
"""
2012-12-07 21:57:35 +00:00
Remove the specified frame from the item
"""
2018-10-28 16:34:17 +00:00
if frame in self.slides:
self.slides.remove(frame)
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:
time = str(datetime.timedelta(seconds=self.start_time))
start = translate('OpenLP.ServiceItem',
'<strong>Start</strong>: {start}').format(start=time)
2011-02-14 17:54:09 +00:00
if self.media_length != 0:
length = str(datetime.timedelta(seconds=self.media_length // 1000))
end = translate('OpenLP.ServiceItem', '<strong>Length</strong>: {length}').format(length=length)
2011-02-13 10:37:27 +00:00
if not start and not end:
2013-08-31 18:17:38 +00:00
return ''
2011-02-13 10:37:27 +00:00
elif start and not end:
return start
elif not start and end:
return end
else:
return '{start} <br>{end}'.format(start=start, end=end)
2011-04-26 17:03:19 +00:00
def update_theme(self, theme):
"""
updates the theme in the service item
2014-03-17 19:05:55 +00:00
:param theme: The new theme to be replaced in the service item
"""
2013-02-01 19:58:18 +00:00
self.theme_overwritten = (theme is None)
self.theme = theme
self._new_item()
2011-04-26 17:03:19 +00:00
2012-11-02 18:54:42 +00:00
def remove_invalid_frames(self, invalid_paths=None):
"""
Remove invalid frames, such as ones where the file no longer exists.
"""
if self.uses_file():
for frame in self.get_frames():
if self.get_frame_path(frame=frame) in invalid_paths:
self.remove_frame(frame)
def missing_frames(self):
2012-11-02 18:54:42 +00:00
"""
Returns if there are any frames in the service item
2012-11-02 18:54:42 +00:00
"""
2018-10-28 16:34:17 +00:00
return not bool(self.slides)
def validate_item(self, suffix_list=None):
"""
Validates a service item to make sure it is valid
"""
self.is_valid = True
for slide in self.slides:
2018-12-01 05:52:49 +00:00
if self.is_image() and not os.path.exists(slide['path']):
self.is_valid = False
break
elif self.is_command():
if self.is_capable(ItemCapabilities.IsOptical) and State().check_preconditions('media'):
if not os.path.exists(slide['title']):
self.is_valid = False
break
else:
file_name = os.path.join(slide['path'], slide['title'])
if not os.path.exists(file_name):
self.is_valid = False
break
if suffix_list and not self.is_text():
file_suffix = slide['title'].split('.')[-1]
if file_suffix.lower() not in suffix_list:
self.is_valid = False
2014-06-17 07:27:12 +00:00
break