2010-04-03 00:13:07 +00:00
|
|
|
# -*- 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 #
|
2012-06-22 14:14:53 +00:00
|
|
|
# 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, #
|
2012-11-07 21:37:01 +00:00
|
|
|
# Frode Woldsund, Martin Zibricky #
|
2010-04-03 00:13:07 +00:00
|
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
# 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-07-04 16:40:53 +00:00
|
|
|
"""
|
2011-01-05 16:31:04 +00:00
|
|
|
The :mod:`xml` module provides the XML functionality.
|
2010-04-03 00:13:07 +00:00
|
|
|
|
2011-02-10 05:25:08 +00:00
|
|
|
The basic XML for storing the lyrics in the song database looks like this::
|
2010-04-03 00:13:07 +00:00
|
|
|
|
2010-07-04 16:40:53 +00:00
|
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
<song version="1.0">
|
2011-01-09 16:52:31 +00:00
|
|
|
<lyrics>
|
2011-03-18 18:43:26 +00:00
|
|
|
<verse type="c" label="1" lang="en">
|
2012-05-05 14:03:48 +00:00
|
|
|
<![CDATA[Chorus optional split 1[---]Chorus optional split 2]]>
|
2010-07-04 16:40:53 +00:00
|
|
|
</verse>
|
|
|
|
</lyrics>
|
|
|
|
</song>
|
2011-01-05 16:31:04 +00:00
|
|
|
|
|
|
|
|
2011-02-10 05:25:08 +00:00
|
|
|
The XML of an `OpenLyrics <http://openlyrics.info/>`_ song looks like this::
|
2011-01-05 16:31:04 +00:00
|
|
|
|
|
|
|
<song xmlns="http://openlyrics.info/namespace/2009/song"
|
|
|
|
version="0.7"
|
|
|
|
createdIn="OpenLP 1.9.0"
|
|
|
|
modifiedIn="ChangingSong 0.0.1"
|
|
|
|
modifiedDate="2010-01-28T13:15:30+01:00">
|
|
|
|
<properties>
|
|
|
|
<titles>
|
|
|
|
<title>Amazing Grace</title>
|
|
|
|
</titles>
|
|
|
|
</properties>
|
|
|
|
<lyrics>
|
|
|
|
<verse name="v1">
|
|
|
|
<lines>
|
|
|
|
<line>Amazing grace how sweet the sound</line>
|
|
|
|
</lines>
|
|
|
|
</verse>
|
|
|
|
</lyrics>
|
|
|
|
</song>
|
2010-07-04 16:40:53 +00:00
|
|
|
"""
|
2011-10-08 13:20:57 +00:00
|
|
|
import cgi
|
2010-07-04 16:40:53 +00:00
|
|
|
import logging
|
2010-11-28 13:39:51 +00:00
|
|
|
import re
|
2010-07-04 16:40:53 +00:00
|
|
|
|
|
|
|
from lxml import etree, objectify
|
2011-01-03 07:16:21 +00:00
|
|
|
|
2012-03-06 20:03:18 +00:00
|
|
|
from openlp.core.lib import FormattingTags, translate
|
2011-03-14 18:59:59 +00:00
|
|
|
from openlp.plugins.songs.lib import clean_song, VerseType
|
2011-01-03 13:43:09 +00:00
|
|
|
from openlp.plugins.songs.lib.db import Author, Book, Song, Topic
|
2011-03-24 19:12:27 +00:00
|
|
|
from openlp.core.utils import get_application_version
|
2010-07-04 16:40:53 +00:00
|
|
|
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
2011-09-17 23:22:16 +00:00
|
|
|
NAMESPACE = u'http://openlyrics.info/namespace/2009/song'
|
|
|
|
NSMAP = '{' + NAMESPACE + '}' + '%s'
|
|
|
|
|
2011-09-06 13:22:17 +00:00
|
|
|
|
2011-01-09 16:52:31 +00:00
|
|
|
class SongXML(object):
|
2010-04-03 00:13:07 +00:00
|
|
|
"""
|
2011-01-09 16:52:31 +00:00
|
|
|
This class builds and parses the XML used to describe songs.
|
2010-07-04 16:40:53 +00:00
|
|
|
"""
|
2011-01-09 16:52:31 +00:00
|
|
|
log.info(u'SongXML Loaded')
|
2010-04-03 00:13:07 +00:00
|
|
|
|
2011-01-09 16:52:31 +00:00
|
|
|
def __init__(self):
|
2010-07-04 16:40:53 +00:00
|
|
|
"""
|
2011-01-09 16:52:31 +00:00
|
|
|
Set up the default variables.
|
2010-07-04 16:40:53 +00:00
|
|
|
"""
|
|
|
|
self.song_xml = objectify.fromstring(u'<song version="1.0" />')
|
2011-01-09 16:52:31 +00:00
|
|
|
self.lyrics = etree.SubElement(self.song_xml, u'lyrics')
|
2010-07-04 16:40:53 +00:00
|
|
|
|
2011-01-19 19:22:43 +00:00
|
|
|
def add_verse_to_lyrics(self, type, number, content, lang=None):
|
2010-07-04 16:40:53 +00:00
|
|
|
"""
|
2011-02-10 05:25:08 +00:00
|
|
|
Add a verse to the ``<lyrics>`` tag.
|
2010-07-04 16:40:53 +00:00
|
|
|
|
|
|
|
``type``
|
2011-03-17 20:00:19 +00:00
|
|
|
A string denoting the type of verse. Possible values are *v*,
|
|
|
|
*c*, *b*, *p*, *i*, *e* and *o*.
|
2011-02-09 19:55:53 +00:00
|
|
|
Any other type is **not** allowed, this also includes translated
|
|
|
|
types.
|
2010-07-04 16:40:53 +00:00
|
|
|
|
|
|
|
``number``
|
|
|
|
An integer denoting the number of the item, for example: verse 1.
|
|
|
|
|
|
|
|
``content``
|
|
|
|
The actual text of the verse to be stored.
|
2011-01-19 19:22:43 +00:00
|
|
|
|
|
|
|
``lang``
|
2011-01-19 19:57:08 +00:00
|
|
|
The verse's language code (ISO-639). This is not required, but
|
|
|
|
should be added if available.
|
2010-07-04 16:40:53 +00:00
|
|
|
"""
|
2011-01-02 16:42:09 +00:00
|
|
|
verse = etree.Element(u'verse', type=unicode(type),
|
|
|
|
label=unicode(number))
|
2011-01-19 19:22:43 +00:00
|
|
|
if lang:
|
|
|
|
verse.set(u'lang', lang)
|
2010-07-04 16:40:53 +00:00
|
|
|
verse.text = etree.CDATA(content)
|
2010-07-05 21:23:39 +00:00
|
|
|
self.lyrics.append(verse)
|
2010-07-04 16:40:53 +00:00
|
|
|
|
|
|
|
def extract_xml(self):
|
|
|
|
"""
|
|
|
|
Extract our newly created XML song.
|
|
|
|
"""
|
2010-07-15 20:26:57 +00:00
|
|
|
return etree.tostring(self.song_xml, encoding=u'UTF-8',
|
2010-07-04 16:40:53 +00:00
|
|
|
xml_declaration=True)
|
|
|
|
|
2011-01-09 16:52:31 +00:00
|
|
|
def get_verses(self, xml):
|
2010-07-04 16:40:53 +00:00
|
|
|
"""
|
2011-01-09 16:52:31 +00:00
|
|
|
Iterates through the verses in the XML and returns a list of verses
|
|
|
|
and their attributes.
|
2010-07-04 16:40:53 +00:00
|
|
|
|
|
|
|
``xml``
|
|
|
|
The XML of the song to be parsed.
|
2011-01-19 19:22:43 +00:00
|
|
|
|
|
|
|
The returned list has the following format::
|
|
|
|
|
2011-03-30 07:13:04 +00:00
|
|
|
[[{'type': 'v', 'label': '1'},
|
2012-05-05 14:03:48 +00:00
|
|
|
u"optional slide split 1[---]optional slide split 2"],
|
2011-03-17 20:00:19 +00:00
|
|
|
[{'lang': 'en', 'type': 'c', 'label': '1'}, u"English chorus"]]
|
2010-07-04 16:40:53 +00:00
|
|
|
"""
|
|
|
|
self.song_xml = None
|
2011-04-28 23:25:28 +00:00
|
|
|
verse_list = []
|
|
|
|
if not xml.startswith(u'<?xml') and not xml.startswith(u'<song'):
|
|
|
|
# This is an old style song, without XML. Let's handle it correctly
|
|
|
|
# by iterating through the verses, and then recreating the internal
|
|
|
|
# xml object as well.
|
|
|
|
self.song_xml = objectify.fromstring(u'<song version="1.0" />')
|
|
|
|
self.lyrics = etree.SubElement(self.song_xml, u'lyrics')
|
|
|
|
verses = xml.split(u'\n\n')
|
|
|
|
for count, verse in enumerate(verses):
|
|
|
|
verse_list.append([{u'type': u'v', u'label': unicode(count)},
|
|
|
|
unicode(verse)])
|
|
|
|
self.add_verse_to_lyrics(u'v', unicode(count), verse)
|
|
|
|
return verse_list
|
2011-04-29 06:28:09 +00:00
|
|
|
elif xml.startswith(u'<?xml'):
|
2010-07-05 11:39:48 +00:00
|
|
|
xml = xml[38:]
|
2010-07-04 16:40:53 +00:00
|
|
|
try:
|
2010-07-05 11:39:48 +00:00
|
|
|
self.song_xml = objectify.fromstring(xml)
|
2010-07-04 16:40:53 +00:00
|
|
|
except etree.XMLSyntaxError:
|
|
|
|
log.exception(u'Invalid xml %s', xml)
|
|
|
|
xml_iter = self.song_xml.getiterator()
|
|
|
|
for element in xml_iter:
|
|
|
|
if element.tag == u'verse':
|
|
|
|
if element.text is None:
|
|
|
|
element.text = u''
|
2010-07-05 11:39:48 +00:00
|
|
|
verse_list.append([element.attrib, unicode(element.text)])
|
2010-07-04 16:40:53 +00:00
|
|
|
return verse_list
|
|
|
|
|
|
|
|
def dump_xml(self):
|
|
|
|
"""
|
|
|
|
Debugging aid to dump XML so that we can see what we have.
|
|
|
|
"""
|
|
|
|
return etree.dump(self.song_xml)
|
|
|
|
|
|
|
|
|
2011-01-09 16:52:31 +00:00
|
|
|
class OpenLyrics(object):
|
2011-01-05 16:31:04 +00:00
|
|
|
"""
|
2011-09-15 12:23:40 +00:00
|
|
|
This class represents the converter for OpenLyrics XML (version 0.8)
|
2011-01-11 16:49:53 +00:00
|
|
|
to/from a song.
|
2011-01-05 16:31:04 +00:00
|
|
|
|
|
|
|
As OpenLyrics has a rich set of different features, we cannot support them
|
2011-02-10 05:25:08 +00:00
|
|
|
all. The following features are supported by the :class:`OpenLyrics` class:
|
2011-01-05 16:31:04 +00:00
|
|
|
|
2011-02-10 05:25:08 +00:00
|
|
|
``<authors>``
|
2011-01-06 09:34:26 +00:00
|
|
|
OpenLP does not support the attribute *type* and *lang*.
|
2011-01-05 16:31:04 +00:00
|
|
|
|
2011-02-10 05:25:08 +00:00
|
|
|
``<chord>``
|
2011-01-08 20:59:46 +00:00
|
|
|
This property is not supported.
|
2011-01-05 16:31:04 +00:00
|
|
|
|
2011-02-10 05:25:08 +00:00
|
|
|
``<comments>``
|
|
|
|
The ``<comments>`` property is fully supported. But comments in lyrics
|
2011-01-05 16:31:04 +00:00
|
|
|
are not supported.
|
|
|
|
|
2011-02-10 05:25:08 +00:00
|
|
|
``<copyright>``
|
2011-01-05 16:31:04 +00:00
|
|
|
This property is fully supported.
|
|
|
|
|
2011-02-10 05:25:08 +00:00
|
|
|
``<customVersion>``
|
2011-01-05 16:31:04 +00:00
|
|
|
This property is not supported.
|
|
|
|
|
2011-02-10 05:25:08 +00:00
|
|
|
``<key>``
|
2011-01-08 20:59:46 +00:00
|
|
|
This property is not supported.
|
2011-01-05 16:31:04 +00:00
|
|
|
|
2011-09-15 12:23:40 +00:00
|
|
|
``<format>``
|
|
|
|
The custom formatting tags are fully supported.
|
|
|
|
|
2011-02-10 05:25:08 +00:00
|
|
|
``<keywords>``
|
2011-01-08 20:59:46 +00:00
|
|
|
This property is not supported.
|
2011-01-05 16:31:04 +00:00
|
|
|
|
2011-02-10 05:25:08 +00:00
|
|
|
``<lines>``
|
2011-08-22 13:39:02 +00:00
|
|
|
The attribute *part* is not supported. The *break* attribute is
|
2011-08-20 17:06:48 +00:00
|
|
|
supported.
|
2011-01-05 16:31:04 +00:00
|
|
|
|
2011-02-10 05:25:08 +00:00
|
|
|
``<publisher>``
|
2011-01-08 20:59:46 +00:00
|
|
|
This property is not supported.
|
2011-01-05 16:31:04 +00:00
|
|
|
|
2011-02-10 05:25:08 +00:00
|
|
|
``<songbooks>``
|
2011-01-05 16:31:04 +00:00
|
|
|
As OpenLP does only support one songbook, we cannot consider more than
|
|
|
|
one songbook.
|
|
|
|
|
2011-02-10 05:25:08 +00:00
|
|
|
``<tempo>``
|
2011-01-08 20:59:46 +00:00
|
|
|
This property is not supported.
|
2011-01-05 16:31:04 +00:00
|
|
|
|
2011-02-10 05:25:08 +00:00
|
|
|
``<themes>``
|
2011-01-05 16:31:04 +00:00
|
|
|
Topics, as they are called in OpenLP, are fully supported, whereby only
|
2011-01-06 09:34:26 +00:00
|
|
|
the topic text (e. g. Grace) is considered, but neither the *id* nor
|
|
|
|
*lang*.
|
2011-01-05 16:31:04 +00:00
|
|
|
|
2011-02-10 05:25:08 +00:00
|
|
|
``<transposition>``
|
2011-01-05 16:31:04 +00:00
|
|
|
This property is not supported.
|
|
|
|
|
2011-02-10 05:25:08 +00:00
|
|
|
``<variant>``
|
2011-01-08 20:59:46 +00:00
|
|
|
This property is not supported.
|
2011-01-05 16:31:04 +00:00
|
|
|
|
2011-02-10 05:25:08 +00:00
|
|
|
``<verse name="v1a" lang="he" translit="en">``
|
2011-01-24 19:27:59 +00:00
|
|
|
The attribute *translit* is not supported. Note, the attribute *lang* is
|
2011-08-20 17:06:48 +00:00
|
|
|
considered, but there is not further functionality implemented yet. The
|
|
|
|
following verse "types" are supported by OpenLP:
|
|
|
|
|
|
|
|
* v
|
|
|
|
* c
|
|
|
|
* b
|
|
|
|
* p
|
|
|
|
* i
|
|
|
|
* e
|
|
|
|
* o
|
|
|
|
|
|
|
|
The verse "types" stand for *Verse*, *Chorus*, *Bridge*, *Pre-Chorus*,
|
|
|
|
*Intro*, *Ending* and *Other*. Any numeric value is allowed after the
|
|
|
|
verse type. The complete verse name in OpenLP always consists of the
|
|
|
|
verse type and the verse number. If not number is present *1* is
|
|
|
|
assumed.
|
2011-08-20 17:39:38 +00:00
|
|
|
OpenLP will merge verses which are split up by appending a letter to the
|
|
|
|
verse name, such as *v1a*.
|
2011-01-05 16:31:04 +00:00
|
|
|
|
2011-02-10 05:25:08 +00:00
|
|
|
``<verseOrder>``
|
2011-01-05 16:31:04 +00:00
|
|
|
OpenLP supports this property.
|
2011-02-10 05:25:08 +00:00
|
|
|
|
2011-01-05 16:31:04 +00:00
|
|
|
"""
|
2011-08-20 17:06:48 +00:00
|
|
|
IMPLEMENTED_VERSION = u'0.8'
|
2011-10-09 19:45:50 +00:00
|
|
|
START_TAGS_REGEX = re.compile(r'\{(\w+)\}')
|
|
|
|
END_TAGS_REGEX = re.compile(r'\{\/(\w+)\}')
|
2012-09-25 20:15:56 +00:00
|
|
|
VERSE_TAG_SPLITTER = re.compile(u'([a-zA-Z]+)([0-9]*)([a-zA-Z]?)')
|
2011-09-06 13:22:17 +00:00
|
|
|
|
2011-01-05 16:31:04 +00:00
|
|
|
def __init__(self, manager):
|
|
|
|
self.manager = manager
|
|
|
|
|
2011-01-11 16:49:53 +00:00
|
|
|
def song_to_xml(self, song):
|
2011-01-09 16:52:31 +00:00
|
|
|
"""
|
|
|
|
Convert the song to OpenLyrics Format.
|
|
|
|
"""
|
|
|
|
sxml = SongXML()
|
2011-01-24 19:27:59 +00:00
|
|
|
song_xml = objectify.fromstring(u'<song/>')
|
|
|
|
# Append the necessary meta data to the song.
|
2011-09-17 23:22:16 +00:00
|
|
|
song_xml.set(u'xmlns', NAMESPACE)
|
2011-01-24 19:27:59 +00:00
|
|
|
song_xml.set(u'version', OpenLyrics.IMPLEMENTED_VERSION)
|
2011-08-14 10:56:05 +00:00
|
|
|
application_name = u'OpenLP ' + get_application_version()[u'version']
|
|
|
|
song_xml.set(u'createdIn', application_name)
|
|
|
|
song_xml.set(u'modifiedIn', application_name)
|
2011-12-27 10:33:55 +00:00
|
|
|
# "Convert" 2012-08-27 11:49:15 to 2012-08-27T11:49:15.
|
2011-08-27 14:00:24 +00:00
|
|
|
song_xml.set(u'modifiedDate',
|
|
|
|
unicode(song.last_modified).replace(u' ', u'T'))
|
2011-01-09 16:52:31 +00:00
|
|
|
properties = etree.SubElement(song_xml, u'properties')
|
|
|
|
titles = etree.SubElement(properties, u'titles')
|
2011-03-14 18:59:59 +00:00
|
|
|
self._add_text_to_element(u'title', titles, song.title)
|
2011-01-09 16:52:31 +00:00
|
|
|
if song.alternate_title:
|
2011-03-14 18:59:59 +00:00
|
|
|
self._add_text_to_element(u'title', titles, song.alternate_title)
|
2011-01-09 16:52:31 +00:00
|
|
|
if song.comments:
|
|
|
|
comments = etree.SubElement(properties, u'comments')
|
|
|
|
self._add_text_to_element(u'comment', comments, song.comments)
|
|
|
|
if song.copyright:
|
|
|
|
self._add_text_to_element(u'copyright', properties, song.copyright)
|
|
|
|
if song.verse_order:
|
|
|
|
self._add_text_to_element(
|
2011-01-24 19:27:59 +00:00
|
|
|
u'verseOrder', properties, song.verse_order.lower())
|
2011-01-09 16:52:31 +00:00
|
|
|
if song.ccli_number:
|
|
|
|
self._add_text_to_element(u'ccliNo', properties, song.ccli_number)
|
|
|
|
if song.authors:
|
|
|
|
authors = etree.SubElement(properties, u'authors')
|
|
|
|
for author in song.authors:
|
|
|
|
self._add_text_to_element(
|
|
|
|
u'author', authors, author.display_name)
|
|
|
|
book = self.manager.get_object_filtered(
|
|
|
|
Book, Book.id == song.song_book_id)
|
|
|
|
if book is not None:
|
|
|
|
book = book.name
|
|
|
|
songbooks = etree.SubElement(properties, u'songbooks')
|
|
|
|
element = self._add_text_to_element(
|
|
|
|
u'songbook', songbooks, None, book)
|
2011-02-10 17:42:13 +00:00
|
|
|
if song.song_number:
|
|
|
|
element.set(u'entry', song.song_number)
|
2011-01-09 16:52:31 +00:00
|
|
|
if song.topics:
|
|
|
|
themes = etree.SubElement(properties, u'themes')
|
|
|
|
for topic in song.topics:
|
|
|
|
self._add_text_to_element(u'theme', themes, topic.name)
|
2011-08-27 14:00:24 +00:00
|
|
|
# Process the formatting tags.
|
2011-09-15 12:23:40 +00:00
|
|
|
# Have we any tags in song lyrics?
|
2011-09-07 20:54:53 +00:00
|
|
|
tags_element = None
|
|
|
|
match = re.search(u'\{/?\w+\}', song.lyrics, re.UNICODE)
|
2011-09-06 13:14:57 +00:00
|
|
|
if match:
|
2012-05-05 16:03:40 +00:00
|
|
|
# Named 'format_' - 'format' is built-in fuction in Python.
|
2011-09-22 14:23:51 +00:00
|
|
|
format_ = etree.SubElement(song_xml, u'format')
|
|
|
|
tags_element = etree.SubElement(format_, u'tags')
|
2011-09-07 20:54:53 +00:00
|
|
|
tags_element.set(u'application', u'OpenLP')
|
2011-03-27 13:39:43 +00:00
|
|
|
# Process the song's lyrics.
|
2011-01-09 16:52:31 +00:00
|
|
|
lyrics = etree.SubElement(song_xml, u'lyrics')
|
2011-03-27 13:39:43 +00:00
|
|
|
verse_list = sxml.get_verses(song.lyrics)
|
2012-09-25 20:15:56 +00:00
|
|
|
# Add a suffix letter to each verse
|
|
|
|
verse_tags = []
|
2011-01-09 16:52:31 +00:00
|
|
|
for verse in verse_list:
|
2011-03-27 13:39:43 +00:00
|
|
|
verse_tag = verse[0][u'type'][0].lower()
|
|
|
|
verse_number = verse[0][u'label']
|
2011-08-20 17:06:48 +00:00
|
|
|
verse_def = verse_tag + verse_number
|
2012-09-25 20:15:56 +00:00
|
|
|
verse_tags.append(verse_def)
|
|
|
|
# Create the letter from the number of duplicates
|
|
|
|
verse[0][u'suffix'] = chr(96 + verse_tags.count(verse_def))
|
|
|
|
# If the verse tag is a duplicate use the suffix letter
|
|
|
|
for verse in verse_list:
|
|
|
|
verse_tag = verse[0][u'type'][0].lower()
|
|
|
|
verse_number = verse[0][u'label']
|
|
|
|
verse_def = verse_tag + verse_number
|
|
|
|
if verse_tags.count(verse_def) > 1:
|
|
|
|
verse_def += verse[0][u'suffix']
|
2011-08-20 17:06:48 +00:00
|
|
|
verse_element = \
|
|
|
|
self._add_text_to_element(u'verse', lyrics, None, verse_def)
|
2011-09-06 13:22:17 +00:00
|
|
|
if u'lang' in verse[0]:
|
2011-08-20 17:06:48 +00:00
|
|
|
verse_element.set(u'lang', verse[0][u'lang'])
|
2012-05-05 14:03:48 +00:00
|
|
|
# Create a list with all "optional" verses.
|
2012-05-05 13:59:04 +00:00
|
|
|
optional_verses = cgi.escape(verse[1])
|
2012-05-05 14:25:55 +00:00
|
|
|
optional_verses = optional_verses.split(u'\n[---]\n')
|
2012-05-05 13:59:04 +00:00
|
|
|
start_tags = u''
|
|
|
|
end_tags = u''
|
|
|
|
for index, optional_verse in enumerate(optional_verses):
|
2012-05-05 14:08:07 +00:00
|
|
|
# Fix up missing end and start tags such as {r} or {/r}.
|
2012-05-05 13:59:04 +00:00
|
|
|
optional_verse = start_tags + optional_verse
|
2012-05-05 14:12:11 +00:00
|
|
|
start_tags, end_tags = self._get_missing_tags(optional_verse)
|
2012-05-05 13:59:04 +00:00
|
|
|
optional_verse += end_tags
|
2011-09-20 23:06:43 +00:00
|
|
|
# Add formatting tags to text
|
2011-09-22 14:23:51 +00:00
|
|
|
lines_element = self._add_text_with_tags_to_lines(verse_element,
|
2012-05-05 13:59:04 +00:00
|
|
|
optional_verse, tags_element)
|
2011-08-20 17:06:48 +00:00
|
|
|
# Do not add the break attribute to the last lines element.
|
2012-05-05 13:59:04 +00:00
|
|
|
if index < len(optional_verses) - 1:
|
2011-08-20 17:06:48 +00:00
|
|
|
lines_element.set(u'break', u'optional')
|
2011-01-11 16:49:53 +00:00
|
|
|
return self._extract_xml(song_xml)
|
2011-01-09 16:52:31 +00:00
|
|
|
|
2012-05-05 14:12:11 +00:00
|
|
|
def _get_missing_tags(self, text):
|
2012-05-05 13:59:04 +00:00
|
|
|
"""
|
|
|
|
Tests the given text for not closed formatting tags and returns a tuple
|
|
|
|
consisting of two unicode strings::
|
|
|
|
|
|
|
|
(u'{st}{r}', u'{/r}{/st}')
|
|
|
|
|
|
|
|
The first unicode string are the start tags (for the next slide). The
|
|
|
|
second unicode string are the end tags.
|
|
|
|
|
|
|
|
``text``
|
|
|
|
The text to test. The text must **not** contain html tags, only
|
|
|
|
OpenLP formatting tags are allowed::
|
|
|
|
|
|
|
|
{st}{r}Text text text
|
|
|
|
"""
|
|
|
|
tags = []
|
|
|
|
for tag in FormattingTags.get_html_tags():
|
|
|
|
if tag[u'start tag'] == u'{br}':
|
|
|
|
continue
|
|
|
|
if text.count(tag[u'start tag']) != text.count(tag[u'end tag']):
|
|
|
|
tags.append((text.find(tag[u'start tag']),
|
|
|
|
tag[u'start tag'], tag[u'end tag']))
|
|
|
|
# Sort the lists, so that the tags which were opened first on the first
|
|
|
|
# slide (the text we are checking) will be opened first on the next
|
|
|
|
# slide as well.
|
|
|
|
tags.sort(key=lambda tag: tag[0])
|
|
|
|
end_tags = []
|
|
|
|
start_tags = []
|
|
|
|
for tag in tags:
|
|
|
|
start_tags.append(tag[1])
|
|
|
|
end_tags.append(tag[2])
|
|
|
|
end_tags.reverse()
|
|
|
|
return u''.join(start_tags), u''.join(end_tags)
|
|
|
|
|
2011-12-05 19:47:50 +00:00
|
|
|
def xml_to_song(self, xml, parse_and_temporary_save=False):
|
2010-11-27 15:25:00 +00:00
|
|
|
"""
|
2011-01-03 21:47:49 +00:00
|
|
|
Create and save a song from OpenLyrics format xml to the database. Since
|
|
|
|
we also export XML from external sources (e. g. OpenLyrics import), we
|
|
|
|
cannot ensure, that it completely conforms to the OpenLyrics standard.
|
2011-01-06 08:13:45 +00:00
|
|
|
|
|
|
|
``xml``
|
|
|
|
The XML to parse (unicode).
|
2011-08-26 15:06:22 +00:00
|
|
|
|
2011-12-05 19:47:50 +00:00
|
|
|
``parse_and_temporary_save``
|
|
|
|
Switch to skip processing the whole song and storing the songs in
|
|
|
|
the database with a temporary flag. Defaults to ``False``.
|
2010-11-27 15:25:00 +00:00
|
|
|
"""
|
2011-01-03 07:16:21 +00:00
|
|
|
# No xml get out of here.
|
2010-11-29 16:02:41 +00:00
|
|
|
if not xml:
|
2011-01-11 16:49:53 +00:00
|
|
|
return None
|
2010-11-27 20:51:54 +00:00
|
|
|
if xml[:5] == u'<?xml':
|
|
|
|
xml = xml[38:]
|
2010-11-28 13:39:51 +00:00
|
|
|
song_xml = objectify.fromstring(xml)
|
2011-02-20 17:34:57 +00:00
|
|
|
if hasattr(song_xml, u'properties'):
|
2011-01-20 05:42:27 +00:00
|
|
|
properties = song_xml.properties
|
2011-02-20 17:34:57 +00:00
|
|
|
else:
|
2011-01-20 05:42:27 +00:00
|
|
|
return None
|
2011-09-17 23:22:16 +00:00
|
|
|
# Formatting tags are new in OpenLyrics 0.8
|
|
|
|
if float(song_xml.get(u'version')) > 0.7:
|
2011-12-05 19:47:50 +00:00
|
|
|
self._process_formatting_tags(song_xml, parse_and_temporary_save)
|
2011-01-20 05:42:27 +00:00
|
|
|
song = Song()
|
2011-08-27 14:00:24 +00:00
|
|
|
# Values will be set when cleaning the song.
|
|
|
|
song.search_lyrics = u''
|
|
|
|
song.verse_order = u''
|
|
|
|
song.search_title = u''
|
2011-12-05 19:47:50 +00:00
|
|
|
song.temporary = parse_and_temporary_save
|
2011-08-27 14:00:24 +00:00
|
|
|
self._process_copyright(properties, song)
|
|
|
|
self._process_cclinumber(properties, song)
|
|
|
|
self._process_titles(properties, song)
|
2011-01-06 08:13:45 +00:00
|
|
|
# The verse order is processed with the lyrics!
|
2011-08-20 17:15:31 +00:00
|
|
|
self._process_lyrics(properties, song_xml, song)
|
2011-08-27 14:00:24 +00:00
|
|
|
self._process_comments(properties, song)
|
|
|
|
self._process_authors(properties, song)
|
|
|
|
self._process_songbooks(properties, song)
|
|
|
|
self._process_topics(properties, song)
|
|
|
|
clean_song(self.manager, song)
|
|
|
|
self.manager.save_object(song)
|
2011-08-28 20:51:44 +00:00
|
|
|
return song
|
2010-11-27 15:25:00 +00:00
|
|
|
|
2011-01-09 16:52:31 +00:00
|
|
|
def _add_text_to_element(self, tag, parent, text=None, label=None):
|
|
|
|
if label:
|
|
|
|
element = etree.Element(tag, name=unicode(label))
|
|
|
|
else:
|
|
|
|
element = etree.Element(tag)
|
|
|
|
if text:
|
|
|
|
element.text = unicode(text)
|
|
|
|
parent.append(element)
|
|
|
|
return element
|
|
|
|
|
2011-09-07 23:16:14 +00:00
|
|
|
def _add_tag_to_formatting(self, tag_name, tags_element):
|
2011-09-15 12:23:40 +00:00
|
|
|
"""
|
2011-10-08 13:20:57 +00:00
|
|
|
Add new formatting tag to the element ``<format>`` if the tag is not
|
|
|
|
present yet.
|
2011-09-15 12:23:40 +00:00
|
|
|
"""
|
2011-09-07 23:16:14 +00:00
|
|
|
available_tags = FormattingTags.get_html_tags()
|
|
|
|
start_tag = '{%s}' % tag_name
|
2011-10-08 13:20:57 +00:00
|
|
|
for tag in available_tags:
|
|
|
|
if tag[u'start tag'] == start_tag:
|
2011-09-15 17:44:03 +00:00
|
|
|
# Create new formatting tag in openlyrics xml.
|
2011-10-08 13:20:57 +00:00
|
|
|
element = self._add_text_to_element(u'tag', tags_element)
|
|
|
|
element.set(u'name', tag_name)
|
|
|
|
element_open = self._add_text_to_element(u'open', element)
|
|
|
|
element_open.text = etree.CDATA(tag[u'start html'])
|
2011-09-20 23:06:43 +00:00
|
|
|
# Check if formatting tag contains end tag. Some formatting
|
|
|
|
# tags e.g. {br} has only start tag. If no end tag is present
|
|
|
|
# <close> element has not to be in OpenLyrics xml.
|
2011-10-08 13:20:57 +00:00
|
|
|
if tag['end tag']:
|
|
|
|
element_close = self._add_text_to_element(u'close', element)
|
|
|
|
element_close.text = etree.CDATA(tag[u'end html'])
|
2011-09-20 23:06:43 +00:00
|
|
|
|
|
|
|
def _add_text_with_tags_to_lines(self, verse_element, text, tags_element):
|
2011-09-15 12:23:40 +00:00
|
|
|
"""
|
|
|
|
Convert text with formatting tags from OpenLP format to OpenLyrics
|
|
|
|
format and append it to element ``<lines>``.
|
|
|
|
"""
|
2011-10-08 13:20:57 +00:00
|
|
|
start_tags = OpenLyrics.START_TAGS_REGEX.findall(text)
|
|
|
|
end_tags = OpenLyrics.END_TAGS_REGEX.findall(text)
|
2011-09-15 12:23:40 +00:00
|
|
|
# Replace start tags with xml syntax.
|
2011-09-07 23:16:14 +00:00
|
|
|
for tag in start_tags:
|
2011-09-20 23:06:43 +00:00
|
|
|
# Tags already converted to xml structure.
|
|
|
|
xml_tags = tags_element.xpath(u'tag/attribute::name')
|
|
|
|
# Some formatting tag has only starting part e.g. <br>.
|
|
|
|
# Handle this case.
|
|
|
|
if tag in end_tags:
|
|
|
|
text = text.replace(u'{%s}' % tag, u'<tag name="%s">' % tag)
|
|
|
|
else:
|
|
|
|
text = text.replace(u'{%s}' % tag, u'<tag name="%s"/>' % tag)
|
2011-09-16 11:31:11 +00:00
|
|
|
# Add tag to <format> element if tag not present.
|
2011-09-20 23:06:43 +00:00
|
|
|
if tag not in xml_tags:
|
|
|
|
self._add_tag_to_formatting(tag, tags_element)
|
2011-09-15 12:23:40 +00:00
|
|
|
# Replace end tags.
|
2011-10-08 13:20:57 +00:00
|
|
|
for tag in end_tags:
|
|
|
|
text = text.replace(u'{/%s}' % tag, u'</tag>')
|
2011-09-20 23:06:43 +00:00
|
|
|
# Replace \n with <br/>.
|
|
|
|
text = text.replace(u'\n', u'<br/>')
|
2011-10-09 19:51:44 +00:00
|
|
|
element = etree.XML(u'<lines>%s</lines>' % text)
|
2011-09-20 23:06:43 +00:00
|
|
|
verse_element.append(element)
|
|
|
|
return element
|
2011-09-07 20:54:53 +00:00
|
|
|
|
2011-01-11 16:49:53 +00:00
|
|
|
def _extract_xml(self, xml):
|
2011-01-09 16:52:31 +00:00
|
|
|
"""
|
|
|
|
Extract our newly created XML song.
|
|
|
|
"""
|
|
|
|
return etree.tostring(xml, encoding=u'UTF-8',
|
2011-01-11 16:49:53 +00:00
|
|
|
xml_declaration=True)
|
2011-01-09 16:52:31 +00:00
|
|
|
|
2011-01-03 21:47:49 +00:00
|
|
|
def _text(self, element):
|
|
|
|
"""
|
2011-01-06 08:13:45 +00:00
|
|
|
This returns the text of an element as unicode string.
|
2011-01-03 21:47:49 +00:00
|
|
|
|
|
|
|
``element``
|
|
|
|
The element.
|
|
|
|
"""
|
|
|
|
if element.text is not None:
|
|
|
|
return unicode(element.text)
|
|
|
|
return u''
|
|
|
|
|
2011-01-05 16:31:04 +00:00
|
|
|
def _process_authors(self, properties, song):
|
2010-11-27 15:25:00 +00:00
|
|
|
"""
|
2011-01-06 08:13:45 +00:00
|
|
|
Adds the authors specified in the XML to the song.
|
|
|
|
|
|
|
|
``properties``
|
|
|
|
The property object (lxml.objectify.ObjectifiedElement).
|
|
|
|
|
|
|
|
``song``
|
|
|
|
The song object.
|
2010-11-27 15:25:00 +00:00
|
|
|
"""
|
2011-01-05 16:31:04 +00:00
|
|
|
authors = []
|
2011-02-20 17:34:57 +00:00
|
|
|
if hasattr(properties, u'authors'):
|
2011-01-05 16:31:04 +00:00
|
|
|
for author in properties.authors.author:
|
|
|
|
display_name = self._text(author)
|
|
|
|
if display_name:
|
|
|
|
authors.append(display_name)
|
|
|
|
for display_name in authors:
|
|
|
|
author = self.manager.get_object_filtered(Author,
|
|
|
|
Author.display_name == display_name)
|
|
|
|
if author is None:
|
|
|
|
# We need to create a new author, as the author does not exist.
|
|
|
|
author = Author.populate(display_name=display_name,
|
|
|
|
last_name=display_name.split(u' ')[-1],
|
|
|
|
first_name=u' '.join(display_name.split(u' ')[:-1]))
|
2011-02-25 01:03:25 +00:00
|
|
|
song.authors.append(author)
|
2010-11-27 15:25:00 +00:00
|
|
|
|
2011-01-06 08:13:45 +00:00
|
|
|
def _process_cclinumber(self, properties, song):
|
|
|
|
"""
|
|
|
|
Adds the CCLI number to the song.
|
|
|
|
|
|
|
|
``properties``
|
|
|
|
The property object (lxml.objectify.ObjectifiedElement).
|
|
|
|
|
|
|
|
``song``
|
|
|
|
The song object.
|
|
|
|
"""
|
2011-02-20 17:34:57 +00:00
|
|
|
if hasattr(properties, u'ccliNo'):
|
2011-01-06 08:13:45 +00:00
|
|
|
song.ccli_number = self._text(properties.ccliNo)
|
|
|
|
|
2011-01-05 16:31:04 +00:00
|
|
|
def _process_comments(self, properties, song):
|
2010-11-27 15:25:00 +00:00
|
|
|
"""
|
2011-01-06 08:13:45 +00:00
|
|
|
Joins the comments specified in the XML and add it to the song.
|
|
|
|
|
|
|
|
``properties``
|
|
|
|
The property object (lxml.objectify.ObjectifiedElement).
|
|
|
|
|
|
|
|
``song``
|
|
|
|
The song object.
|
2010-11-27 15:25:00 +00:00
|
|
|
"""
|
2011-02-20 17:34:57 +00:00
|
|
|
if hasattr(properties, u'comments'):
|
2011-02-24 05:47:38 +00:00
|
|
|
comments_list = []
|
2011-01-06 08:13:45 +00:00
|
|
|
for comment in properties.comments.comment:
|
2012-04-21 22:29:08 +00:00
|
|
|
comment_text = self._text(comment)
|
|
|
|
if comment_text:
|
|
|
|
comments_list.append(comment_text)
|
2011-01-06 08:13:45 +00:00
|
|
|
song.comments = u'\n'.join(comments_list)
|
2010-11-28 13:39:51 +00:00
|
|
|
|
2011-01-06 08:13:45 +00:00
|
|
|
def _process_copyright(self, properties, song):
|
2010-11-28 13:39:51 +00:00
|
|
|
"""
|
2011-01-06 08:13:45 +00:00
|
|
|
Adds the copyright to the song.
|
|
|
|
|
|
|
|
``properties``
|
|
|
|
The property object (lxml.objectify.ObjectifiedElement).
|
|
|
|
|
|
|
|
``song``
|
|
|
|
The song object.
|
|
|
|
"""
|
2011-02-20 17:34:57 +00:00
|
|
|
if hasattr(properties, u'copyright'):
|
2011-01-06 08:13:45 +00:00
|
|
|
song.copyright = self._text(properties.copyright)
|
|
|
|
|
2011-08-27 14:00:24 +00:00
|
|
|
def _process_formatting_tags(self, song_xml, temporary):
|
|
|
|
"""
|
|
|
|
Process the formatting tags from the song and either add missing tags
|
|
|
|
temporary or permanently to the formatting tag list.
|
|
|
|
"""
|
|
|
|
if not hasattr(song_xml, u'format'):
|
|
|
|
return
|
|
|
|
found_tags = []
|
|
|
|
for tag in song_xml.format.tags.getchildren():
|
|
|
|
name = tag.get(u'name')
|
|
|
|
if name is None:
|
|
|
|
continue
|
2011-09-20 15:52:19 +00:00
|
|
|
start_tag = u'{%s}' % name[:5]
|
|
|
|
# Some tags have only start tag e.g. {br}
|
2011-09-22 14:23:51 +00:00
|
|
|
end_tag = u'{/' + name[:5] + u'}' if hasattr(tag, 'close') else u''
|
2011-08-27 14:00:24 +00:00
|
|
|
openlp_tag = {
|
|
|
|
u'desc': name,
|
2011-09-20 15:52:19 +00:00
|
|
|
u'start tag': start_tag,
|
|
|
|
u'end tag': end_tag,
|
2011-08-27 14:00:24 +00:00
|
|
|
u'start html': tag.open.text,
|
2011-09-20 15:52:19 +00:00
|
|
|
# Some tags have only start html e.g. {br}
|
|
|
|
u'end html': tag.close.text if hasattr(tag, 'close') else u'',
|
2011-08-27 14:00:24 +00:00
|
|
|
u'protected': False,
|
|
|
|
}
|
2011-09-22 20:30:15 +00:00
|
|
|
# Add 'temporary' key in case the formatting tag should not be
|
|
|
|
# saved otherwise it is supposed that formatting tag is permanent.
|
|
|
|
if temporary:
|
|
|
|
openlp_tag[u'temporary'] = temporary
|
2011-08-27 14:00:24 +00:00
|
|
|
found_tags.append(openlp_tag)
|
|
|
|
existing_tag_ids = [tag[u'start tag']
|
|
|
|
for tag in FormattingTags.get_html_tags()]
|
2011-09-22 14:23:51 +00:00
|
|
|
new_tags = [tag for tag in found_tags
|
|
|
|
if tag[u'start tag'] not in existing_tag_ids]
|
2012-05-05 16:03:40 +00:00
|
|
|
FormattingTags.add_html_tags(new_tags)
|
|
|
|
FormattingTags.save_html_tags()
|
2011-08-27 14:00:24 +00:00
|
|
|
|
2011-09-20 15:52:19 +00:00
|
|
|
def _process_lines_mixed_content(self, element, newlines=True):
|
2011-09-18 23:00:55 +00:00
|
|
|
"""
|
|
|
|
Converts the xml text with mixed content to OpenLP representation.
|
|
|
|
Chords are skipped and formatting tags are converted.
|
|
|
|
|
|
|
|
``element``
|
|
|
|
The property object (lxml.etree.Element).
|
2011-09-20 15:52:19 +00:00
|
|
|
|
|
|
|
``newlines``
|
|
|
|
The switch to enable/disable processing of line breaks <br/>.
|
|
|
|
The <br/> is used since OpenLyrics 0.8.
|
2011-09-18 23:00:55 +00:00
|
|
|
"""
|
2011-09-17 23:22:16 +00:00
|
|
|
text = u''
|
2011-09-20 15:52:19 +00:00
|
|
|
use_endtag = True
|
|
|
|
# Skip <comment> elements - not yet supported.
|
2011-09-23 00:12:55 +00:00
|
|
|
if element.tag == NSMAP % u'comment':
|
|
|
|
if element.tail:
|
|
|
|
# Append tail text at chord element.
|
|
|
|
text += element.tail
|
2011-09-20 15:52:19 +00:00
|
|
|
return text
|
|
|
|
# Skip <chord> element - not yet supported.
|
2011-09-23 00:12:55 +00:00
|
|
|
elif element.tag == NSMAP % u'chord':
|
|
|
|
if element.tail:
|
|
|
|
# Append tail text at chord element.
|
|
|
|
text += element.tail
|
2011-09-20 15:52:19 +00:00
|
|
|
return text
|
|
|
|
# Convert line breaks <br/> to \n.
|
|
|
|
elif newlines and element.tag == NSMAP % u'br':
|
|
|
|
text += u'\n'
|
|
|
|
if element.tail:
|
|
|
|
text += element.tail
|
|
|
|
return text
|
2011-09-17 23:22:16 +00:00
|
|
|
# Start formatting tag.
|
2011-09-20 15:52:19 +00:00
|
|
|
if element.tag == NSMAP % u'tag':
|
2011-09-17 23:22:16 +00:00
|
|
|
text += u'{%s}' % element.get(u'name')
|
2011-09-20 15:52:19 +00:00
|
|
|
# Some formattings may have only start tag.
|
|
|
|
# Handle this case if element has no children and contains no text.
|
2012-04-29 15:31:56 +00:00
|
|
|
if not element and not element.text:
|
2011-09-20 15:52:19 +00:00
|
|
|
use_endtag = False
|
2011-09-18 23:00:55 +00:00
|
|
|
# Append text from element.
|
2011-09-17 23:22:16 +00:00
|
|
|
if element.text:
|
|
|
|
text += element.text
|
2011-09-18 23:00:55 +00:00
|
|
|
# Process nested formatting tags.
|
2011-09-17 23:22:16 +00:00
|
|
|
for child in element:
|
|
|
|
# Use recursion since nested formatting tags are allowed.
|
2011-09-20 15:52:19 +00:00
|
|
|
text += self._process_lines_mixed_content(child, newlines)
|
2011-09-17 23:22:16 +00:00
|
|
|
# Append text from tail and add formatting end tag.
|
2011-09-20 15:52:19 +00:00
|
|
|
if element.tag == NSMAP % 'tag' and use_endtag:
|
2011-09-17 23:22:16 +00:00
|
|
|
text += u'{/%s}' % element.get(u'name')
|
2011-09-18 23:00:55 +00:00
|
|
|
# Append text from tail.
|
2011-09-17 23:22:16 +00:00
|
|
|
if element.tail:
|
|
|
|
text += element.tail
|
|
|
|
return text
|
|
|
|
|
2011-09-23 00:12:55 +00:00
|
|
|
def _process_verse_lines(self, lines, version):
|
2011-09-18 23:00:55 +00:00
|
|
|
"""
|
2011-09-20 15:52:19 +00:00
|
|
|
Converts lyrics lines to OpenLP representation.
|
2011-09-18 23:00:55 +00:00
|
|
|
|
2011-09-20 15:52:19 +00:00
|
|
|
``lines``
|
|
|
|
The lines object (lxml.objectify.ObjectifiedElement).
|
2011-09-18 23:00:55 +00:00
|
|
|
"""
|
2011-09-20 15:52:19 +00:00
|
|
|
text = u''
|
2011-09-18 23:00:55 +00:00
|
|
|
# Convert lxml.objectify to lxml.etree representation.
|
2011-09-20 15:52:19 +00:00
|
|
|
lines = etree.tostring(lines)
|
|
|
|
element = etree.XML(lines)
|
2011-09-23 00:12:55 +00:00
|
|
|
|
|
|
|
# OpenLyrics 0.8 uses <br/> for new lines.
|
|
|
|
# Append text from "lines" element to verse text.
|
|
|
|
if version > '0.7':
|
|
|
|
text = self._process_lines_mixed_content(element)
|
2011-09-20 15:52:19 +00:00
|
|
|
# OpenLyrics version <= 0.7 contais <line> elements to represent lines.
|
|
|
|
# First child element is tested.
|
2011-09-23 00:12:55 +00:00
|
|
|
else:
|
2011-09-20 15:52:19 +00:00
|
|
|
# Loop over the "line" elements removing comments and chords.
|
|
|
|
for line in element:
|
2011-09-23 00:12:55 +00:00
|
|
|
# Skip comment lines.
|
|
|
|
if line.tag == NSMAP % u'comment':
|
|
|
|
continue
|
2011-09-20 15:52:19 +00:00
|
|
|
if text:
|
|
|
|
text += u'\n'
|
|
|
|
text += self._process_lines_mixed_content(line, newlines=False)
|
|
|
|
return text
|
2011-09-17 23:22:16 +00:00
|
|
|
|
2011-08-20 17:15:31 +00:00
|
|
|
def _process_lyrics(self, properties, song_xml, song_obj):
|
2011-01-06 08:13:45 +00:00
|
|
|
"""
|
|
|
|
Processes the verses and search_lyrics for the song.
|
|
|
|
|
|
|
|
``properties``
|
|
|
|
The properties object (lxml.objectify.ObjectifiedElement).
|
|
|
|
|
2011-08-20 17:15:31 +00:00
|
|
|
``song_xml``
|
|
|
|
The objectified song (lxml.objectify.ObjectifiedElement).
|
2011-01-06 08:13:45 +00:00
|
|
|
|
2011-08-20 17:15:31 +00:00
|
|
|
``song_obj``
|
2011-01-06 08:13:45 +00:00
|
|
|
The song object.
|
2011-01-05 16:31:04 +00:00
|
|
|
"""
|
2011-01-09 16:52:31 +00:00
|
|
|
sxml = SongXML()
|
2011-03-27 13:39:43 +00:00
|
|
|
verses = {}
|
2011-03-30 07:13:04 +00:00
|
|
|
verse_def_list = []
|
2011-12-15 11:53:51 +00:00
|
|
|
try:
|
|
|
|
lyrics = song_xml.lyrics
|
|
|
|
except AttributeError:
|
2012-03-09 06:19:39 +00:00
|
|
|
raise OpenLyricsError(OpenLyricsError.LyricsError,
|
|
|
|
'<lyrics> tag is missing.',
|
2012-03-06 19:56:49 +00:00
|
|
|
unicode(translate('OpenLP.OpenLyricsImportError',
|
2012-03-09 06:19:39 +00:00
|
|
|
'<lyrics> tag is missing.')))
|
2011-12-15 11:53:51 +00:00
|
|
|
try:
|
2012-03-11 16:03:18 +00:00
|
|
|
verse_list = lyrics.verse
|
2011-12-15 11:53:51 +00:00
|
|
|
except AttributeError:
|
2012-03-09 06:19:39 +00:00
|
|
|
raise OpenLyricsError(OpenLyricsError.VerseError,
|
|
|
|
'<verse> tag is missing.',
|
2012-03-06 19:56:49 +00:00
|
|
|
unicode(translate('OpenLP.OpenLyricsImportError',
|
2012-03-09 06:19:39 +00:00
|
|
|
'<verse> tag is missing.')))
|
2011-08-22 13:39:02 +00:00
|
|
|
# Loop over the "verse" elements.
|
2012-03-11 16:03:18 +00:00
|
|
|
for verse in verse_list:
|
2011-01-05 16:31:04 +00:00
|
|
|
text = u''
|
2011-08-22 13:39:02 +00:00
|
|
|
# Loop over the "lines" elements.
|
2011-01-05 16:31:04 +00:00
|
|
|
for lines in verse.lines:
|
|
|
|
if text:
|
|
|
|
text += u'\n'
|
2011-09-19 12:59:37 +00:00
|
|
|
# Append text from "lines" element to verse text.
|
2011-09-23 00:12:55 +00:00
|
|
|
text += self._process_verse_lines(lines,
|
|
|
|
version=song_xml.get(u'version'))
|
2012-04-18 21:55:44 +00:00
|
|
|
# Add an optional split to the verse text.
|
2011-08-20 17:15:31 +00:00
|
|
|
if lines.get(u'break') is not None:
|
2011-08-20 17:06:48 +00:00
|
|
|
text += u'\n[---]'
|
2011-08-20 17:15:31 +00:00
|
|
|
verse_def = verse.get(u'name', u' ').lower()
|
2012-09-25 20:15:56 +00:00
|
|
|
verse_tag, verse_number, verse_part = \
|
|
|
|
OpenLyrics.VERSE_TAG_SPLITTER.search(verse_def).groups()
|
|
|
|
if verse_tag not in VerseType.Tags:
|
2011-03-17 20:38:05 +00:00
|
|
|
verse_tag = VerseType.Tags[VerseType.Other]
|
2011-03-13 14:39:55 +00:00
|
|
|
# OpenLyrics allows e. g. "c", but we need "c1". However, this does
|
|
|
|
# not correct the verse order.
|
|
|
|
if not verse_number:
|
|
|
|
verse_number = u'1'
|
2011-08-20 17:15:31 +00:00
|
|
|
lang = verse.get(u'lang')
|
2012-10-16 17:26:23 +00:00
|
|
|
translit = verse.get(u'translit')
|
2011-08-20 17:39:38 +00:00
|
|
|
# In OpenLP 1.9.6 we used v1a, v1b ... to represent visual slide
|
|
|
|
# breaks. In OpenLyrics 0.7 an attribute has been added.
|
|
|
|
if song_xml.get(u'modifiedIn') in (u'1.9.6', u'OpenLP 1.9.6') and \
|
|
|
|
song_xml.get(u'version') == u'0.7' and \
|
2012-10-18 20:22:48 +00:00
|
|
|
(verse_tag, verse_number, lang, translit) in verses:
|
|
|
|
verses[(verse_tag, verse_number, lang, translit, None)] += u'\n[---]\n' + text
|
2011-08-20 17:39:38 +00:00
|
|
|
# Merge v1a, v1b, .... to v1.
|
2012-10-16 17:26:23 +00:00
|
|
|
elif (verse_tag, verse_number, lang, translit, verse_part) in verses:
|
|
|
|
verses[(verse_tag, verse_number, lang, translit, verse_part)] += u'\n' + text
|
2011-03-27 13:39:43 +00:00
|
|
|
else:
|
2012-10-16 17:26:23 +00:00
|
|
|
verses[(verse_tag, verse_number, lang, translit, verse_part)] = text
|
|
|
|
verse_def_list.append((verse_tag, verse_number, lang, translit, verse_part))
|
2011-03-30 07:13:04 +00:00
|
|
|
# We have to use a list to keep the order, as dicts are not sorted.
|
|
|
|
for verse in verse_def_list:
|
2011-03-27 13:39:43 +00:00
|
|
|
sxml.add_verse_to_lyrics(
|
|
|
|
verse[0], verse[1], verses[verse], verse[2])
|
2011-08-20 17:39:38 +00:00
|
|
|
song_obj.lyrics = unicode(sxml.extract_xml(), u'utf-8')
|
2011-01-06 08:13:45 +00:00
|
|
|
# Process verse order
|
2011-02-20 17:34:57 +00:00
|
|
|
if hasattr(properties, u'verseOrder'):
|
2011-08-20 17:39:38 +00:00
|
|
|
song_obj.verse_order = self._text(properties.verseOrder)
|
2011-01-03 07:16:21 +00:00
|
|
|
|
2011-01-05 16:31:04 +00:00
|
|
|
def _process_songbooks(self, properties, song):
|
2011-01-03 07:16:21 +00:00
|
|
|
"""
|
2011-01-06 08:13:45 +00:00
|
|
|
Adds the song book and song number specified in the XML to the song.
|
|
|
|
|
|
|
|
``properties``
|
|
|
|
The property object (lxml.objectify.ObjectifiedElement).
|
|
|
|
|
|
|
|
``song``
|
|
|
|
The song object.
|
2011-01-05 16:31:04 +00:00
|
|
|
"""
|
2011-05-30 11:17:23 +00:00
|
|
|
song.song_book_id = None
|
2011-01-05 16:31:04 +00:00
|
|
|
song.song_number = u''
|
2011-02-20 17:34:57 +00:00
|
|
|
if hasattr(properties, u'songbooks'):
|
2011-01-05 16:31:04 +00:00
|
|
|
for songbook in properties.songbooks.songbook:
|
2012-04-21 22:29:08 +00:00
|
|
|
book_name = songbook.get(u'name', u'')
|
|
|
|
if book_name:
|
2011-01-05 16:31:04 +00:00
|
|
|
book = self.manager.get_object_filtered(Book,
|
2012-04-21 22:29:08 +00:00
|
|
|
Book.name == book_name)
|
2011-01-05 16:31:04 +00:00
|
|
|
if book is None:
|
|
|
|
# We need to create a book, because it does not exist.
|
2012-04-21 22:29:08 +00:00
|
|
|
book = Book.populate(name=book_name, publisher=u'')
|
2011-01-05 16:31:04 +00:00
|
|
|
self.manager.save_object(book)
|
|
|
|
song.song_book_id = book.id
|
2011-08-20 17:15:31 +00:00
|
|
|
song.song_number = songbook.get(u'entry', u'')
|
2011-01-11 16:49:53 +00:00
|
|
|
# We only support one song book, so take the first one.
|
2011-01-05 16:31:04 +00:00
|
|
|
break
|
2011-01-03 07:16:21 +00:00
|
|
|
|
2011-01-05 16:31:04 +00:00
|
|
|
def _process_titles(self, properties, song):
|
|
|
|
"""
|
2011-01-06 08:13:45 +00:00
|
|
|
Processes the titles specified in the song's XML.
|
|
|
|
|
|
|
|
``properties``
|
|
|
|
The property object (lxml.objectify.ObjectifiedElement).
|
|
|
|
|
|
|
|
``song``
|
|
|
|
The song object.
|
2011-01-03 07:16:21 +00:00
|
|
|
"""
|
2011-01-05 16:31:04 +00:00
|
|
|
for title in properties.titles.title:
|
|
|
|
if not song.title:
|
|
|
|
song.title = self._text(title)
|
|
|
|
song.alternate_title = u''
|
|
|
|
else:
|
|
|
|
song.alternate_title = self._text(title)
|
2011-01-03 13:43:09 +00:00
|
|
|
|
2011-01-05 16:31:04 +00:00
|
|
|
def _process_topics(self, properties, song):
|
2011-01-03 13:43:09 +00:00
|
|
|
"""
|
2011-01-06 08:13:45 +00:00
|
|
|
Adds the topics to the song.
|
|
|
|
|
|
|
|
``properties``
|
|
|
|
The property object (lxml.objectify.ObjectifiedElement).
|
|
|
|
|
|
|
|
``song``
|
|
|
|
The song object.
|
2011-01-05 16:31:04 +00:00
|
|
|
"""
|
2011-02-20 17:34:57 +00:00
|
|
|
if hasattr(properties, u'themes'):
|
2012-04-21 22:29:08 +00:00
|
|
|
for topic_text in properties.themes.theme:
|
|
|
|
topic_text = self._text(topic_text)
|
|
|
|
if topic_text:
|
2011-01-06 08:13:45 +00:00
|
|
|
topic = self.manager.get_object_filtered(Topic,
|
2012-04-21 22:29:08 +00:00
|
|
|
Topic.name == topic_text)
|
2011-01-06 08:13:45 +00:00
|
|
|
if topic is None:
|
|
|
|
# We need to create a topic, because it does not exist.
|
2012-04-21 22:29:08 +00:00
|
|
|
topic = Topic.populate(name=topic_text)
|
2011-01-06 08:13:45 +00:00
|
|
|
self.manager.save_object(topic)
|
|
|
|
song.topics.append(topic)
|
2011-01-09 16:52:31 +00:00
|
|
|
|
|
|
|
def _dump_xml(self, xml):
|
|
|
|
"""
|
|
|
|
Debugging aid to dump XML so that we can see what we have.
|
|
|
|
"""
|
|
|
|
return etree.tostring(xml, encoding=u'UTF-8',
|
|
|
|
xml_declaration=True, pretty_print=True)
|
2011-12-17 17:26:59 +00:00
|
|
|
|
|
|
|
|
2011-12-17 17:47:08 +00:00
|
|
|
class OpenLyricsError(Exception):
|
2012-03-09 06:19:39 +00:00
|
|
|
# XML tree is missing the lyrics tag
|
|
|
|
LyricsError = 1
|
|
|
|
# XML tree has no verse tags
|
|
|
|
VerseError = 2
|
|
|
|
|
|
|
|
def __init__(self, type, log_message, display_message):
|
2012-04-21 22:29:08 +00:00
|
|
|
Exception.__init__(self)
|
2012-03-09 06:19:39 +00:00
|
|
|
self.type = type
|
|
|
|
self.log_message = log_message
|
2012-03-06 19:56:49 +00:00
|
|
|
self.display_message = display_message
|