diff --git a/MANIFEST.in b/MANIFEST.in index b51cd4c06..35e83e30f 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -4,6 +4,7 @@ recursive-include openlp *.csv recursive-include openlp *.html recursive-include openlp *.js recursive-include openlp *.css +recursive-include openlp *.png recursive-include documentation * recursive-include resources * recursive-include scripts * diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index ec0dceb65..8991c873f 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -184,8 +184,9 @@ class ServiceItem(object): else: log.error(u'Invalid value renderer :%s' % self.service_item_type) self.title = clean_tags(self.title) - # The footer should never be None, but to be compatible with older - # release of OpenLP, we have to correct this to avoid tracebacks. + # The footer should never be None, but to be compatible with a few + # nightly builds between 1.9.4 and 1.9.5, we have to correct this to + # avoid tracebacks. if self.raw_footer is None: self.raw_footer = [] self.foot_text = \ diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index 193e96c1d..1153917a2 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -110,7 +110,7 @@ class MainDisplay(DisplayWidget): Phonon.createPath(self.mediaObject, self.audio) QtCore.QObject.connect(self.mediaObject, QtCore.SIGNAL(u'stateChanged(Phonon::State, Phonon::State)'), - self.videoStart) + self.videoState) QtCore.QObject.connect(self.mediaObject, QtCore.SIGNAL(u'finished()'), self.videoFinished) @@ -378,11 +378,13 @@ class MainDisplay(DisplayWidget): Receiver.send_message(u'maindisplay_active') return self.preview() - def videoStart(self, newState, oldState): + def videoState(self, newState, oldState): """ Start the video at a predetermined point. """ - if newState == Phonon.PlayingState: + if newState == Phonon.PlayingState \ + and oldState != Phonon.PausedState \ + and self.serviceItem.start_time > 0: # set start time in milliseconds self.mediaObject.seek(self.serviceItem.start_time * 1000) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 7cb1eb5c4..dfffa2e66 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -46,7 +46,6 @@ class SlideList(QtGui.QTableWidget): QtGui.QTableWidget.__init__(self, parent.controller) self.parent = parent - class SlideController(QtGui.QWidget): """ SlideController is the slide controller widget. This widget is what the @@ -858,6 +857,7 @@ class SlideController(QtGui.QWidget): self.serviceItem.bg_image_bytes = None self.slidePreview.setPixmap(QtGui.QPixmap.fromImage(frame)) self.selectedRow = row + self.__checkUpdateSelectedSlide(row) Receiver.send_message(u'slidecontroller_%s_changed' % self.typePrefix, row) diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 407d0cfa8..487798f5b 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -424,12 +424,12 @@ class ThemeManager(QtGui.QWidget): unicode(translate('OpenLP.ThemeManager', 'OpenLP Themes (*.theme *.otz)'))) log.info(u'New Themes %s', unicode(files)) + if not files: + return Receiver.send_message(u'cursor_busy') - if files: - for file in files: - SettingsManager.set_last_dir( - self.settingsSection, unicode(file)) - self.unzipTheme(file, self.path) + for file in files: + SettingsManager.set_last_dir(self.settingsSection, unicode(file)) + self.unzipTheme(file, self.path) self.loadThemes() Receiver.send_message(u'cursor_normal') @@ -502,16 +502,16 @@ class ThemeManager(QtGui.QWidget): def unzipTheme(self, filename, dir): """ Unzip the theme, remove the preview file if stored - Generate a new preview fileCheck the XML theme version and upgrade if + Generate a new preview file. Check the XML theme version and upgrade if necessary. """ log.debug(u'Unzipping theme %s', filename) filename = unicode(filename) zip = None outfile = None + filexml = None try: zip = zipfile.ZipFile(filename) - filexml = None themename = None for file in zip.namelist(): ucsfile = file_is_unicode(file) @@ -547,7 +547,7 @@ class ThemeManager(QtGui.QWidget): else: outfile = open(fullpath, u'wb') outfile.write(zip.read(file)) - except (IOError, NameError): + except (IOError, NameError, zipfile.BadZipfile): critical_error_message_box( translate('OpenLP.ThemeManager', 'Validation Error'), translate('OpenLP.ThemeManager', 'File is not a valid theme.')) @@ -562,7 +562,9 @@ class ThemeManager(QtGui.QWidget): if filexml: theme = self._createThemeFromXml(filexml, self.path) self.generateAndSaveImage(dir, themename, theme) - else: + # Only show the error message, when IOError was not raised (in this + # case the error message has already been shown). + elif zip is not None: critical_error_message_box( translate('OpenLP.ThemeManager', 'Validation Error'), translate('OpenLP.ThemeManager', diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index e5ab6e5e0..8a1bc2b76 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -36,7 +36,7 @@ from openlp.core.lib.ui import UiStrings, critical_error_message_box from PyQt4.phonon import Phonon log = logging.getLogger(__name__) - + class MediaMediaItem(MediaManagerItem): """ This is the custom media manager item for Media Slides. @@ -54,9 +54,6 @@ class MediaMediaItem(MediaManagerItem): QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'video_background_replaced'), self.videobackgroundReplaced) - QtCore.QObject.connect(self.mediaObject, - QtCore.SIGNAL(u'stateChanged(Phonon::State, Phonon::State)'), - self.videoStart) def retranslateUi(self): self.onNewPrompt = translate('MediaPlugin.MediaItem', 'Select Media') @@ -125,41 +122,67 @@ class MediaMediaItem(MediaManagerItem): if item is None: return False filename = unicode(item.data(QtCore.Qt.UserRole).toString()) - if os.path.exists(filename): - self.mediaState = None - self.mediaObject.stop() - self.mediaObject.clearQueue() - self.mediaObject.setCurrentSource(Phonon.MediaSource(filename)) - self.mediaObject.play() - service_item.title = unicode(self.plugin.nameStrings[u'singular']) - service_item.add_capability(ItemCapabilities.RequiresMedia) - # force a nonexistent theme - service_item.theme = -1 - frame = u':/media/image_clapperboard.png' - (path, name) = os.path.split(filename) - file_size = os.path.getsize(filename) - # File too big for processing - if file_size <= 52428800: # 50MiB - start = datetime.now() - while not self.mediaState: - Receiver.send_message(u'openlp_process_events') - tme = datetime.now() - start - if tme.seconds > 5: - break - if self.mediaState: - service_item.media_length = self.mediaLength - service_item.add_capability( - ItemCapabilities.AllowsVariableStartTime) - service_item.add_from_command(path, name, frame) - return True - else: + if not os.path.exists(filename): # File is no longer present critical_error_message_box( translate('MediaPlugin.MediaItem', 'Missing Media File'), unicode(translate('MediaPlugin.MediaItem', 'The file %s no longer exists.')) % filename) return False + self.mediaObject.stop() + self.mediaObject.clearQueue() + self.mediaObject.setCurrentSource(Phonon.MediaSource(filename)) + if not self.mediaStateWait(Phonon.StoppedState): + # Due to string freeze, borrow a message from presentations + # This will be corrected in 1.9.6 + critical_error_message_box( + translate('PresentationPlugin.MediaItem', 'Unsupported File'), + unicode(translate('PresentationPlugin.MediaItem', + 'Unsupported File'))) + return False + # File too big for processing + if os.path.getsize(filename) <= 52428800: # 50MiB + self.mediaObject.play() + if not self.mediaStateWait(Phonon.PlayingState) \ + or self.mediaObject.currentSource().type() \ + == Phonon.MediaSource.Invalid: + # Due to string freeze, borrow a message from presentations + # This will be corrected in 1.9.6 + self.mediaObject.stop() + critical_error_message_box( + translate('PresentationPlugin.MediaItem', + 'Unsupported File'), + unicode(translate('PresentationPlugin.MediaItem', + 'Unsupported File'))) + return False + self.mediaLength = self.mediaObject.totalTime() / 1000 + self.mediaObject.stop() + service_item.media_length = self.mediaLength + service_item.add_capability( + ItemCapabilities.AllowsVariableStartTime) + service_item.title = unicode(self.plugin.nameStrings[u'singular']) + service_item.add_capability(ItemCapabilities.RequiresMedia) + # force a non-existent theme + service_item.theme = -1 + frame = u':/media/image_clapperboard.png' + (path, name) = os.path.split(filename) + service_item.add_from_command(path, name, frame) + return True + def mediaStateWait(self, mediaState): + """ + Wait for the video to change its state + Wait no longer than 5 seconds. + """ + start = datetime.now() + while self.mediaObject.state() != mediaState: + if self.mediaObject.state() == Phonon.ErrorState: + return False + Receiver.send_message(u'openlp_process_events') + if (datetime.now() - start).seconds > 5: + return False + return True + def initialise(self): self.listView.clear() self.listView.setIconSize(QtCore.QSize(88, 50)) @@ -187,12 +210,3 @@ class MediaMediaItem(MediaManagerItem): item_name.setIcon(build_icon(img)) item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file)) self.listView.addItem(item_name) - - def videoStart(self, newState, oldState): - """ - Start the video at a predetermined point. - """ - if newState == Phonon.PlayingState: - self.mediaState = newState - self.mediaLength = self.mediaObject.totalTime()/1000 - self.mediaObject.stop() diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index 1c1957b9e..b9dcd1e96 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -275,14 +275,33 @@ def clean_song(manager, song): sxml = SongXML() # Rebuild the song's verses, to remove any wrong verse names (for example # translated ones), which might have been added prior to 1.9.5. + # List for later comparison. + compare_order = [] for verse in verses: + type = VerseType.Tags[VerseType.from_loose_input(verse[0][u'type'])] sxml.add_verse_to_lyrics( - VerseType.Tags[VerseType.from_loose_input(verse[0][u'type'])], + type, verse[0][u'label'], verse[1], verse[0][u'lang'] if verse[0].has_key(u'lang') else None ) + compare_order.append((u'%s%s' % (type, verse[0][u'label'])).upper()) song.lyrics = unicode(sxml.extract_xml(), u'utf-8') + # Rebuild the verse order, to convert translated verse tags, which might + # have been added prior to 1.9.5. + order = song.verse_order.strip().split() + new_order = [] + for verse_def in order: + new_order.append((u'%s%s' % ( + VerseType.Tags[VerseType.from_loose_input(verse_def[0])], + verse_def[1:])).upper() + ) + song.verse_order = u' '.join(new_order) + # Check if the verse order contains tags for verses which do not exist. + for order in new_order: + if order not in compare_order: + song.verse_order = u'' + break # The song does not have any author, add one. if not song.authors: name = SongStrings.AuthorUnknown diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 57e56aaf6..b95bff3dc 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -461,5 +461,5 @@ class SongMediaItem(MediaManagerItem): """ Locale aware collation of song titles """ - return locale.strcoll(unicode(song_1.title.lower()), + return locale.strcoll(unicode(song_1.title.lower()), unicode(song_2.title.lower())) diff --git a/openlp/plugins/songs/lib/sofimport.py b/openlp/plugins/songs/lib/sofimport.py index 37f3136e9..49ed5b569 100644 --- a/openlp/plugins/songs/lib/sofimport.py +++ b/openlp/plugins/songs/lib/sofimport.py @@ -309,7 +309,7 @@ class SofImport(OooImport): self.add_verse(lyrics, tag) if not self.is_chorus and u'C1' in self.verse_order_list_generated: self.verse_order_list_generated.append(u'C1') - self.verse_order_list_generated_useful = True + self.verse_order_list_generated_useful = True def uncap_text(self, text): """ diff --git a/openlp/plugins/songs/lib/songimport.py b/openlp/plugins/songs/lib/songimport.py index ea7a30e2f..c14354dcb 100644 --- a/openlp/plugins/songs/lib/songimport.py +++ b/openlp/plugins/songs/lib/songimport.py @@ -225,7 +225,7 @@ class SongImport(QtCore.QObject): self.verse_counts[verse_def[0]] = int(verse_def[1:]) self.verses.append([verse_def, verse_text.rstrip(), lang]) self.verse_order_list_generated.append(verse_def) - + def repeat_verse(self): """ Repeat the previous verse in the verse order diff --git a/openlp/plugins/songs/lib/songshowplusimport.py b/openlp/plugins/songs/lib/songshowplusimport.py index d69160faa..363bfaa98 100644 --- a/openlp/plugins/songs/lib/songshowplusimport.py +++ b/openlp/plugins/songs/lib/songshowplusimport.py @@ -131,7 +131,7 @@ class SongShowPlusImport(SongImport): lengthDescriptor, = struct.unpack("B", songData.read(1)) data = songData.read(lengthDescriptor) if blockKey == TITLE: - self.title = unicode(data, u'cp1252') + self.title = unicode(data, u'cp1252') elif blockKey == AUTHOR: authors = data.split(" / ") for author in authors: diff --git a/setup.py b/setup.py index 98e9d40fd..516da89ff 100755 --- a/setup.py +++ b/setup.py @@ -62,7 +62,41 @@ setup( description="Open source Church presentation and lyrics projection application.", long_description="""\ OpenLP (previously openlp.org) is free church presentation software, or lyrics projection software, used to display slides of songs, Bible verses, videos, images, and even presentations (if PowerPoint is installed) for church worship using a computer and a data projector.""", - classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers + classifiers=[ + 'Development Status :: 4 - Beta', + 'Environment :: MacOS X', + 'Environment :: Win32 (MS Windows)', + 'Environment :: X11 Applications', + 'Environment :: X11 Applications :: Qt', + 'Intended Audience :: End Users/Desktop', + 'Intended Audience :: Religion', + 'License :: OSI Approved :: GNU General Public License (GPL)', + 'Natural Language :: Afrikaans', + 'Natural Language :: Dutch', + 'Natural Language :: English', + 'Natural Language :: French', + 'Natural Language :: German', + 'Natural Language :: Hungarian', + 'Natural Language :: Indonesian', + 'Natural Language :: Japanese', + 'Natural Language :: Norwegian', + 'Natural Language :: Portuguese (Brazilian)', + 'Natural Language :: Russian', + 'Natural Language :: Swedish', + 'Operating System :: MacOS :: MacOS X', + 'Operating System :: Microsoft :: Windows', + 'Operating System :: POSIX :: BSD :: FreeBSD', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Topic :: Desktop Environment :: Gnome', + 'Topic :: Desktop Environment :: K Desktop Environment (KDE)', + 'Topic :: Multimedia', + 'Topic :: Multimedia :: Graphics :: Presentation', + 'Topic :: Multimedia :: Sound/Audio', + 'Topic :: Multimedia :: Video', + 'Topic :: Religion' + ], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers keywords='open source church presentation lyrics projection song bible display project', author='Raoul Snyman', author_email='raoulsnyman@openlp.org',