Merge branch 'liveworship' into 'master'

LiveWorship importer

See merge request openlp/openlp!30
This commit is contained in:
Tim Bentley 2019-10-07 17:12:38 +00:00
commit fc71461a06
6 changed files with 817 additions and 19 deletions

View File

@ -222,6 +222,16 @@ def is_linux():
return sys.platform.startswith('linux') return sys.platform.startswith('linux')
def is_64bit_instance():
"""
Returns true if the python/OpenLP instance running is 64 bit. If running a 32 bit instance on
a 64 bit system this will return false.
:return: True if the python/OpenLP instance running is 64 bit, otherwise False.
"""
return (sys.maxsize > 2**32)
def verify_ipv4(addr): def verify_ipv4(addr):
""" """
Validate an IPv4 address Validate an IPv4 address

View File

@ -34,6 +34,7 @@ from .importers.dreambeam import DreamBeamImport
from .importers.easyslides import EasySlidesImport from .importers.easyslides import EasySlidesImport
from .importers.easyworship import EasyWorshipSongImport from .importers.easyworship import EasyWorshipSongImport
from .importers.foilpresenter import FoilPresenterImport from .importers.foilpresenter import FoilPresenterImport
from .importers.liveworship import LiveWorshipImport
from .importers.lyrix import LyrixImport from .importers.lyrix import LyrixImport
from .importers.openlp import OpenLPSongImport from .importers.openlp import OpenLPSongImport
from .importers.openlyrics import OpenLyricsImport from .importers.openlyrics import OpenLyricsImport
@ -166,25 +167,26 @@ class SongFormat(object):
EasyWorshipSqliteDB = 8 EasyWorshipSqliteDB = 8
EasyWorshipService = 9 EasyWorshipService = 9
FoilPresenter = 10 FoilPresenter = 10
Lyrix = 11 LiveWorship = 11
MediaShout = 12 Lyrix = 12
OpenSong = 13 MediaShout = 13
OPSPro = 14 OpenSong = 14
PowerPraise = 15 OPSPro = 15
PowerSong = 16 PowerPraise = 16
PresentationManager = 17 PowerSong = 17
ProPresenter = 18 PresentationManager = 18
SingingTheFaith = 19 ProPresenter = 19
SongBeamer = 20 SingingTheFaith = 20
SongPro = 21 SongBeamer = 21
SongShowPlus = 22 SongPro = 22
SongsOfFellowship = 23 SongShowPlus = 23
SundayPlus = 24 SongsOfFellowship = 24
VideoPsalm = 25 SundayPlus = 25
WordsOfWorship = 26 VideoPsalm = 26
WorshipAssistant = 27 WordsOfWorship = 27
WorshipCenterPro = 28 WorshipAssistant = 28
ZionWorx = 29 WorshipCenterPro = 29
ZionWorx = 30
# Set optional attribute defaults # Set optional attribute defaults
__defaults__ = { __defaults__ = {
@ -282,6 +284,14 @@ class SongFormat(object):
'filter': '{text} (*.foil)'.format(text=translate('SongsPlugin.ImportWizardForm', 'filter': '{text} (*.foil)'.format(text=translate('SongsPlugin.ImportWizardForm',
'Foilpresenter Song Files')) 'Foilpresenter Song Files'))
}, },
LiveWorship: {
'class': LiveWorshipImport,
'name': 'LiveWorship Database',
'prefix': 'liveWorship',
'selectMode': SongFormatSelect.SingleFile,
'filter': '{text} (*.vdb)'.format(text=translate('SongsPlugin.ImportWizardForm',
'LiveWorship Database'))
},
Lyrix: { Lyrix: {
'class': LyrixImport, 'class': LyrixImport,
'name': 'LyriX', 'name': 'LyriX',
@ -466,6 +476,7 @@ class SongFormat(object):
SongFormat.EasyWorshipSqliteDB, SongFormat.EasyWorshipSqliteDB,
SongFormat.EasyWorshipService, SongFormat.EasyWorshipService,
SongFormat.FoilPresenter, SongFormat.FoilPresenter,
SongFormat.LiveWorship,
SongFormat.Lyrix, SongFormat.Lyrix,
SongFormat.MediaShout, SongFormat.MediaShout,
SongFormat.OpenSong, SongFormat.OpenSong,

View File

@ -0,0 +1,287 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
##########################################################################
# 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/>. #
##########################################################################
"""
The :mod:`liveworship` module provides the functionality for importing
a LiveWorship database into the OpenLP database.
"""
import os
import logging
import ctypes
from lxml import etree
from pathlib import Path
from tempfile import gettempdir
from openlp.core.common import is_win, is_linux, is_macosx, is_64bit_instance, delete_file
from openlp.core.common.i18n import translate
from openlp.core.lib.ui import critical_error_message_box
from openlp.plugins.songs.lib.importers.songimport import SongImport
from openlp.plugins.songs.lib.ui import SongStrings
# Copied from VCSDK_Enums.h
EVStorageType_kDisk = 1
EVDumpType_kSQL = 1
EVDumpType_kXML = 2
EVDataKind_kStructureAndRecords = 2
EVDataKind_kRecordsOnly = 3
pretty_print = 1
log = logging.getLogger(__name__)
class LiveWorshipImport(SongImport):
"""
The :class:`LiveWorshipImport` class provides the ability to import the
LiveWorship Valentina Database.
The approach is to use the Valentina DB ADK for C via ctypes to dump the database content to a XML
file that is then analysed for data extraction. It would also be possible to skip the XML and
extract the data directly from the database using ctypes, but that approach requires a lot of ctypes
interaction and type conversion that is rather fragile across platforms and versions, so the XML approach
is used for now.
It was implemented using Valentina DB ADK for C version 9.6.
"""
def __init__(self, manager, **kwargs):
"""
Initialise the LiveWorship importer.
"""
self.root = None
super(LiveWorshipImport, self).__init__(manager, **kwargs)
def do_import(self):
"""
Receive a path to a LiveWorship (valentina) DB.
"""
self.dump_file = Path(gettempdir()) / 'openlp-liveworship-dump.xml'
if not self.dump_valentina_to_xml():
return
self.load_xml_dump()
if self.root is None:
return
self.extract_songs()
delete_file(self.dump_file)
def dump_valentina_to_xml(self):
"""
Load the LiveWorship database using the Valentina DB ADK for C and dump the DB content to a XML file.
"""
self.import_wizard.increment_progress_bar(translate('SongsPlugin.LiveWorshipImport',
'Extracting data from database'), 0)
# Based on OS and bitness, try to load the dll
libVCSDK = None
if is_win():
# The DLL path must be set depending on the bitness of the OpenLP/Python instance
if is_64bit_instance():
vcdk_install_folder = 'VCDK_x64_{adkver}'
dll_name = '/vcsdk_release_x64.dll'
else:
vcdk_install_folder = 'VCDK_{adkver}'
dll_name = '/vcsdk_release_x86.dll'
dll_path = '{pf}\\Paradigma Software\\{vcdk}'.format(pf=os.getenv('PROGRAMFILES'), vcdk=vcdk_install_folder)
dll_path2 = '{pf}\\Paradigma Software\\vcomponents_win_vc'.format(pf=os.getenv('PROGRAMFILES'))
os.environ['PATH'] = ';'.join([os.environ['PATH'], dll_path, dll_path2])
libVCSDK_path = dll_path + dll_name
elif is_linux():
libVCSDK_path = '/opt/VCSDK/libVCSDK.so'
elif is_macosx():
# The DLL path must be set depending on the bitness of the OpenLP/Python instance
if is_64bit_instance():
vcdk_install_folder = 'VCDK_x64_{adkver}'
dll_name = '/vcsdk_x64.dylib'
else:
vcdk_install_folder = 'VCDK_{adkver}'
dll_name = '/vcsdk_x86.dylib'
libVCSDK_path = '/Users/Shared/Paradigma Software/{folder}/{dll}'.format(folder=vcdk_install_folder,
dll=dll_name)
# Try to make this somewhat future proof by trying versions 9 to 15
found_dll = False
if '{adkver}' in libVCSDK_path:
for i in range(9, 16):
if os.path.exists(libVCSDK_path.format(adkver=i)):
found_dll = True
libVCSDK_path = libVCSDK_path.format(adkver=i)
break
if not found_dll:
libVCSDK_path = libVCSDK_path.format(adkver=9)
elif os.path.exists(libVCSDK_path):
found_dll = True
if not found_dll:
adk_name = "Valentina DB ADK for C, {bitness} bit"
if is_64bit_instance():
adk_name = adk_name.format(bitness=64)
else:
adk_name = adk_name.format(bitness=32)
critical_error_message_box(translate('SongsPlugin.LiveWorshipImport',
'Could not find Valentina DB ADK libraries '),
translate('SongsPlugin.LiveWorshipImport',
'Could not find "{dllpath}", please install "{adk}"'
.format(dllpath=libVCSDK_path, adk=adk_name)))
return False
libVCSDK = ctypes.CDLL(libVCSDK_path)
# cache size set to 1024, got no idea what this means...
# serial numbers set to None - only 10 minutes access, should be enough :)
libVCSDK.Valentina_Init(1024, None, None, None)
# Create a DB instance
Database_New = libVCSDK.Database_New
Database_New.argtypes = [ctypes.c_int]
Database_New.restype = ctypes.c_void_p
database = Database_New(EVStorageType_kDisk)
database_ptr = ctypes.c_void_p(database)
# Load the file into our instance
libVCSDK.Database_Open(database_ptr, ctypes.c_char_p(str(self.import_source).encode()))
# Dump the database to XML
libVCSDK.Database_Dump(database_ptr, ctypes.c_char_p(str(self.dump_file).encode()), EVDumpType_kXML,
EVDataKind_kStructureAndRecords, pretty_print, ctypes.c_char_p(b'utf-8'))
# Close the DB
libVCSDK.Database_Close(database_ptr)
# Shutdown Valentina
libVCSDK.Valentina_Shutdown()
return True
def load_xml_dump(self):
self.import_wizard.increment_progress_bar(translate('SongsPlugin.LiveWorshipImport',
'Loading the extracting data'), 0)
# The file can contain the EOT control character and certain invalid tags with missing attributewhich
# will make lxml fail, so it must be removed.
xml_file = open(self.dump_file, 'rt')
xml_content = xml_file.read()
xml_file.close()
xml_content = xml_content.replace('\4', '**EOT**').replace('CustomProperty =""', 'CustomProperty a=""', 1)
# Now load the XML
parser = etree.XMLParser(remove_blank_text=True, recover=True)
try:
self.root = etree.fromstring(xml_content, parser)
except etree.XMLSyntaxError:
self.log_error(self.dump_file, SongStrings.XMLSyntaxError)
log.exception('XML syntax error in file {path}'.format(path=str(self.dump_file)))
def extract_songs(self):
"""
Extract all the songs from the XML object
"""
# Find song records
xpath = "//BaseObjectData[@Name='SlideCol']/Record/f[@n='Type' and normalize-space(text())='song']/.."
song_records = self.root.xpath(xpath)
# Song count for progress bar
song_count = len(song_records)
# set progress bar to songcount
self.import_wizard.progress_bar.setMaximum(song_count)
for record in song_records:
if self.stop_import_flag:
break
# reset to default values
self.set_defaults()
# Get song metadata
title = record.xpath("f[@n='Title']/text()")
song_rowid = record.xpath("f[@n='_rowid']/text()")
if title and song_rowid:
self.title = self.clean_string(title[0])
song_rowid = self.clean_string(song_rowid[0])
else:
# if no title or no rowid we skip the song
continue
authors_line = record.xpath("f[@n='Author']/text()")
if authors_line:
self.extract_authors(authors_line[0])
cpr = record.xpath("f[@n='Copyright']/text()")
if cpr:
self.add_copyright(self.clean_string(cpr[0]))
ccli = record.xpath("f[@n='CCLI']/text()")
if ccli:
self.ccli = self.clean_string(ccli[0])
# Get song tags
self.extract_tags(song_rowid)
# Get song verses
self.extract_verses(song_rowid)
if not self.finish():
self.log_error(self.title)
def extract_tags(self, song_id):
"""
Extract the tags for a particular song
"""
xpath = "//BaseObjectData[@Name='TagGroup']/Record/f[@n='SlideCol_rowid' "\
"and normalize-space(text())='{rowid}']/.."
tag_group_records = self.root.xpath(xpath.format(rowid=song_id))
for record in tag_group_records:
tag_rowid = record.xpath("f[@n='Tag_rowid']/text()")
if tag_rowid:
tag_rowid = self.clean_string(tag_rowid[0])
xpath = "//BaseObjectData[@Name='Tag']/Record/f[@n='_rowid' and normalize-space(text())='{rowid}']/"\
"../f[@n='Description']/text()"
tag = self.root.xpath(xpath.format(rowid=tag_rowid))
if tag:
tag = self.clean_string(tag[0])
self.topics.append(tag)
def extract_verses(self, song_id):
"""
Extract the verses for a particular song
"""
xpath = "//BaseObjectData[@Name='SlideColSlides']/Record/f[@n='SlideCol_rowid' and "\
"normalize-space(text())='{rowid}']/.."
slides_records = self.root.xpath(xpath.format(rowid=song_id))
for record in slides_records:
verse_text = record.xpath("f[@n='kText']/text()")
if verse_text:
verse_text = self.clean_verse(verse_text[0])
verse_tag = record.xpath("f[@n='Description']/text()")
if verse_tag:
verse_tag = self.convert_verse_name(verse_tag[0])
else:
verse_tag = 'v'
self.add_verse(verse_text, verse_tag)
def extract_authors(self, authors_line):
"""
Extract the authors as a list of str from the authors record
"""
if not authors_line:
return
for author_name in authors_line.split('/'):
name_parts = [self.clean_string(part) for part in author_name.split(',')][::-1]
self.parse_author(' '.join(name_parts))
def clean_string(self, string):
"""
Clean up the strings
"""
# &#x09; is tab
return string.replace('^`^', '\'').replace('/', '-').replace('&#x09;', ' ').strip()
def clean_verse(self, verse_line):
"""
Extract the verse lines from the verse record
"""
# &#x0D; is carriage return
return self.clean_string(verse_line.replace('&#x0D;', '\n'))
def convert_verse_name(self, verse_name):
"""
Convert the Verse # to v#
"""
name_parts = verse_name.lower().split()
if len(name_parts) > 1:
return name_parts[0][0] + name_parts[1]
else:
return name_parts[0][0]

View File

@ -0,0 +1,93 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
##########################################################################
# 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/>. #
##########################################################################
"""
This module contains tests for the LiveWorship song importer.
"""
from unittest import TestCase
from unittest.mock import MagicMock, patch
from tests.utils import load_external_result_data
from tests.utils.constants import RESOURCE_PATH
from openlp.core.common.registry import Registry
from openlp.plugins.songs.lib.importers.liveworship import LiveWorshipImport
TEST_PATH = RESOURCE_PATH / 'songs' / 'liveworship'
def _get_item(data, key):
"""
Get an item or return a blank string
"""
if key in data:
return data[key]
return ''
class TestLiveWorshipSongImport(TestCase):
"""
Test the functions in the :mod:`liveworshipimport` module.
"""
def setUp(self):
"""
Create the registry
"""
Registry.create()
@patch('openlp.plugins.songs.lib.importers.liveworship.SongImport')
def test_create_importer(self, mocked_songimport):
"""
Test creating an instance of the LiveWorship file importer
"""
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
mocked_manager = MagicMock()
# WHEN: An importer object is created
importer = LiveWorshipImport(mocked_manager, file_paths=[])
# THEN: The importer object should not be None
assert importer is not None, 'Import should not be none'
@patch('openlp.plugins.songs.lib.importers.liveworship.SongImport')
def test_parse_xml_dump(self, mocked_songimport):
"""
Test importing a simple XML dump in LiveWorshipImport
"""
# GIVEN: A mocked out SongImport class, a mocked out "manager" and a simple XML dump
mocked_manager = MagicMock()
importer = LiveWorshipImport(mocked_manager, file_paths=[])
importer.finish = MagicMock()
importer.import_wizard = MagicMock()
importer.dump_file = TEST_PATH / 'valentina-db-simplified-dump.xml'
# WHEN: The XML is loaded and processed
importer.load_xml_dump()
importer.extract_songs()
# THEN: The imported data should look like expected
result_data = load_external_result_data(TEST_PATH / 'A Child Of The King.json')
assert importer.title == _get_item(result_data, 'title')
assert importer.verses == _get_item(result_data, 'verses')
assert importer.topics[0] == _get_item(result_data, 'topics')[0]
assert importer.authors[0][0] == _get_item(result_data, 'authors')[0]
assert importer.authors[1][0] == _get_item(result_data, 'authors')[1]

View File

@ -0,0 +1,35 @@
{
"title": "A Child Of The King",
"authors": [
"Harriet Eugenia Peck Buell",
"John Bunnell Sumner"
],
"topics": [ "Worship" ],
"verses": [
[
"v1",
"My Father is rich in houses and lands,\nHe holdeth the wealth of the world in His hands!\nOf rubies and diamonds, of silver and gold,\nHis coffers are full, He has riches untold.",
null
],
[
"v2",
"My Father's own Son, the Savior of men,\nOnce wandered on earth as the poorest of them;\nBut now He is pleading our pardon on high,\nThat we may be His when He comes by and by.",
null
],
[
"v3",
"I once was an outcast stranger on earth,\nA sinner by choice, an alien by birth,\nBut I've been adopted, my name's written down,\nAn heir to a mansion, a robe and a crown.",
null
],
[
"v4",
"A tent or a cottage, why should I care?\nThey're building a palace for me over there;\nThough exiled from home, yet still may I sing:\nAll glory to God, I'm a child of the King.",
null
],
[
"c1",
"I'm a child of the King,\nA child of the King:\nWith Jesus my Savior,\nI'm a child of the King.",
null
]
]
}

View File

@ -0,0 +1,362 @@
<?xml version="1.1"?>
<Database>
<DatabaseDesc Name="Ebenezer7.vdb">
<DatabaseMode>1</DatabaseMode>
<DatabaseSegment>32768</DatabaseSegment>
<SchemaVersion>7</SchemaVersion>
<IdentsCaseSensitive>0</IdentsCaseSensitive>
<DateFormat>0</DateFormat>
<DateSeparator>/</DateSeparator>
<TimeSeparator>:</TimeSeparator>
<KeyDelimiter>.</KeyDelimiter>
<LocaleInfo>
<LocaleName>en_US_POSIX</LocaleName>
<CollationAttributes>
<FrenchCollation>16</FrenchCollation>
<AlternateHandling>21</AlternateHandling>
<CaseFirst>16</CaseFirst>
<CaseLevel>16</CaseLevel>
<NormalizationMode>16</NormalizationMode>
<Strength>2</Strength>
<HiraganaQuaternaryMode>16</HiraganaQuaternaryMode>
<NumericCollation>16</NumericCollation>
</CollationAttributes>
<IOEncoding>UTF-16</IOEncoding>
<StorageEncoding>UTF-16</StorageEncoding>
</LocaleInfo>
<SequenceCount>0</SequenceCount>
<KeyValueCount>0</KeyValueCount>
<TypeCount>0</TypeCount>
<Types>
</Types>
<IndexStyleCount>0</IndexStyleCount>
<BaseObjectCount>16</BaseObjectCount>
<LinkCount>0</LinkCount>
<ViewCount>0</ViewCount>
<SToredProcedureCount>0</SToredProcedureCount>
<TriggerCount>0</TriggerCount>
</DatabaseDesc>
<DatabaseData>
<DatabaseStructureData>
</DatabaseStructureData>
<DatabaseUserData>
<BaseObjectData Name="SlideCol">
<Record>
<f n="RecID">1</f>
<f n="_rowid"> 1.000000</f>
<f n="Type">song</f>
<f n="Title">A Charge To Keep I Have</f>
<f n="CreateDate">00/00/0000 00:00:00:000</f>
<f n="Author">Wesley, Charles / Mason, Lowell</f>
<f n="Copyright">Public Domain </f>
<f n="CCLI" />
<f n="UseCount"> 0.000000</f>
</Record>
<Record>
<f n="RecID">2</f>
<f n="_rowid"> 2.000000</f>
<f n="Type">song</f>
<f n="Title">A Child Of The King</f>
<f n="CreateDate">00/00/0000 00:00:00:000</f>
<f n="Author">Buell, Harriet Eugenia Peck / Sumner, John Bunnell</f>
<f n="Copyright">Public Domain </f>
<f n="CCLI" />
<f n="UseCount"> 0.000000</f>
</Record>
</BaseObjectData>
<BaseObjectData Name="SlideColSlides">
<RecordCount>9478</RecordCount>
<Record>
<f n="RecID">1</f>
<f n="_rowid"> 1.000000</f>
<f n="SlideCol_rowid"> 1.000000</f>
<f n="Description">Verse 1</f>
<f n="Delay"> 0.000000</f>
<f n="AudioDone">0</f>
<f n="AudioId"> 0.000000</f>
<f n="AudioPath" />
<f n="BkColor" />
<f n="BkDone">0</f>
<f n="BKFullScreen">0</f>
<f n="BkId"> 0.000000</f>
<f n="BkPath" />
<f n="BkSize"> 0.000000</f>
<f n="BkStretch">0</f>
<f n="BkX"> 0.000000</f>
<f n="BkY"> 0.000000</f>
<f n="kText"><![CDATA[A charge to keep I have,&#x0D;A God to glorify,&#x0D;A never dying soul to save,&#x0D;And fit it for the sky.]]></f>
<f n="TextDone">0</f>
<f n="TextDS">0</f>
<f n="TextDSColor" />
<f n="TextDSOffSet"> 0.000000</f>
<f n="TextHalign" />
<f n="TextId"> 0.000000</f>
<f n="StyledText" />
<f n="TextValign" />
<f n="TextX1"> 0.000000</f>
<f n="TextX2"> 0.000000</f>
</Record>
<Record>
<f n="RecID">2</f>
<f n="_rowid"> 2.000000</f>
<f n="SlideCol_rowid"> 1.000000</f>
<f n="Description">Verse 2</f>
<f n="Delay"> 0.000000</f>
<f n="AudioDone">0</f>
<f n="AudioId"> 0.000000</f>
<f n="AudioPath" />
<f n="BkColor" />
<f n="BkDone">0</f>
<f n="BKFullScreen">0</f>
<f n="BkId"> 0.000000</f>
<f n="BkPath" />
<f n="BkSize"> 0.000000</f>
<f n="BkStretch">0</f>
<f n="BkX"> 0.000000</f>
<f n="BkY"> 0.000000</f>
<f n="kText"><![CDATA[To serve the present age,&#x0D;My calling to fulfill;&#x0D;O may it all my powr^`^s engage&#x0D;To do my Master^`^s will!]]></f>
<f n="TextDone">0</f>
<f n="TextDS">0</f>
<f n="TextDSColor" />
<f n="TextDSOffSet"> 0.000000</f>
<f n="TextHalign" />
<f n="TextId"> 0.000000</f>
<f n="StyledText" />
<f n="TextValign" />
<f n="TextX1"> 0.000000</f>
<f n="TextX2"> 0.000000</f>
</Record>
<Record>
<f n="RecID">3</f>
<f n="_rowid"> 3.000000</f>
<f n="SlideCol_rowid"> 1.000000</f>
<f n="Description">Verse 3</f>
<f n="Delay"> 0.000000</f>
<f n="AudioDone">0</f>
<f n="AudioId"> 0.000000</f>
<f n="AudioPath" />
<f n="BkColor" />
<f n="BkDone">0</f>
<f n="BKFullScreen">0</f>
<f n="BkId"> 0.000000</f>
<f n="BkPath" />
<f n="BkSize"> 0.000000</f>
<f n="BkStretch">0</f>
<f n="BkX"> 0.000000</f>
<f n="BkY"> 0.000000</f>
<f n="kText"><![CDATA[Arm me with jealous care,&#x0D;As in Thy sight to live,&#x0D;And O, Thy servant, Lord, prepare,&#x0D;A strict account to give!]]></f>
<f n="TextDone">0</f>
<f n="TextDS">0</f>
<f n="TextDSColor" />
<f n="TextDSOffSet"> 0.000000</f>
<f n="TextHalign" />
<f n="TextId"> 0.000000</f>
<f n="StyledText" />
<f n="TextValign" />
<f n="TextX1"> 0.000000</f>
<f n="TextX2"> 0.000000</f>
</Record>
<Record>
<f n="RecID">4</f>
<f n="_rowid"> 4.000000</f>
<f n="SlideCol_rowid"> 1.000000</f>
<f n="Description">Verse 4</f>
<f n="Delay"> 0.000000</f>
<f n="AudioDone">0</f>
<f n="AudioId"> 0.000000</f>
<f n="AudioPath" />
<f n="BkColor" />
<f n="BkDone">0</f>
<f n="BKFullScreen">0</f>
<f n="BkId"> 0.000000</f>
<f n="BkPath" />
<f n="BkSize"> 0.000000</f>
<f n="BkStretch">0</f>
<f n="BkX"> 0.000000</f>
<f n="BkY"> 0.000000</f>
<f n="kText"><![CDATA[Help me to watch and pray,&#x0D;And on Thyself rely,&#x0D;Assured, if I my trust betray,&#x0D;I shall forever die.]]></f>
<f n="TextDone">0</f>
<f n="TextDS">0</f>
<f n="TextDSColor" />
<f n="TextDSOffSet"> 0.000000</f>
<f n="TextHalign" />
<f n="TextId"> 0.000000</f>
<f n="StyledText" />
<f n="TextValign" />
<f n="TextX1"> 0.000000</f>
<f n="TextX2"> 0.000000</f>
</Record>
<Record>
<f n="RecID">5</f>
<f n="_rowid"> 5.000000</f>
<f n="SlideCol_rowid"> 2.000000</f>
<f n="Description">Verse 1</f>
<f n="Delay"> 0.000000</f>
<f n="AudioDone">0</f>
<f n="AudioId"> 0.000000</f>
<f n="AudioPath" />
<f n="BkColor" />
<f n="BkDone">0</f>
<f n="BKFullScreen">0</f>
<f n="BkId"> 0.000000</f>
<f n="BkPath" />
<f n="BkSize"> 0.000000</f>
<f n="BkStretch">0</f>
<f n="BkX"> 0.000000</f>
<f n="BkY"> 0.000000</f>
<f n="kText"><![CDATA[My Father is rich in houses and lands,&#x0D;He holdeth the wealth of the world in His hands!&#x0D;Of rubies and diamonds, of silver and gold,&#x0D;His coffers are full, He has riches untold.]]></f>
<f n="TextDone">0</f>
<f n="TextDS">0</f>
<f n="TextDSColor" />
<f n="TextDSOffSet"> 0.000000</f>
<f n="TextHalign" />
<f n="TextId"> 0.000000</f>
<f n="StyledText" />
<f n="TextValign" />
<f n="TextX1"> 0.000000</f>
<f n="TextX2"> 0.000000</f>
</Record>
<Record>
<f n="RecID">6</f>
<f n="_rowid"> 6.000000</f>
<f n="SlideCol_rowid"> 2.000000</f>
<f n="Description">Verse 2</f>
<f n="Delay"> 0.000000</f>
<f n="AudioDone">0</f>
<f n="AudioId"> 0.000000</f>
<f n="AudioPath" />
<f n="BkColor" />
<f n="BkDone">0</f>
<f n="BKFullScreen">0</f>
<f n="BkId"> 0.000000</f>
<f n="BkPath" />
<f n="BkSize"> 0.000000</f>
<f n="BkStretch">0</f>
<f n="BkX"> 0.000000</f>
<f n="BkY"> 0.000000</f>
<f n="kText"><![CDATA[My Father^`^s own Son, the Savior of men,&#x0D;Once wandered on earth as the poorest of them;&#x0D;But now He is pleading our pardon on high,&#x0D;That we may be His when He comes by and by.]]></f>
<f n="TextDone">0</f>
<f n="TextDS">0</f>
<f n="TextDSColor" />
<f n="TextDSOffSet"> 0.000000</f>
<f n="TextHalign" />
<f n="TextId"> 0.000000</f>
<f n="StyledText" />
<f n="TextValign" />
<f n="TextX1"> 0.000000</f>
<f n="TextX2"> 0.000000</f>
</Record>
<Record>
<f n="RecID">7</f>
<f n="_rowid"> 7.000000</f>
<f n="SlideCol_rowid"> 2.000000</f>
<f n="Description">Verse 3</f>
<f n="Delay"> 0.000000</f>
<f n="AudioDone">0</f>
<f n="AudioId"> 0.000000</f>
<f n="AudioPath" />
<f n="BkColor" />
<f n="BkDone">0</f>
<f n="BKFullScreen">0</f>
<f n="BkId"> 0.000000</f>
<f n="BkPath" />
<f n="BkSize"> 0.000000</f>
<f n="BkStretch">0</f>
<f n="BkX"> 0.000000</f>
<f n="BkY"> 0.000000</f>
<f n="kText"><![CDATA[I once was an outcast stranger on earth,&#x0D;A sinner by choice, an alien by birth,&#x0D;But I^`^ve been adopted, my name^`^s written down,&#x0D;An heir to a mansion, a robe and a crown.]]></f>
<f n="TextDone">0</f>
<f n="TextDS">0</f>
<f n="TextDSColor" />
<f n="TextDSOffSet"> 0.000000</f>
<f n="TextHalign" />
<f n="TextId"> 0.000000</f>
<f n="StyledText" />
<f n="TextValign" />
<f n="TextX1"> 0.000000</f>
<f n="TextX2"> 0.000000</f>
</Record>
<Record>
<f n="RecID">8</f>
<f n="_rowid"> 8.000000</f>
<f n="SlideCol_rowid"> 2.000000</f>
<f n="Description">Verse 4</f>
<f n="Delay"> 0.000000</f>
<f n="AudioDone">0</f>
<f n="AudioId"> 0.000000</f>
<f n="AudioPath" />
<f n="BkColor" />
<f n="BkDone">0</f>
<f n="BKFullScreen">0</f>
<f n="BkId"> 0.000000</f>
<f n="BkPath" />
<f n="BkSize"> 0.000000</f>
<f n="BkStretch">0</f>
<f n="BkX"> 0.000000</f>
<f n="BkY"> 0.000000</f>
<f n="kText"><![CDATA[A tent or a cottage, why should I care?&#x0D;They^`^re building a palace for me over there;&#x0D;Though exiled from home, yet still may I sing:&#x0D;All glory to God, I^`^m a child of the King.]]></f>
<f n="TextDone">0</f>
<f n="TextDS">0</f>
<f n="TextDSColor" />
<f n="TextDSOffSet"> 0.000000</f>
<f n="TextHalign" />
<f n="TextId"> 0.000000</f>
<f n="StyledText" />
<f n="TextValign" />
<f n="TextX1"> 0.000000</f>
<f n="TextX2"> 0.000000</f>
</Record>
<Record>
<f n="RecID">9</f>
<f n="_rowid"> 9.000000</f>
<f n="SlideCol_rowid"> 2.000000</f>
<f n="Description">Chorus</f>
<f n="Delay"> 0.000000</f>
<f n="AudioDone">0</f>
<f n="AudioId"> 0.000000</f>
<f n="AudioPath" />
<f n="BkColor" />
<f n="BkDone">0</f>
<f n="BKFullScreen">0</f>
<f n="BkId"> 0.000000</f>
<f n="BkPath" />
<f n="BkSize"> 0.000000</f>
<f n="BkStretch">0</f>
<f n="BkX"> 0.000000</f>
<f n="BkY"> 0.000000</f>
<f n="kText"><![CDATA[I^`^m a child of the King,&#x0D;A child of the King:&#x0D;With Jesus my Savior,&#x0D;I^`^m a child of the King.]]></f>
<f n="TextDone">0</f>
<f n="TextDS">0</f>
<f n="TextDSColor" />
<f n="TextDSOffSet"> 0.000000</f>
<f n="TextHalign" />
<f n="TextId"> 0.000000</f>
<f n="StyledText" />
<f n="TextValign" />
<f n="TextX1"> 0.000000</f>
<f n="TextX2"> 0.000000</f>
</Record>
</BaseObjectData>
<BaseObjectData Name="TagGroup">
<RecordCount>158</RecordCount>
<Record>
<f n="RecID">1</f>
<f n="_rowid"> 854.000000</f>
<f n="Tag_rowid"> 28.000000</f>
<f n="Lib_rowid"> 0.000000</f>
<f n="gText_rowid"> 0.000000</f>
<f n="SlideCol_rowid"> 2.000000</f>
<f n="Presentation_rowid"> 0.000000</f>
</Record>
</BaseObjectData>
<BaseObjectData Name="Tag">
<RecordCount>11</RecordCount>
<Record>
<f n="RecID">1</f>
<f n="_rowid"> 28.000000</f>
<f n="MediaType_rowid"> 6.000000</f>
<f n="Description"> Worship</f>
</Record>
</BaseObjectData>
</DatabaseUserData>
</DatabaseData></Database>