forked from openlp/openlp
bugfixing and sync with trunk
This commit is contained in:
commit
81be1f36ae
@ -27,3 +27,9 @@
|
|||||||
"""
|
"""
|
||||||
The :mod:`openlp` module contains all the project produced OpenLP functionality
|
The :mod:`openlp` module contains all the project produced OpenLP functionality
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import core
|
||||||
|
import plugins
|
||||||
|
|
||||||
|
__all__ = [u'core', u'plugins']
|
||||||
|
|
||||||
|
@ -25,7 +25,12 @@
|
|||||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
__all__ = ('OpenLP', 'main')
|
"""
|
||||||
|
The :mod:`core` module provides all core application functions
|
||||||
|
|
||||||
|
All the core functions of the OpenLP application including the GUI, settings,
|
||||||
|
logging and a plugin framework are contained within the openlp.core module.
|
||||||
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
@ -46,16 +51,11 @@ from openlp.core.ui import SplashScreen, ScreenList
|
|||||||
from openlp.core.utils import AppLocation, LanguageManager, VersionThread, \
|
from openlp.core.utils import AppLocation, LanguageManager, VersionThread, \
|
||||||
get_application_version, DelayStartThread
|
get_application_version, DelayStartThread
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [u'OpenLP', u'main']
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger()
|
log = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
The :mod:`core` module provides all core application functions
|
|
||||||
|
|
||||||
All the core functions of the OpenLP application including the GUI, settings,
|
|
||||||
logging and a plugin framework are contained within the openlp.core module.
|
|
||||||
"""
|
|
||||||
|
|
||||||
application_stylesheet = u"""
|
application_stylesheet = u"""
|
||||||
QMainWindow::separator
|
QMainWindow::separator
|
||||||
{
|
{
|
||||||
@ -236,7 +236,6 @@ def main(args=None):
|
|||||||
logfile.setFormatter(logging.Formatter(
|
logfile.setFormatter(logging.Formatter(
|
||||||
u'%(asctime)s %(name)-55s %(levelname)-8s %(message)s'))
|
u'%(asctime)s %(name)-55s %(levelname)-8s %(message)s'))
|
||||||
log.addHandler(logfile)
|
log.addHandler(logfile)
|
||||||
logging.addLevelName(15, u'Timer')
|
|
||||||
# Parse command line options and deal with them.
|
# Parse command line options and deal with them.
|
||||||
# Use args supplied programatically if possible.
|
# Use args supplied programatically if possible.
|
||||||
(options, args) = parser.parse_args(args) if args else parser.parse_args()
|
(options, args) = parser.parse_args(args) if args else parser.parse_args()
|
||||||
@ -261,8 +260,10 @@ def main(args=None):
|
|||||||
app.setApplicationName(u'OpenLP')
|
app.setApplicationName(u'OpenLP')
|
||||||
app.setApplicationVersion(get_application_version()[u'version'])
|
app.setApplicationVersion(get_application_version()[u'version'])
|
||||||
# Instance check
|
# Instance check
|
||||||
if app.isAlreadyRunning():
|
if not options.testing:
|
||||||
sys.exit()
|
# Instance check
|
||||||
|
if app.isAlreadyRunning():
|
||||||
|
sys.exit()
|
||||||
# First time checks in settings
|
# First time checks in settings
|
||||||
if not QtCore.QSettings().value(u'general/has run wizard',
|
if not QtCore.QSettings().value(u'general/has run wizard',
|
||||||
QtCore.QVariant(False)).toBool():
|
QtCore.QVariant(False)).toBool():
|
||||||
@ -284,5 +285,7 @@ def main(args=None):
|
|||||||
# Do not run method app.exec_() when running gui tests
|
# Do not run method app.exec_() when running gui tests
|
||||||
if options.testing:
|
if options.testing:
|
||||||
app.run(qt_args, testing=True)
|
app.run(qt_args, testing=True)
|
||||||
|
# For gui tests we need access to window intances and their components
|
||||||
|
return app
|
||||||
else:
|
else:
|
||||||
sys.exit(app.run(qt_args))
|
sys.exit(app.run(qt_args))
|
||||||
|
@ -36,6 +36,13 @@ from PyQt4 import QtCore, QtGui
|
|||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class MediaType(object):
|
||||||
|
"""
|
||||||
|
An enumeration class for types of media.
|
||||||
|
"""
|
||||||
|
Audio = 1
|
||||||
|
Video = 2
|
||||||
|
|
||||||
def translate(context, text, comment=None,
|
def translate(context, text, comment=None,
|
||||||
encoding=QtCore.QCoreApplication.CodecForTr, n=-1,
|
encoding=QtCore.QCoreApplication.CodecForTr, n=-1,
|
||||||
translate=QtCore.QCoreApplication.translate):
|
translate=QtCore.QCoreApplication.translate):
|
||||||
@ -137,7 +144,7 @@ def image_to_byte(image):
|
|||||||
# convert to base64 encoding so does not get missed!
|
# convert to base64 encoding so does not get missed!
|
||||||
return byte_array.toBase64()
|
return byte_array.toBase64()
|
||||||
|
|
||||||
def resize_image(image_path, width, height, background):
|
def resize_image(image_path, width, height, background=u'#000000'):
|
||||||
"""
|
"""
|
||||||
Resize an image to fit on the current screen.
|
Resize an image to fit on the current screen.
|
||||||
|
|
||||||
@ -152,6 +159,8 @@ def resize_image(image_path, width, height, background):
|
|||||||
|
|
||||||
``background``
|
``background``
|
||||||
The background colour defaults to black.
|
The background colour defaults to black.
|
||||||
|
|
||||||
|
DO NOT REMOVE THE DEFAULT BACKGROUND VALUE!
|
||||||
"""
|
"""
|
||||||
log.debug(u'resize_image - start')
|
log.debug(u'resize_image - start')
|
||||||
reader = QtGui.QImageReader(image_path)
|
reader = QtGui.QImageReader(image_path)
|
||||||
@ -178,7 +187,7 @@ def resize_image(image_path, width, height, background):
|
|||||||
new_image = QtGui.QImage(width, height,
|
new_image = QtGui.QImage(width, height,
|
||||||
QtGui.QImage.Format_ARGB32_Premultiplied)
|
QtGui.QImage.Format_ARGB32_Premultiplied)
|
||||||
painter = QtGui.QPainter(new_image)
|
painter = QtGui.QPainter(new_image)
|
||||||
painter.fillRect(new_image.rect(), background)
|
painter.fillRect(new_image.rect(), QtGui.QColor(background))
|
||||||
painter.drawImage((width - realw) / 2, (height - realh) / 2, preview)
|
painter.drawImage((width - realw) / 2, (height - realh) / 2, preview)
|
||||||
return new_image
|
return new_image
|
||||||
|
|
||||||
@ -241,9 +250,7 @@ from settingsmanager import SettingsManager
|
|||||||
from plugin import PluginStatus, StringContent, Plugin
|
from plugin import PluginStatus, StringContent, Plugin
|
||||||
from pluginmanager import PluginManager
|
from pluginmanager import PluginManager
|
||||||
from settingstab import SettingsTab
|
from settingstab import SettingsTab
|
||||||
from serviceitem import ServiceItem
|
from serviceitem import ServiceItem, ServiceItemType, ItemCapabilities
|
||||||
from serviceitem import ServiceItemType
|
|
||||||
from serviceitem import ItemCapabilities
|
|
||||||
from htmlbuilder import build_html, build_lyrics_format_css, \
|
from htmlbuilder import build_html, build_lyrics_format_css, \
|
||||||
build_lyrics_outline_css
|
build_lyrics_outline_css
|
||||||
from toolbar import OpenLPToolbar
|
from toolbar import OpenLPToolbar
|
||||||
|
@ -82,7 +82,7 @@ def upgrade_db(url, upgrade):
|
|||||||
load_changes = True
|
load_changes = True
|
||||||
try:
|
try:
|
||||||
tables = upgrade.upgrade_setup(metadata)
|
tables = upgrade.upgrade_setup(metadata)
|
||||||
except SQLAlchemyError, DBAPIError:
|
except (SQLAlchemyError, DBAPIError):
|
||||||
load_changes = False
|
load_changes = False
|
||||||
metadata_table = Table(u'metadata', metadata,
|
metadata_table = Table(u'metadata', metadata,
|
||||||
Column(u'key', types.Unicode(64), primary_key=True),
|
Column(u'key', types.Unicode(64), primary_key=True),
|
||||||
@ -106,7 +106,7 @@ def upgrade_db(url, upgrade):
|
|||||||
getattr(upgrade, u'upgrade_%d' % version) \
|
getattr(upgrade, u'upgrade_%d' % version) \
|
||||||
(session, metadata, tables)
|
(session, metadata, tables)
|
||||||
version_meta.value = unicode(version)
|
version_meta.value = unicode(version)
|
||||||
except SQLAlchemyError, DBAPIError:
|
except (SQLAlchemyError, DBAPIError):
|
||||||
log.exception(u'Could not run database upgrade script '
|
log.exception(u'Could not run database upgrade script '
|
||||||
'"upgrade_%s", upgrade process has been halted.', version)
|
'"upgrade_%s", upgrade process has been halted.', version)
|
||||||
break
|
break
|
||||||
@ -213,7 +213,8 @@ class Manager(object):
|
|||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
self.session = init_schema(self.db_url)
|
self.session = init_schema(self.db_url)
|
||||||
except:
|
except (SQLAlchemyError, DBAPIError):
|
||||||
|
log.exception(u'Error loading database: %s', self.db_url)
|
||||||
critical_error_message_box(
|
critical_error_message_box(
|
||||||
translate('OpenLP.Manager', 'Database Error'),
|
translate('OpenLP.Manager', 'Database Error'),
|
||||||
unicode(translate('OpenLP.Manager', 'OpenLP cannot load your '
|
unicode(translate('OpenLP.Manager', 'OpenLP cannot load your '
|
||||||
|
@ -235,7 +235,7 @@ sup {
|
|||||||
</html>
|
</html>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def build_html(item, screen, alert, islive, background, plugins=None, \
|
def build_html(item, screen, alert, islive, background, plugins=None,
|
||||||
image=None):
|
image=None):
|
||||||
"""
|
"""
|
||||||
Build the full web paged structure for display
|
Build the full web paged structure for display
|
||||||
|
@ -215,6 +215,8 @@ class ImageManager(QtCore.QObject):
|
|||||||
image = self._cache[name]
|
image = self._cache[name]
|
||||||
if image.image is None:
|
if image.image is None:
|
||||||
self._conversion_queue.modify_priority(image, Priority.High)
|
self._conversion_queue.modify_priority(image, Priority.High)
|
||||||
|
# make sure we are running and if not give it a kick
|
||||||
|
self.process_updates()
|
||||||
while image.image is None:
|
while image.image is None:
|
||||||
log.debug(u'get_image - waiting')
|
log.debug(u'get_image - waiting')
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
@ -235,6 +237,8 @@ class ImageManager(QtCore.QObject):
|
|||||||
image = self._cache[name]
|
image = self._cache[name]
|
||||||
if image.image_bytes is None:
|
if image.image_bytes is None:
|
||||||
self._conversion_queue.modify_priority(image, Priority.Urgent)
|
self._conversion_queue.modify_priority(image, Priority.Urgent)
|
||||||
|
# make sure we are running and if not give it a kick
|
||||||
|
self.process_updates()
|
||||||
while image.image_bytes is None:
|
while image.image_bytes is None:
|
||||||
log.debug(u'get_image_bytes - waiting')
|
log.debug(u'get_image_bytes - waiting')
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
@ -111,7 +111,7 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
self.requiredIcons()
|
self.requiredIcons()
|
||||||
self.setupUi()
|
self.setupUi()
|
||||||
self.retranslateUi()
|
self.retranslateUi()
|
||||||
self.auto_select_id = -1
|
self.autoSelectId = -1
|
||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
QtCore.SIGNAL(u'%s_service_load' % self.plugin.name),
|
QtCore.SIGNAL(u'%s_service_load' % self.plugin.name),
|
||||||
self.serviceLoad)
|
self.serviceLoad)
|
||||||
@ -376,18 +376,23 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
The files to be loaded
|
The files to be loaded
|
||||||
"""
|
"""
|
||||||
names = []
|
names = []
|
||||||
|
fullList = []
|
||||||
for count in range(0, self.listView.count()):
|
for count in range(0, self.listView.count()):
|
||||||
names.append(self.listView.item(count).text())
|
names.append(unicode(self.listView.item(count).text()))
|
||||||
newFiles = []
|
fullList.append(unicode(self.listView.item(count).
|
||||||
|
data(QtCore.Qt.UserRole).toString()))
|
||||||
duplicatesFound = False
|
duplicatesFound = False
|
||||||
|
filesAdded = False
|
||||||
for file in files:
|
for file in files:
|
||||||
filename = os.path.split(unicode(file))[1]
|
filename = os.path.split(unicode(file))[1]
|
||||||
if filename in names:
|
if filename in names:
|
||||||
duplicatesFound = True
|
duplicatesFound = True
|
||||||
else:
|
else:
|
||||||
newFiles.append(file)
|
filesAdded = True
|
||||||
if newFiles:
|
fullList.append(file)
|
||||||
self.loadList(newFiles)
|
if fullList and filesAdded:
|
||||||
|
self.listView.clear()
|
||||||
|
self.loadList(fullList)
|
||||||
lastDir = os.path.split(unicode(files[0]))[0]
|
lastDir = os.path.split(unicode(files[0]))[0]
|
||||||
SettingsManager.set_last_dir(self.settingsSection, lastDir)
|
SettingsManager.set_last_dir(self.settingsSection, lastDir)
|
||||||
SettingsManager.set_list(self.settingsSection,
|
SettingsManager.set_list(self.settingsSection,
|
||||||
@ -396,7 +401,7 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
critical_error_message_box(
|
critical_error_message_box(
|
||||||
UiStrings().Duplicate,
|
UiStrings().Duplicate,
|
||||||
unicode(translate('OpenLP.MediaManagerItem',
|
unicode(translate('OpenLP.MediaManagerItem',
|
||||||
'Duplicate files found on import and ignored.')))
|
'Duplicate files were found on import and were ignored.')))
|
||||||
|
|
||||||
def contextMenu(self, point):
|
def contextMenu(self, point):
|
||||||
item = self.listView.itemAt(point)
|
item = self.listView.itemAt(point)
|
||||||
@ -485,7 +490,8 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def generateSlideData(self, serviceItem, item=None, xmlVersion=False):
|
def generateSlideData(self, serviceItem, item=None, xmlVersion=False,
|
||||||
|
remote=False):
|
||||||
raise NotImplementedError(u'MediaManagerItem.generateSlideData needs '
|
raise NotImplementedError(u'MediaManagerItem.generateSlideData needs '
|
||||||
u'to be defined by the plugin')
|
u'to be defined by the plugin')
|
||||||
|
|
||||||
@ -506,7 +512,7 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
if QtCore.QSettings().value(u'advanced/single click preview',
|
if QtCore.QSettings().value(u'advanced/single click preview',
|
||||||
QtCore.QVariant(False)).toBool() and self.quickPreviewAllowed \
|
QtCore.QVariant(False)).toBool() and self.quickPreviewAllowed \
|
||||||
and self.listView.selectedIndexes() \
|
and self.listView.selectedIndexes() \
|
||||||
and self.auto_select_id == -1:
|
and self.autoSelectId == -1:
|
||||||
self.onPreviewClick(True)
|
self.onPreviewClick(True)
|
||||||
|
|
||||||
def onPreviewClick(self, keepFocus=False):
|
def onPreviewClick(self, keepFocus=False):
|
||||||
@ -539,12 +545,12 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
else:
|
else:
|
||||||
self.goLive()
|
self.goLive()
|
||||||
|
|
||||||
def goLive(self, item_id=None):
|
def goLive(self, item_id=None, remote=False):
|
||||||
log.debug(u'%s Live requested', self.plugin.name)
|
log.debug(u'%s Live requested', self.plugin.name)
|
||||||
item = None
|
item = None
|
||||||
if item_id:
|
if item_id:
|
||||||
item = self.createItemFromId(item_id)
|
item = self.createItemFromId(item_id)
|
||||||
serviceItem = self.buildServiceItem(item)
|
serviceItem = self.buildServiceItem(item, remote=remote)
|
||||||
if serviceItem:
|
if serviceItem:
|
||||||
if not item_id:
|
if not item_id:
|
||||||
serviceItem.from_plugin = True
|
serviceItem.from_plugin = True
|
||||||
@ -574,8 +580,8 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
for item in items:
|
for item in items:
|
||||||
self.addToService(item)
|
self.addToService(item)
|
||||||
|
|
||||||
def addToService(self, item=None, replace=None):
|
def addToService(self, item=None, replace=None, remote=False):
|
||||||
serviceItem = self.buildServiceItem(item, True)
|
serviceItem = self.buildServiceItem(item, True, remote=remote)
|
||||||
if serviceItem:
|
if serviceItem:
|
||||||
serviceItem.from_plugin = False
|
serviceItem.from_plugin = False
|
||||||
self.plugin.serviceManager.addServiceItem(serviceItem,
|
self.plugin.serviceManager.addServiceItem(serviceItem,
|
||||||
@ -608,13 +614,13 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
unicode(translate('OpenLP.MediaManagerItem',
|
unicode(translate('OpenLP.MediaManagerItem',
|
||||||
'You must select a %s service item.')) % self.title)
|
'You must select a %s service item.')) % self.title)
|
||||||
|
|
||||||
def buildServiceItem(self, item=None, xmlVersion=False):
|
def buildServiceItem(self, item=None, xmlVersion=False, remote=False):
|
||||||
"""
|
"""
|
||||||
Common method for generating a service item
|
Common method for generating a service item
|
||||||
"""
|
"""
|
||||||
serviceItem = ServiceItem(self.plugin)
|
serviceItem = ServiceItem(self.plugin)
|
||||||
serviceItem.add_icon(self.plugin.icon_path)
|
serviceItem.add_icon(self.plugin.icon_path)
|
||||||
if self.generateSlideData(serviceItem, item, xmlVersion):
|
if self.generateSlideData(serviceItem, item, xmlVersion, remote):
|
||||||
return serviceItem
|
return serviceItem
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
@ -626,7 +632,7 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def check_search_result(self):
|
def checkSearchResult(self):
|
||||||
"""
|
"""
|
||||||
Checks if the listView is empty and adds a "No Search Results" item.
|
Checks if the listView is empty and adds a "No Search Results" item.
|
||||||
"""
|
"""
|
||||||
@ -662,15 +668,15 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
|
item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
|
||||||
return item_id
|
return item_id
|
||||||
|
|
||||||
def save_auto_select_id(self):
|
def saveAutoSelectId(self):
|
||||||
"""
|
"""
|
||||||
Sorts out, what item to select after loading a list.
|
Sorts out, what item to select after loading a list.
|
||||||
"""
|
"""
|
||||||
# The item to select has not been set.
|
# The item to select has not been set.
|
||||||
if self.auto_select_id == -1:
|
if self.autoSelectId == -1:
|
||||||
item = self.listView.currentItem()
|
item = self.listView.currentItem()
|
||||||
if item:
|
if item:
|
||||||
self.auto_select_id = item.data(QtCore.Qt.UserRole).toInt()[0]
|
self.autoSelectId = item.data(QtCore.Qt.UserRole).toInt()[0]
|
||||||
|
|
||||||
def search(self, string):
|
def search(self, string):
|
||||||
"""
|
"""
|
||||||
|
@ -31,7 +31,7 @@ from PyQt4 import QtGui, QtCore, QtWebKit
|
|||||||
|
|
||||||
from openlp.core.lib import ServiceItem, expand_tags, \
|
from openlp.core.lib import ServiceItem, expand_tags, \
|
||||||
build_lyrics_format_css, build_lyrics_outline_css, Receiver, \
|
build_lyrics_format_css, build_lyrics_outline_css, Receiver, \
|
||||||
ItemCapabilities
|
ItemCapabilities, FormattingTags
|
||||||
from openlp.core.lib.theme import ThemeLevel
|
from openlp.core.lib.theme import ThemeLevel
|
||||||
from openlp.core.ui import MainDisplay, ScreenList
|
from openlp.core.ui import MainDisplay, ScreenList
|
||||||
|
|
||||||
@ -225,56 +225,52 @@ class Renderer(object):
|
|||||||
if item.is_capable(ItemCapabilities.NoLineBreaks):
|
if item.is_capable(ItemCapabilities.NoLineBreaks):
|
||||||
line_end = u' '
|
line_end = u' '
|
||||||
# Bibles
|
# Bibles
|
||||||
if item.is_capable(ItemCapabilities.AllowsWordSplit):
|
if item.is_capable(ItemCapabilities.CanWordSplit):
|
||||||
pages = self._paginate_slide_words(text.split(u'\n'), line_end)
|
pages = self._paginate_slide_words(text.split(u'\n'), line_end)
|
||||||
else:
|
else:
|
||||||
# Clean up line endings.
|
# Clean up line endings.
|
||||||
lines = self._lines_split(text)
|
lines = self._lines_split(text)
|
||||||
pages = self._paginate_slide(lines, line_end)
|
pages = self._paginate_slide(lines, line_end)
|
||||||
# Songs and Custom
|
# Songs and Custom
|
||||||
if item.is_capable(ItemCapabilities.AllowsVirtualSplit) and \
|
if item.is_capable(ItemCapabilities.CanSoftBreak) and \
|
||||||
len(pages) > 1 and u'[---]' in text:
|
len(pages) > 1 and u'[---]' in text:
|
||||||
pages = []
|
pages = []
|
||||||
while True:
|
while True:
|
||||||
# Check if the first two potential virtual slides will fit
|
slides = text.split(u'\n[---]\n', 2)
|
||||||
# (as a whole) on one slide.
|
# If there are (at least) two occurrences of [---] we use
|
||||||
html_text = expand_tags(
|
# the first two slides (and neglect the last for now).
|
||||||
u'\n'.join(text.split(u'\n[---]\n', 2)[:-1]))
|
if len(slides) == 3:
|
||||||
|
html_text = expand_tags(u'\n'.join(slides[:2]))
|
||||||
|
# We check both slides to determine if the virtual break is
|
||||||
|
# needed (there is only one virtual break).
|
||||||
|
else:
|
||||||
|
html_text = expand_tags(u'\n'.join(slides))
|
||||||
html_text = html_text.replace(u'\n', u'<br>')
|
html_text = html_text.replace(u'\n', u'<br>')
|
||||||
if self._text_fits_on_slide(html_text):
|
if self._text_fits_on_slide(html_text):
|
||||||
# The first two virtual slides fit (as a whole) on one
|
# The first two virtual slides fit (as a whole) on one
|
||||||
# slide. Replace the occurrences of [---].
|
# slide. Replace the first occurrence of [---].
|
||||||
text = text.replace(u'\n[---]', u'', 2)
|
text = text.replace(u'\n[---]', u'', 1)
|
||||||
else:
|
else:
|
||||||
# The first two virtual slides did not fit as a whole.
|
# The first virtual slide fits, which means we have to
|
||||||
# Check if the first virtual slide will fit.
|
# render the first virtual slide.
|
||||||
html_text = expand_tags(text.split(u'\n[---]\n', 1)[1])
|
text_contains_break = u'[---]' in text
|
||||||
html_text = html_text.replace(u'\n', u'<br>')
|
if text_contains_break:
|
||||||
if self._text_fits_on_slide(html_text):
|
text_to_render, text = text.split(u'\n[---]\n', 1)
|
||||||
# The first virtual slide fits, so remove it.
|
|
||||||
text = text.replace(u'\n[---]', u'', 1)
|
|
||||||
else:
|
else:
|
||||||
# The first virtual slide does not fit, which means
|
text_to_render = text
|
||||||
# we have to render the first virtual slide.
|
text = u''
|
||||||
text_contains_break = u'[---]' in text
|
lines = text_to_render.strip(u'\n').split(u'\n')
|
||||||
if text_contains_break:
|
slides = self._paginate_slide(lines, line_end)
|
||||||
text_to_render, text = text.split(u'\n[---]\n', 1)
|
if len(slides) > 1 and text:
|
||||||
|
# Add all slides apart from the last one the list.
|
||||||
|
pages.extend(slides[:-1])
|
||||||
|
if text_contains_break:
|
||||||
|
text = slides[-1] + u'\n[---]\n' + text
|
||||||
else:
|
else:
|
||||||
text_to_render = text
|
text = slides[-1] + u'\n'+ text
|
||||||
text = u''
|
text = text.replace(u'<br>', u'\n')
|
||||||
lines = text_to_render.strip(u'\n').split(u'\n')
|
else:
|
||||||
slides = self._paginate_slide(lines, line_end)
|
pages.extend(slides)
|
||||||
if len(slides) > 1 and text:
|
|
||||||
# Add all slides apart from the last one the
|
|
||||||
# list.
|
|
||||||
pages.extend(slides[:-1])
|
|
||||||
if text_contains_break:
|
|
||||||
text = slides[-1] + u'\n[---]\n' + text
|
|
||||||
else:
|
|
||||||
text = slides[-1] + u'\n'+ text
|
|
||||||
text = text.replace(u'<br>', u'\n')
|
|
||||||
else:
|
|
||||||
pages.extend(slides)
|
|
||||||
if u'[---]' not in text:
|
if u'[---]' not in text:
|
||||||
lines = text.strip(u'\n').split(u'\n')
|
lines = text.strip(u'\n').split(u'\n')
|
||||||
pages.extend(self._paginate_slide(lines, line_end))
|
pages.extend(self._paginate_slide(lines, line_end))
|
||||||
@ -446,6 +442,50 @@ class Renderer(object):
|
|||||||
log.debug(u'_paginate_slide_words - End')
|
log.debug(u'_paginate_slide_words - End')
|
||||||
return formatted
|
return formatted
|
||||||
|
|
||||||
|
def _get_start_tags(self, raw_text):
|
||||||
|
"""
|
||||||
|
Tests the given text for not closed formatting tags and returns a tuple
|
||||||
|
consisting of three unicode strings::
|
||||||
|
|
||||||
|
(u'{st}{r}Text text text{/r}{/st}', u'{st}{r}', u'<strong>
|
||||||
|
<span style="-webkit-text-fill-color:red">')
|
||||||
|
|
||||||
|
The first unicode string is the text, with correct closing tags. The
|
||||||
|
second unicode string are OpenLP's opening formatting tags and the third
|
||||||
|
unicode string the html opening formatting tags.
|
||||||
|
|
||||||
|
``raw_text``
|
||||||
|
The text to test. The text must **not** contain html tags, only
|
||||||
|
OpenLP formatting tags are allowed::
|
||||||
|
|
||||||
|
{st}{r}Text text text
|
||||||
|
"""
|
||||||
|
raw_tags = []
|
||||||
|
html_tags = []
|
||||||
|
for tag in FormattingTags.get_html_tags():
|
||||||
|
if tag[u'start tag'] == u'{br}':
|
||||||
|
continue
|
||||||
|
if raw_text.count(tag[u'start tag']) != \
|
||||||
|
raw_text.count(tag[u'end tag']):
|
||||||
|
raw_tags.append(
|
||||||
|
(raw_text.find(tag[u'start tag']), tag[u'start tag'],
|
||||||
|
tag[u'end tag']))
|
||||||
|
html_tags.append(
|
||||||
|
(raw_text.find(tag[u'start tag']), tag[u'start html']))
|
||||||
|
# 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.
|
||||||
|
raw_tags.sort(key=lambda tag: tag[0])
|
||||||
|
html_tags.sort(key=lambda tag: tag[0])
|
||||||
|
# Create a list with closing tags for the raw_text.
|
||||||
|
end_tags = [tag[2] for tag in raw_tags]
|
||||||
|
end_tags.reverse()
|
||||||
|
# Remove the indexes.
|
||||||
|
raw_tags = [tag[1] for tag in raw_tags]
|
||||||
|
html_tags = [tag[1] for tag in html_tags]
|
||||||
|
return raw_text + u''.join(end_tags), u''.join(raw_tags), \
|
||||||
|
u''.join(html_tags)
|
||||||
|
|
||||||
def _binary_chop(self, formatted, previous_html, previous_raw, html_list,
|
def _binary_chop(self, formatted, previous_html, previous_raw, html_list,
|
||||||
raw_list, separator, line_end):
|
raw_list, separator, line_end):
|
||||||
"""
|
"""
|
||||||
@ -497,8 +537,10 @@ class Renderer(object):
|
|||||||
# We found the number of words which will fit.
|
# We found the number of words which will fit.
|
||||||
if smallest_index == index or highest_index == index:
|
if smallest_index == index or highest_index == index:
|
||||||
index = smallest_index
|
index = smallest_index
|
||||||
formatted.append(previous_raw.rstrip(u'<br>') +
|
text = previous_raw.rstrip(u'<br>') + \
|
||||||
separator.join(raw_list[:index + 1]))
|
separator.join(raw_list[:index + 1])
|
||||||
|
text, raw_tags, html_tags = self._get_start_tags(text)
|
||||||
|
formatted.append(text)
|
||||||
previous_html = u''
|
previous_html = u''
|
||||||
previous_raw = u''
|
previous_raw = u''
|
||||||
# Stop here as the theme line count was requested.
|
# Stop here as the theme line count was requested.
|
||||||
@ -509,17 +551,19 @@ class Renderer(object):
|
|||||||
continue
|
continue
|
||||||
# Check if the remaining elements fit on the slide.
|
# Check if the remaining elements fit on the slide.
|
||||||
if self._text_fits_on_slide(
|
if self._text_fits_on_slide(
|
||||||
separator.join(html_list[index + 1:]).strip()):
|
html_tags + separator.join(html_list[index + 1:]).strip()):
|
||||||
previous_html = separator.join(
|
previous_html = html_tags + separator.join(
|
||||||
html_list[index + 1:]).strip() + line_end
|
html_list[index + 1:]).strip() + line_end
|
||||||
previous_raw = separator.join(
|
previous_raw = raw_tags + separator.join(
|
||||||
raw_list[index + 1:]).strip() + line_end
|
raw_list[index + 1:]).strip() + line_end
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
# The remaining elements do not fit, thus reset the indexes,
|
# The remaining elements do not fit, thus reset the indexes,
|
||||||
# create a new list and continue.
|
# create a new list and continue.
|
||||||
raw_list = raw_list[index + 1:]
|
raw_list = raw_list[index + 1:]
|
||||||
|
raw_list[0] = raw_tags + raw_list[0]
|
||||||
html_list = html_list[index + 1:]
|
html_list = html_list[index + 1:]
|
||||||
|
html_list[0] = html_tags + html_list[0]
|
||||||
smallest_index = 0
|
smallest_index = 0
|
||||||
highest_index = len(html_list) - 1
|
highest_index = len(html_list) - 1
|
||||||
index = int(highest_index / 2)
|
index = int(highest_index / 2)
|
||||||
|
@ -52,20 +52,21 @@ class ItemCapabilities(object):
|
|||||||
"""
|
"""
|
||||||
Provides an enumeration of a serviceitem's capabilities
|
Provides an enumeration of a serviceitem's capabilities
|
||||||
"""
|
"""
|
||||||
AllowsPreview = 1
|
CanPreview = 1
|
||||||
AllowsEdit = 2
|
CanEdit = 2
|
||||||
AllowsMaintain = 3
|
CanMaintain = 3
|
||||||
RequiresMedia = 4
|
RequiresMedia = 4
|
||||||
AllowsLoop = 5
|
CanLoop = 5
|
||||||
AllowsAdditions = 6
|
CanAppend = 6
|
||||||
NoLineBreaks = 7
|
NoLineBreaks = 7
|
||||||
OnLoadUpdate = 8
|
OnLoadUpdate = 8
|
||||||
AddIfNewItem = 9
|
AddIfNewItem = 9
|
||||||
ProvidesOwnDisplay = 10
|
ProvidesOwnDisplay = 10
|
||||||
AllowsDetailedTitleDisplay = 11
|
HasDetailedTitleDisplay = 11
|
||||||
AllowsVariableStartTime = 12
|
HasVariableStartTime = 12
|
||||||
AllowsVirtualSplit = 13
|
CanSoftBreak = 13
|
||||||
AllowsWordSplit = 14
|
CanWordSplit = 14
|
||||||
|
HasBackgroundAudio = 15
|
||||||
|
|
||||||
|
|
||||||
class ServiceItem(object):
|
class ServiceItem(object):
|
||||||
@ -116,6 +117,7 @@ class ServiceItem(object):
|
|||||||
self.media_length = 0
|
self.media_length = 0
|
||||||
self.from_service = False
|
self.from_service = False
|
||||||
self.image_border = u'#000000'
|
self.image_border = u'#000000'
|
||||||
|
self.background_audio = []
|
||||||
self._new_item()
|
self._new_item()
|
||||||
|
|
||||||
def _new_item(self):
|
def _new_item(self):
|
||||||
@ -159,7 +161,7 @@ class ServiceItem(object):
|
|||||||
"""
|
"""
|
||||||
The render method is what generates the frames for the screen and
|
The render method is what generates the frames for the screen and
|
||||||
obtains the display information from the renderemanager.
|
obtains the display information from the renderemanager.
|
||||||
At this point all the slides are build for the given
|
At this point all the slides are built for the given
|
||||||
display size.
|
display size.
|
||||||
"""
|
"""
|
||||||
log.debug(u'Render called')
|
log.debug(u'Render called')
|
||||||
@ -272,7 +274,8 @@ class ServiceItem(object):
|
|||||||
u'xml_version': self.xml_version,
|
u'xml_version': self.xml_version,
|
||||||
u'start_time': self.start_time,
|
u'start_time': self.start_time,
|
||||||
u'end_time': self.end_time,
|
u'end_time': self.end_time,
|
||||||
u'media_length': self.media_length
|
u'media_length': self.media_length,
|
||||||
|
u'background_audio': self.background_audio
|
||||||
}
|
}
|
||||||
service_data = []
|
service_data = []
|
||||||
if self.service_item_type == ServiceItemType.Text:
|
if self.service_item_type == ServiceItemType.Text:
|
||||||
@ -320,6 +323,8 @@ class ServiceItem(object):
|
|||||||
self.end_time = header[u'end_time']
|
self.end_time = header[u'end_time']
|
||||||
if u'media_length' in header:
|
if u'media_length' in header:
|
||||||
self.media_length = header[u'media_length']
|
self.media_length = header[u'media_length']
|
||||||
|
if u'background_audio' in header:
|
||||||
|
self.background_audio = header[u'background_audio']
|
||||||
if self.service_item_type == ServiceItemType.Text:
|
if self.service_item_type == ServiceItemType.Text:
|
||||||
for slide in serviceitem[u'serviceitem'][u'data']:
|
for slide in serviceitem[u'serviceitem'][u'data']:
|
||||||
self._raw_frames.append(slide)
|
self._raw_frames.append(slide)
|
||||||
@ -341,7 +346,7 @@ class ServiceItem(object):
|
|||||||
if self.is_text():
|
if self.is_text():
|
||||||
return self.title
|
return self.title
|
||||||
else:
|
else:
|
||||||
if ItemCapabilities.AllowsDetailedTitleDisplay in self.capabilities:
|
if ItemCapabilities.HasDetailedTitleDisplay in self.capabilities:
|
||||||
return self._raw_frames[0][u'title']
|
return self._raw_frames[0][u'title']
|
||||||
elif len(self._raw_frames) > 1:
|
elif len(self._raw_frames) > 1:
|
||||||
return self.title
|
return self.title
|
||||||
@ -359,6 +364,8 @@ class ServiceItem(object):
|
|||||||
"""
|
"""
|
||||||
self._uuid = other._uuid
|
self._uuid = other._uuid
|
||||||
self.notes = other.notes
|
self.notes = other.notes
|
||||||
|
if self.is_capable(ItemCapabilities.HasBackgroundAudio):
|
||||||
|
log.debug(self.background_audio)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
"""
|
"""
|
||||||
@ -459,7 +466,7 @@ class ServiceItem(object):
|
|||||||
'<strong>Length</strong>: %s')) % \
|
'<strong>Length</strong>: %s')) % \
|
||||||
unicode(datetime.timedelta(seconds=self.media_length))
|
unicode(datetime.timedelta(seconds=self.media_length))
|
||||||
if not start and not end:
|
if not start and not end:
|
||||||
return None
|
return u''
|
||||||
elif start and not end:
|
elif start and not end:
|
||||||
return start
|
return start
|
||||||
elif not start and end:
|
elif not start and end:
|
||||||
|
@ -28,7 +28,9 @@
|
|||||||
import io
|
import io
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
import urllib
|
import urllib
|
||||||
|
import urllib2
|
||||||
from tempfile import gettempdir
|
from tempfile import gettempdir
|
||||||
from ConfigParser import SafeConfigParser
|
from ConfigParser import SafeConfigParser
|
||||||
|
|
||||||
@ -60,8 +62,13 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
|||||||
files = self.webAccess.read()
|
files = self.webAccess.read()
|
||||||
self.config.readfp(io.BytesIO(files))
|
self.config.readfp(io.BytesIO(files))
|
||||||
self.updateScreenListCombo()
|
self.updateScreenListCombo()
|
||||||
|
self.downloadCanceled = False
|
||||||
self.downloading = unicode(translate('OpenLP.FirstTimeWizard',
|
self.downloading = unicode(translate('OpenLP.FirstTimeWizard',
|
||||||
'Downloading %s...'))
|
'Downloading %s...'))
|
||||||
|
QtCore.QObject.connect(self.cancelButton,QtCore.SIGNAL('clicked()'),
|
||||||
|
self.onCancelButtonClicked)
|
||||||
|
QtCore.QObject.connect(self.noInternetFinishButton,
|
||||||
|
QtCore.SIGNAL('clicked()'), self.onNoInternetFinishButtonClicked)
|
||||||
QtCore.QObject.connect(self,
|
QtCore.QObject.connect(self,
|
||||||
QtCore.SIGNAL(u'currentIdChanged(int)'), self.onCurrentIdChanged)
|
QtCore.SIGNAL(u'currentIdChanged(int)'), self.onCurrentIdChanged)
|
||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
@ -80,6 +87,10 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
|||||||
"""
|
"""
|
||||||
self.restart()
|
self.restart()
|
||||||
check_directory_exists(os.path.join(gettempdir(), u'openlp'))
|
check_directory_exists(os.path.join(gettempdir(), u'openlp'))
|
||||||
|
self.noInternetFinishButton.setVisible(False)
|
||||||
|
# Check if this is a re-run of the wizard.
|
||||||
|
self.hasRunWizard = QtCore.QSettings().value(
|
||||||
|
u'general/has run wizard', QtCore.QVariant(False)).toBool()
|
||||||
# Sort out internet access for downloads
|
# Sort out internet access for downloads
|
||||||
if self.webAccess:
|
if self.webAccess:
|
||||||
songs = self.config.get(u'songs', u'languages')
|
songs = self.config.get(u'songs', u'languages')
|
||||||
@ -120,7 +131,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
|||||||
title = self.config.get(u'theme_%s' % theme, u'title')
|
title = self.config.get(u'theme_%s' % theme, u'title')
|
||||||
filename = self.config.get(u'theme_%s' % theme, u'filename')
|
filename = self.config.get(u'theme_%s' % theme, u'filename')
|
||||||
screenshot = self.config.get(u'theme_%s' % theme, u'screenshot')
|
screenshot = self.config.get(u'theme_%s' % theme, u'screenshot')
|
||||||
urllib.urlretrieve(u'%s/%s' % (self.web, screenshot),
|
urllib.urlretrieve(u'%s%s' % (self.web, screenshot),
|
||||||
os.path.join(gettempdir(), u'openlp', screenshot))
|
os.path.join(gettempdir(), u'openlp', screenshot))
|
||||||
item = QtGui.QListWidgetItem(title, self.themesListWidget)
|
item = QtGui.QListWidgetItem(title, self.themesListWidget)
|
||||||
item.setData(QtCore.Qt.UserRole,
|
item.setData(QtCore.Qt.UserRole,
|
||||||
@ -135,6 +146,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
|||||||
"""
|
"""
|
||||||
Determine the next page in the Wizard to go to.
|
Determine the next page in the Wizard to go to.
|
||||||
"""
|
"""
|
||||||
|
Receiver.send_message(u'openlp_process_events')
|
||||||
if self.currentId() == FirstTimePage.Plugins:
|
if self.currentId() == FirstTimePage.Plugins:
|
||||||
if not self.webAccess:
|
if not self.webAccess:
|
||||||
return FirstTimePage.NoInternet
|
return FirstTimePage.NoInternet
|
||||||
@ -151,16 +163,24 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
|||||||
"""
|
"""
|
||||||
Detects Page changes and updates as approprate.
|
Detects Page changes and updates as approprate.
|
||||||
"""
|
"""
|
||||||
if pageId == FirstTimePage.Defaults:
|
# Keep track of the page we are at. Pressing "Cancel" causes pageId
|
||||||
|
# to be a -1.
|
||||||
|
if pageId != -1:
|
||||||
|
self.lastId = pageId
|
||||||
|
if pageId == FirstTimePage.Plugins:
|
||||||
|
# Set the no internet page text.
|
||||||
|
if self.hasRunWizard:
|
||||||
|
self.noInternetLabel.setText(self.noInternetText)
|
||||||
|
else:
|
||||||
|
self.noInternetLabel.setText(self.noInternetText +
|
||||||
|
self.cancelWizardText)
|
||||||
|
elif pageId == FirstTimePage.Defaults:
|
||||||
self.themeComboBox.clear()
|
self.themeComboBox.clear()
|
||||||
for iter in xrange(self.themesListWidget.count()):
|
for iter in xrange(self.themesListWidget.count()):
|
||||||
item = self.themesListWidget.item(iter)
|
item = self.themesListWidget.item(iter)
|
||||||
if item.checkState() == QtCore.Qt.Checked:
|
if item.checkState() == QtCore.Qt.Checked:
|
||||||
self.themeComboBox.addItem(item.text())
|
self.themeComboBox.addItem(item.text())
|
||||||
# Check if this is a re-run of the wizard.
|
if self.hasRunWizard:
|
||||||
self.has_run_wizard = QtCore.QSettings().value(
|
|
||||||
u'general/has run wizard', QtCore.QVariant(False)).toBool()
|
|
||||||
if self.has_run_wizard:
|
|
||||||
# Add any existing themes to list.
|
# Add any existing themes to list.
|
||||||
for theme in self.parent().themeManagerContents.getThemes():
|
for theme in self.parent().themeManagerContents.getThemes():
|
||||||
index = self.themeComboBox.findText(theme)
|
index = self.themeComboBox.findText(theme)
|
||||||
@ -172,12 +192,21 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
|||||||
# Pre-select the current default theme.
|
# Pre-select the current default theme.
|
||||||
index = self.themeComboBox.findText(default_theme)
|
index = self.themeComboBox.findText(default_theme)
|
||||||
self.themeComboBox.setCurrentIndex(index)
|
self.themeComboBox.setCurrentIndex(index)
|
||||||
|
elif pageId == FirstTimePage.NoInternet:
|
||||||
|
self.backButton.setVisible(False)
|
||||||
|
self.nextButton.setVisible(False)
|
||||||
|
self.noInternetFinishButton.setVisible(True)
|
||||||
|
if self.hasRunWizard:
|
||||||
|
self.cancelButton.setVisible(False)
|
||||||
elif pageId == FirstTimePage.Progress:
|
elif pageId == FirstTimePage.Progress:
|
||||||
Receiver.send_message(u'cursor_busy')
|
Receiver.send_message(u'cursor_busy')
|
||||||
self._preWizard()
|
self._preWizard()
|
||||||
|
Receiver.send_message(u'openlp_process_events')
|
||||||
self._performWizard()
|
self._performWizard()
|
||||||
|
Receiver.send_message(u'openlp_process_events')
|
||||||
self._postWizard()
|
self._postWizard()
|
||||||
Receiver.send_message(u'cursor_normal')
|
Receiver.send_message(u'cursor_normal')
|
||||||
|
Receiver.send_message(u'openlp_process_events')
|
||||||
|
|
||||||
def updateScreenListCombo(self):
|
def updateScreenListCombo(self):
|
||||||
"""
|
"""
|
||||||
@ -188,6 +217,53 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
|||||||
self.displayComboBox.addItems(self.screens.get_screen_list())
|
self.displayComboBox.addItems(self.screens.get_screen_list())
|
||||||
self.displayComboBox.setCurrentIndex(self.displayComboBox.count() - 1)
|
self.displayComboBox.setCurrentIndex(self.displayComboBox.count() - 1)
|
||||||
|
|
||||||
|
def onCancelButtonClicked(self):
|
||||||
|
"""
|
||||||
|
Process the pressing of the cancel button.
|
||||||
|
"""
|
||||||
|
if self.lastId == FirstTimePage.NoInternet or \
|
||||||
|
(self.lastId <= FirstTimePage.Plugins and \
|
||||||
|
not self.hasRunWizard):
|
||||||
|
QtCore.QCoreApplication.exit()
|
||||||
|
sys.exit()
|
||||||
|
self.downloadCanceled = True
|
||||||
|
Receiver.send_message(u'cursor_normal')
|
||||||
|
|
||||||
|
def onNoInternetFinishButtonClicked(self):
|
||||||
|
"""
|
||||||
|
Process the pressing of the "Finish" button on the No Internet page.
|
||||||
|
"""
|
||||||
|
Receiver.send_message(u'cursor_busy')
|
||||||
|
self._performWizard()
|
||||||
|
Receiver.send_message(u'openlp_process_events')
|
||||||
|
Receiver.send_message(u'cursor_normal')
|
||||||
|
QtCore.QSettings().setValue(u'general/has run wizard',
|
||||||
|
QtCore.QVariant(True))
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def urlGetFile(self, url, fpath):
|
||||||
|
""""
|
||||||
|
Download a file given a URL. The file is retrieved in chunks, giving
|
||||||
|
the ability to cancel the download at any point.
|
||||||
|
"""
|
||||||
|
block_count = 0
|
||||||
|
block_size = 4096
|
||||||
|
urlfile = urllib2.urlopen(url)
|
||||||
|
filesize = urlfile.headers["Content-Length"]
|
||||||
|
filename = open(fpath, "wb")
|
||||||
|
# Download until finished or canceled.
|
||||||
|
while not self.downloadCanceled:
|
||||||
|
data = urlfile.read(block_size)
|
||||||
|
if not data:
|
||||||
|
break
|
||||||
|
filename.write(data)
|
||||||
|
block_count += 1
|
||||||
|
self._downloadProgress(block_count, block_size, filesize)
|
||||||
|
filename.close()
|
||||||
|
# Delete file if canceled, it may be a partial file.
|
||||||
|
if self.downloadCanceled:
|
||||||
|
os.remove(fpath)
|
||||||
|
|
||||||
def _getFileSize(self, url):
|
def _getFileSize(self, url):
|
||||||
site = urllib.urlopen(url)
|
site = urllib.urlopen(url)
|
||||||
meta = site.info()
|
meta = site.info()
|
||||||
@ -219,6 +295,8 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
|||||||
Prepare the UI for the process.
|
Prepare the UI for the process.
|
||||||
"""
|
"""
|
||||||
self.max_progress = 0
|
self.max_progress = 0
|
||||||
|
self.finishButton.setVisible(False)
|
||||||
|
Receiver.send_message(u'openlp_process_events')
|
||||||
# Loop through the songs list and increase for each selected item
|
# Loop through the songs list and increase for each selected item
|
||||||
for i in xrange(self.songsListWidget.count()):
|
for i in xrange(self.songsListWidget.count()):
|
||||||
item = self.songsListWidget.item(i)
|
item = self.songsListWidget.item(i)
|
||||||
@ -242,7 +320,6 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
|||||||
filename = item.data(QtCore.Qt.UserRole).toString()
|
filename = item.data(QtCore.Qt.UserRole).toString()
|
||||||
size = self._getFileSize(u'%s%s' % (self.web, filename))
|
size = self._getFileSize(u'%s%s' % (self.web, filename))
|
||||||
self.max_progress += size
|
self.max_progress += size
|
||||||
self.finishButton.setVisible(False)
|
|
||||||
if self.max_progress:
|
if self.max_progress:
|
||||||
# Add on 2 for plugins status setting plus a "finished" point.
|
# Add on 2 for plugins status setting plus a "finished" point.
|
||||||
self.max_progress = self.max_progress + 2
|
self.max_progress = self.max_progress + 2
|
||||||
@ -266,7 +343,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
|||||||
"""
|
"""
|
||||||
if self.max_progress:
|
if self.max_progress:
|
||||||
self.progressBar.setValue(self.progressBar.maximum())
|
self.progressBar.setValue(self.progressBar.maximum())
|
||||||
if self.has_run_wizard:
|
if self.hasRunWizard:
|
||||||
self.progressLabel.setText(translate('OpenLP.FirstTimeWizard',
|
self.progressLabel.setText(translate('OpenLP.FirstTimeWizard',
|
||||||
'Download complete.'
|
'Download complete.'
|
||||||
' Click the finish button to return to OpenLP.'))
|
' Click the finish button to return to OpenLP.'))
|
||||||
@ -275,7 +352,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
|||||||
'Download complete.'
|
'Download complete.'
|
||||||
' Click the finish button to start OpenLP.'))
|
' Click the finish button to start OpenLP.'))
|
||||||
else:
|
else:
|
||||||
if self.has_run_wizard:
|
if self.hasRunWizard:
|
||||||
self.progressLabel.setText(translate('OpenLP.FirstTimeWizard',
|
self.progressLabel.setText(translate('OpenLP.FirstTimeWizard',
|
||||||
'Click the finish button to return to OpenLP.'))
|
'Click the finish button to return to OpenLP.'))
|
||||||
else:
|
else:
|
||||||
@ -304,42 +381,42 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
|||||||
self._setPluginStatus(self.customCheckBox, u'custom/status')
|
self._setPluginStatus(self.customCheckBox, u'custom/status')
|
||||||
self._setPluginStatus(self.songUsageCheckBox, u'songusage/status')
|
self._setPluginStatus(self.songUsageCheckBox, u'songusage/status')
|
||||||
self._setPluginStatus(self.alertCheckBox, u'alerts/status')
|
self._setPluginStatus(self.alertCheckBox, u'alerts/status')
|
||||||
# Build directories for downloads
|
if self.webAccess:
|
||||||
songs_destination = os.path.join(unicode(gettempdir()), u'openlp')
|
# Build directories for downloads
|
||||||
bibles_destination = AppLocation.get_section_data_path(u'bibles')
|
songs_destination = os.path.join(unicode(gettempdir()), u'openlp')
|
||||||
themes_destination = AppLocation.get_section_data_path(u'themes')
|
bibles_destination = AppLocation.get_section_data_path(u'bibles')
|
||||||
# Download songs
|
themes_destination = AppLocation.get_section_data_path(u'themes')
|
||||||
for i in xrange(self.songsListWidget.count()):
|
# Download songs
|
||||||
item = self.songsListWidget.item(i)
|
for i in xrange(self.songsListWidget.count()):
|
||||||
if item.checkState() == QtCore.Qt.Checked:
|
item = self.songsListWidget.item(i)
|
||||||
filename = item.data(QtCore.Qt.UserRole).toString()
|
if item.checkState() == QtCore.Qt.Checked:
|
||||||
self._incrementProgressBar(self.downloading % filename, 0)
|
filename = item.data(QtCore.Qt.UserRole).toString()
|
||||||
self.previous_size = 0
|
self._incrementProgressBar(self.downloading % filename, 0)
|
||||||
destination = os.path.join(songs_destination, unicode(filename))
|
self.previous_size = 0
|
||||||
urllib.urlretrieve(u'%s%s' % (self.web, filename), destination,
|
destination = os.path.join(songs_destination,
|
||||||
self._downloadProgress)
|
unicode(filename))
|
||||||
# Download Bibles
|
self.urlGetFile(u'%s%s' % (self.web, filename), destination)
|
||||||
bibles_iterator = QtGui.QTreeWidgetItemIterator(self.biblesTreeWidget)
|
# Download Bibles
|
||||||
while bibles_iterator.value():
|
bibles_iterator = QtGui.QTreeWidgetItemIterator(
|
||||||
item = bibles_iterator.value()
|
self.biblesTreeWidget)
|
||||||
if item.parent() and item.checkState(0) == QtCore.Qt.Checked:
|
while bibles_iterator.value():
|
||||||
bible = unicode(item.data(0, QtCore.Qt.UserRole).toString())
|
item = bibles_iterator.value()
|
||||||
self._incrementProgressBar(self.downloading % bible, 0)
|
if item.parent() and item.checkState(0) == QtCore.Qt.Checked:
|
||||||
self.previous_size = 0
|
bible = unicode(item.data(0, QtCore.Qt.UserRole).toString())
|
||||||
urllib.urlretrieve(u'%s%s' % (self.web, bible),
|
self._incrementProgressBar(self.downloading % bible, 0)
|
||||||
os.path.join(bibles_destination, bible),
|
self.previous_size = 0
|
||||||
self._downloadProgress)
|
self.urlGetFile(u'%s%s' % (self.web, bible),
|
||||||
bibles_iterator += 1
|
os.path.join(bibles_destination, bible))
|
||||||
# Download themes
|
bibles_iterator += 1
|
||||||
for i in xrange(self.themesListWidget.count()):
|
# Download themes
|
||||||
item = self.themesListWidget.item(i)
|
for i in xrange(self.themesListWidget.count()):
|
||||||
if item.checkState() == QtCore.Qt.Checked:
|
item = self.themesListWidget.item(i)
|
||||||
theme = unicode(item.data(QtCore.Qt.UserRole).toString())
|
if item.checkState() == QtCore.Qt.Checked:
|
||||||
self._incrementProgressBar(self.downloading % theme, 0)
|
theme = unicode(item.data(QtCore.Qt.UserRole).toString())
|
||||||
self.previous_size = 0
|
self._incrementProgressBar(self.downloading % theme, 0)
|
||||||
urllib.urlretrieve(u'%s%s' % (self.web, theme),
|
self.previous_size = 0
|
||||||
os.path.join(themes_destination, theme),
|
self.urlGetFile(u'%s%s' % (self.web, theme),
|
||||||
self._downloadProgress)
|
os.path.join(themes_destination, theme))
|
||||||
# Set Default Display
|
# Set Default Display
|
||||||
if self.displayComboBox.currentIndex() != -1:
|
if self.displayComboBox.currentIndex() != -1:
|
||||||
QtCore.QSettings().setValue(u'General/monitor',
|
QtCore.QSettings().setValue(u'General/monitor',
|
||||||
|
@ -51,8 +51,10 @@ class Ui_FirstTimeWizard(object):
|
|||||||
FirstTimeWizard.setWizardStyle(QtGui.QWizard.ModernStyle)
|
FirstTimeWizard.setWizardStyle(QtGui.QWizard.ModernStyle)
|
||||||
FirstTimeWizard.setOptions(QtGui.QWizard.IndependentPages |
|
FirstTimeWizard.setOptions(QtGui.QWizard.IndependentPages |
|
||||||
QtGui.QWizard.NoBackButtonOnStartPage |
|
QtGui.QWizard.NoBackButtonOnStartPage |
|
||||||
QtGui.QWizard.NoBackButtonOnLastPage)
|
QtGui.QWizard.NoBackButtonOnLastPage |
|
||||||
|
QtGui.QWizard.HaveCustomButton1)
|
||||||
self.finishButton = self.button(QtGui.QWizard.FinishButton)
|
self.finishButton = self.button(QtGui.QWizard.FinishButton)
|
||||||
|
self.noInternetFinishButton = self.button(QtGui.QWizard.CustomButton1)
|
||||||
self.cancelButton = self.button(QtGui.QWizard.CancelButton)
|
self.cancelButton = self.button(QtGui.QWizard.CancelButton)
|
||||||
self.nextButton = self.button(QtGui.QWizard.NextButton)
|
self.nextButton = self.button(QtGui.QWizard.NextButton)
|
||||||
self.backButton = self.button(QtGui.QWizard.BackButton)
|
self.backButton = self.button(QtGui.QWizard.BackButton)
|
||||||
@ -189,9 +191,7 @@ class Ui_FirstTimeWizard(object):
|
|||||||
self.progressBar.setObjectName(u'progressBar')
|
self.progressBar.setObjectName(u'progressBar')
|
||||||
self.progressLayout.addWidget(self.progressBar)
|
self.progressLayout.addWidget(self.progressBar)
|
||||||
FirstTimeWizard.setPage(FirstTimePage.Progress, self.progressPage)
|
FirstTimeWizard.setPage(FirstTimePage.Progress, self.progressPage)
|
||||||
|
|
||||||
self.retranslateUi(FirstTimeWizard)
|
self.retranslateUi(FirstTimeWizard)
|
||||||
QtCore.QMetaObject.connectSlotsByName(FirstTimeWizard)
|
|
||||||
|
|
||||||
def retranslateUi(self, FirstTimeWizard):
|
def retranslateUi(self, FirstTimeWizard):
|
||||||
FirstTimeWizard.setWindowTitle(translate(
|
FirstTimeWizard.setWindowTitle(translate(
|
||||||
@ -230,14 +230,17 @@ class Ui_FirstTimeWizard(object):
|
|||||||
self.noInternetPage.setSubTitle(translate(
|
self.noInternetPage.setSubTitle(translate(
|
||||||
'OpenLP.FirstTimeWizard',
|
'OpenLP.FirstTimeWizard',
|
||||||
'Unable to detect an Internet connection.'))
|
'Unable to detect an Internet connection.'))
|
||||||
self.noInternetLabel.setText(translate('OpenLP.FirstTimeWizard',
|
self.noInternetText = translate('OpenLP.FirstTimeWizard',
|
||||||
'No Internet connection was found. The First Time Wizard needs an '
|
'No Internet connection was found. The First Time Wizard needs an '
|
||||||
'Internet connection in order to be able to download sample '
|
'Internet connection in order to be able to download sample '
|
||||||
'songs, Bibles and themes.\n\nTo re-run the First Time Wizard and '
|
'songs, Bibles and themes. Press the Finish button now to start '
|
||||||
'import this sample data at a later stage, press the cancel '
|
'OpenLP with initial settings and no sample data.\n\nTo re-run the '
|
||||||
'button now, check your Internet connection, and restart OpenLP.'
|
'First Time Wizard and import this sample data at a later time, '
|
||||||
'\n\nTo cancel the First Time Wizard completely, press the finish '
|
'check your Internet connection and re-run this wizard by '
|
||||||
'button now.'))
|
'selecting "Tools/Re-run First Time Wizard" from OpenLP.')
|
||||||
|
self.cancelWizardText = translate('OpenLP.FirstTimeWizard',
|
||||||
|
'\n\nTo cancel the First Time Wizard completely (and not start '
|
||||||
|
'OpenLP), press the Cancel button now.')
|
||||||
self.songsPage.setTitle(translate('OpenLP.FirstTimeWizard',
|
self.songsPage.setTitle(translate('OpenLP.FirstTimeWizard',
|
||||||
'Sample Songs'))
|
'Sample Songs'))
|
||||||
self.songsPage.setSubTitle(translate('OpenLP.FirstTimeWizard',
|
self.songsPage.setSubTitle(translate('OpenLP.FirstTimeWizard',
|
||||||
@ -260,3 +263,5 @@ class Ui_FirstTimeWizard(object):
|
|||||||
'Select default theme:'))
|
'Select default theme:'))
|
||||||
self.progressLabel.setText(translate('OpenLP.FirstTimeWizard',
|
self.progressLabel.setText(translate('OpenLP.FirstTimeWizard',
|
||||||
'Starting configuration process...'))
|
'Starting configuration process...'))
|
||||||
|
FirstTimeWizard.setButtonText(QtGui.QWizard.CustomButton1,
|
||||||
|
translate('OpenLP.FirstTimeWizard', 'Finish'))
|
||||||
|
@ -170,6 +170,15 @@ class GeneralTab(SettingsTab):
|
|||||||
self.customHeightValueEdit.setMaximum(9999)
|
self.customHeightValueEdit.setMaximum(9999)
|
||||||
self.displayLayout.addWidget(self.customHeightValueEdit, 4, 3)
|
self.displayLayout.addWidget(self.customHeightValueEdit, 4, 3)
|
||||||
self.rightLayout.addWidget(self.displayGroupBox)
|
self.rightLayout.addWidget(self.displayGroupBox)
|
||||||
|
# Background audio
|
||||||
|
self.audioGroupBox = QtGui.QGroupBox(self.rightColumn)
|
||||||
|
self.audioGroupBox.setObjectName(u'audioGroupBox')
|
||||||
|
self.audioLayout = QtGui.QVBoxLayout(self.audioGroupBox)
|
||||||
|
self.audioLayout.setObjectName(u'audioLayout')
|
||||||
|
self.startPausedCheckBox = QtGui.QCheckBox(self.audioGroupBox)
|
||||||
|
self.startPausedCheckBox.setObjectName(u'startPausedCheckBox')
|
||||||
|
self.audioLayout.addWidget(self.startPausedCheckBox)
|
||||||
|
self.rightLayout.addWidget(self.audioGroupBox)
|
||||||
self.rightLayout.addStretch()
|
self.rightLayout.addStretch()
|
||||||
# Signals and slots
|
# Signals and slots
|
||||||
QtCore.QObject.connect(self.overrideCheckBox,
|
QtCore.QObject.connect(self.overrideCheckBox,
|
||||||
@ -243,6 +252,10 @@ class GeneralTab(SettingsTab):
|
|||||||
self.customYLabel.setText(translate('OpenLP.GeneralTab', 'Y'))
|
self.customYLabel.setText(translate('OpenLP.GeneralTab', 'Y'))
|
||||||
self.customHeightLabel.setText(translate('OpenLP.GeneralTab', 'Height'))
|
self.customHeightLabel.setText(translate('OpenLP.GeneralTab', 'Height'))
|
||||||
self.customWidthLabel.setText(translate('OpenLP.GeneralTab', 'Width'))
|
self.customWidthLabel.setText(translate('OpenLP.GeneralTab', 'Width'))
|
||||||
|
self.audioGroupBox.setTitle(
|
||||||
|
translate('OpenLP.GeneralTab', 'Background Audio'))
|
||||||
|
self.startPausedCheckBox.setText(
|
||||||
|
translate('OpenLP.GeneralTab', 'Start background audio paused'))
|
||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
"""
|
"""
|
||||||
@ -290,6 +303,8 @@ class GeneralTab(SettingsTab):
|
|||||||
QtCore.QVariant(self.screens.current[u'size'].height())).toInt()[0])
|
QtCore.QVariant(self.screens.current[u'size'].height())).toInt()[0])
|
||||||
self.customWidthValueEdit.setValue(settings.value(u'width',
|
self.customWidthValueEdit.setValue(settings.value(u'width',
|
||||||
QtCore.QVariant(self.screens.current[u'size'].width())).toInt()[0])
|
QtCore.QVariant(self.screens.current[u'size'].width())).toInt()[0])
|
||||||
|
self.startPausedCheckBox.setChecked(settings.value(
|
||||||
|
u'audio start paused', QtCore.QVariant(True)).toBool())
|
||||||
settings.endGroup()
|
settings.endGroup()
|
||||||
self.customXValueEdit.setEnabled(self.overrideCheckBox.isChecked())
|
self.customXValueEdit.setEnabled(self.overrideCheckBox.isChecked())
|
||||||
self.customYValueEdit.setEnabled(self.overrideCheckBox.isChecked())
|
self.customYValueEdit.setEnabled(self.overrideCheckBox.isChecked())
|
||||||
@ -341,6 +356,8 @@ class GeneralTab(SettingsTab):
|
|||||||
QtCore.QVariant(self.customWidthValueEdit.value()))
|
QtCore.QVariant(self.customWidthValueEdit.value()))
|
||||||
settings.setValue(u'override position',
|
settings.setValue(u'override position',
|
||||||
QtCore.QVariant(self.overrideCheckBox.isChecked()))
|
QtCore.QVariant(self.overrideCheckBox.isChecked()))
|
||||||
|
settings.setValue(u'audio start paused',
|
||||||
|
QtCore.QVariant(self.startPausedCheckBox.isChecked()))
|
||||||
settings.endGroup()
|
settings.endGroup()
|
||||||
# On save update the screens as well
|
# On save update the screens as well
|
||||||
self.postSetUp(True)
|
self.postSetUp(True)
|
||||||
|
@ -97,6 +97,10 @@ class MainDisplay(Display):
|
|||||||
self.override = {}
|
self.override = {}
|
||||||
self.retranslateUi()
|
self.retranslateUi()
|
||||||
self.mediaObject = None
|
self.mediaObject = None
|
||||||
|
if live:
|
||||||
|
self.audioPlayer = AudioPlayer(self)
|
||||||
|
else:
|
||||||
|
self.audioPlayer = None
|
||||||
self.firstTime = True
|
self.firstTime = True
|
||||||
self.setStyleSheet(u'border: 0px; margin: 0px; padding: 0px;')
|
self.setStyleSheet(u'border: 0px; margin: 0px; padding: 0px;')
|
||||||
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.Tool |
|
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.Tool |
|
||||||
@ -127,7 +131,7 @@ class MainDisplay(Display):
|
|||||||
self.webView = QtWebKit.QWebView(self)
|
self.webView = QtWebKit.QWebView(self)
|
||||||
self.webView.setGeometry(0, 0,
|
self.webView.setGeometry(0, 0,
|
||||||
self.screen[u'size'].width(), self.screen[u'size'].height())
|
self.screen[u'size'].width(), self.screen[u'size'].height())
|
||||||
self.webView.settings().setAttribute( \
|
self.webView.settings().setAttribute(
|
||||||
QtWebKit.QWebSettings.PluginsEnabled, True)
|
QtWebKit.QWebSettings.PluginsEnabled, True)
|
||||||
self.page = self.webView.page()
|
self.page = self.webView.page()
|
||||||
self.frame = self.page.mainFrame()
|
self.frame = self.page.mainFrame()
|
||||||
@ -206,8 +210,8 @@ class MainDisplay(Display):
|
|||||||
The slide text to be displayed
|
The slide text to be displayed
|
||||||
"""
|
"""
|
||||||
log.debug(u'alert to display')
|
log.debug(u'alert to display')
|
||||||
if self.height() != self.screen[u'size'].height() \
|
if self.height() != self.screen[u'size'].height() or \
|
||||||
or not self.isVisible():
|
not self.isVisible():
|
||||||
shrink = True
|
shrink = True
|
||||||
else:
|
else:
|
||||||
shrink = False
|
shrink = False
|
||||||
@ -216,19 +220,18 @@ class MainDisplay(Display):
|
|||||||
u'top' if shrink else u'')
|
u'top' if shrink else u'')
|
||||||
height = self.frame.evaluateJavaScript(js)
|
height = self.frame.evaluateJavaScript(js)
|
||||||
if shrink:
|
if shrink:
|
||||||
shrinkItem = self
|
|
||||||
if text:
|
if text:
|
||||||
alert_height = int(height.toString())
|
alert_height = int(height.toString())
|
||||||
shrinkItem.resize(self.width(), alert_height)
|
self.resize(self.width(), alert_height)
|
||||||
shrinkItem.setVisible(True)
|
self.setVisible(True)
|
||||||
if self.alertTab.location == 1:
|
if self.alertTab.location == 1:
|
||||||
shrinkItem.move(self.screen[u'size'].left(),
|
self.move(self.screen[u'size'].left(),
|
||||||
(self.screen[u'size'].height() - alert_height) / 2)
|
(self.screen[u'size'].height() - alert_height) / 2)
|
||||||
elif self.alertTab.location == 2:
|
elif self.alertTab.location == 2:
|
||||||
shrinkItem.move(self.screen[u'size'].left(),
|
self.move(self.screen[u'size'].left(),
|
||||||
self.screen[u'size'].height() - alert_height)
|
self.screen[u'size'].height() - alert_height)
|
||||||
else:
|
else:
|
||||||
shrinkItem.setVisible(False)
|
self.setVisible(False)
|
||||||
self.setGeometry(self.screen[u'size'])
|
self.setGeometry(self.screen[u'size'])
|
||||||
|
|
||||||
def directImage(self, name, path, background):
|
def directImage(self, name, path, background):
|
||||||
@ -452,61 +455,75 @@ class AudioPlayer(QtCore.QObject):
|
|||||||
"""
|
"""
|
||||||
log.debug(u'AudioPlayer Initialisation started')
|
log.debug(u'AudioPlayer Initialisation started')
|
||||||
QtCore.QObject.__init__(self, parent)
|
QtCore.QObject.__init__(self, parent)
|
||||||
self.message = None
|
self.currentIndex = -1
|
||||||
|
self.playlist = []
|
||||||
self.mediaObject = Phonon.MediaObject()
|
self.mediaObject = Phonon.MediaObject()
|
||||||
self.audioObject = Phonon.AudioOutput(Phonon.VideoCategory)
|
self.audioObject = Phonon.AudioOutput(Phonon.VideoCategory)
|
||||||
Phonon.createPath(self.mediaObject, self.audioObject)
|
Phonon.createPath(self.mediaObject, self.audioObject)
|
||||||
|
QtCore.QObject.connect(self.mediaObject,
|
||||||
|
QtCore.SIGNAL(u'aboutToFinish()'), self.onAboutToFinish)
|
||||||
|
|
||||||
def setup(self):
|
def __del__(self):
|
||||||
"""
|
|
||||||
Sets up the Audio Player for use
|
|
||||||
"""
|
|
||||||
log.debug(u'AudioPlayer Setup')
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
"""
|
"""
|
||||||
Shutting down so clean up connections
|
Shutting down so clean up connections
|
||||||
"""
|
"""
|
||||||
self.onMediaStop()
|
self.stop()
|
||||||
for path in self.mediaObject.outputPaths():
|
for path in self.mediaObject.outputPaths():
|
||||||
path.disconnect()
|
path.disconnect()
|
||||||
|
|
||||||
def onMediaQueue(self, message):
|
def onAboutToFinish(self):
|
||||||
"""
|
"""
|
||||||
Set up a video to play from the serviceitem.
|
Just before the audio player finishes the current track, queue the next
|
||||||
|
item in the playlist, if there is one.
|
||||||
"""
|
"""
|
||||||
log.debug(u'AudioPlayer Queue new media message %s' % message)
|
self.currentIndex += 1
|
||||||
mfile = os.path.join(message[0].get_frame_path(),
|
if len(self.playlist) > self.currentIndex:
|
||||||
message[0].get_frame_title())
|
self.mediaObject.enqueue(self.playlist[self.currentIndex])
|
||||||
self.mediaObject.setCurrentSource(Phonon.MediaSource(mfile))
|
|
||||||
self.onMediaPlay()
|
|
||||||
|
|
||||||
def onMediaPlay(self):
|
def connectVolumeSlider(self, slider):
|
||||||
|
slider.setAudioOutput(self.audioObject)
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
"""
|
"""
|
||||||
We want to play the play so start it
|
Reset the audio player, clearing the playlist and the queue.
|
||||||
"""
|
"""
|
||||||
log.debug(u'AudioPlayer _play called')
|
self.currentIndex = -1
|
||||||
|
self.playlist = []
|
||||||
|
self.stop()
|
||||||
|
self.mediaObject.clear()
|
||||||
|
|
||||||
|
def play(self):
|
||||||
|
"""
|
||||||
|
We want to play the file so start it
|
||||||
|
"""
|
||||||
|
log.debug(u'AudioPlayer.play() called')
|
||||||
|
if self.currentIndex == -1:
|
||||||
|
self.onAboutToFinish()
|
||||||
self.mediaObject.play()
|
self.mediaObject.play()
|
||||||
|
|
||||||
def onMediaPause(self):
|
def pause(self):
|
||||||
"""
|
"""
|
||||||
Pause the Audio
|
Pause the Audio
|
||||||
"""
|
"""
|
||||||
log.debug(u'AudioPlayer Media paused by user')
|
log.debug(u'AudioPlayer.pause() called')
|
||||||
self.mediaObject.pause()
|
self.mediaObject.pause()
|
||||||
|
|
||||||
def onMediaStop(self):
|
def stop(self):
|
||||||
"""
|
"""
|
||||||
Stop the Audio and clean up
|
Stop the Audio and clean up
|
||||||
"""
|
"""
|
||||||
log.debug(u'AudioPlayer Media stopped by user')
|
log.debug(u'AudioPlayer.stop() called')
|
||||||
self.message = None
|
|
||||||
self.mediaObject.stop()
|
self.mediaObject.stop()
|
||||||
self.onMediaFinish()
|
|
||||||
|
|
||||||
def onMediaFinish(self):
|
def addToPlaylist(self, filenames):
|
||||||
"""
|
"""
|
||||||
Clean up the Object queue
|
Add another file to the playlist.
|
||||||
|
|
||||||
|
``filename``
|
||||||
|
The file to add to the playlist.
|
||||||
"""
|
"""
|
||||||
log.debug(u'AudioPlayer Reached end of media playlist')
|
if not isinstance(filenames, list):
|
||||||
self.mediaObject.clearQueue()
|
filenames = [filenames]
|
||||||
|
for filename in filenames:
|
||||||
|
self.playlist.append(Phonon.MediaSource(filename))
|
||||||
|
|
||||||
|
@ -541,6 +541,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||||||
self.uiSettingsSection = u'user interface'
|
self.uiSettingsSection = u'user interface'
|
||||||
self.generalSettingsSection = u'general'
|
self.generalSettingsSection = u'general'
|
||||||
self.advancedlSettingsSection = u'advanced'
|
self.advancedlSettingsSection = u'advanced'
|
||||||
|
self.shortcutsSettingsSection = u'shortcuts'
|
||||||
self.servicemanagerSettingsSection = u'servicemanager'
|
self.servicemanagerSettingsSection = u'servicemanager'
|
||||||
self.songsSettingsSection = u'songs'
|
self.songsSettingsSection = u'songs'
|
||||||
self.themesSettingsSection = u'themes'
|
self.themesSettingsSection = u'themes'
|
||||||
@ -779,25 +780,25 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||||||
return
|
return
|
||||||
Receiver.send_message(u'cursor_busy')
|
Receiver.send_message(u'cursor_busy')
|
||||||
screens = ScreenList.get_instance()
|
screens = ScreenList.get_instance()
|
||||||
if FirstTimeForm(screens, self).exec_() == QtGui.QDialog.Accepted:
|
FirstTimeForm(screens, self).exec_()
|
||||||
self.firstTime()
|
self.firstTime()
|
||||||
for plugin in self.pluginManager.plugins:
|
for plugin in self.pluginManager.plugins:
|
||||||
self.activePlugin = plugin
|
self.activePlugin = plugin
|
||||||
oldStatus = self.activePlugin.status
|
oldStatus = self.activePlugin.status
|
||||||
self.activePlugin.setStatus()
|
self.activePlugin.setStatus()
|
||||||
if oldStatus != self.activePlugin.status:
|
if oldStatus != self.activePlugin.status:
|
||||||
if self.activePlugin.status == PluginStatus.Active:
|
if self.activePlugin.status == PluginStatus.Active:
|
||||||
self.activePlugin.toggleStatus(PluginStatus.Active)
|
self.activePlugin.toggleStatus(PluginStatus.Active)
|
||||||
self.activePlugin.appStartup()
|
self.activePlugin.appStartup()
|
||||||
else:
|
else:
|
||||||
self.activePlugin.toggleStatus(PluginStatus.Inactive)
|
self.activePlugin.toggleStatus(PluginStatus.Inactive)
|
||||||
self.themeManagerContents.configUpdated()
|
self.themeManagerContents.configUpdated()
|
||||||
self.themeManagerContents.loadThemes(True)
|
self.themeManagerContents.loadThemes(True)
|
||||||
Receiver.send_message(u'theme_update_global',
|
Receiver.send_message(u'theme_update_global',
|
||||||
self.themeManagerContents.global_theme)
|
self.themeManagerContents.global_theme)
|
||||||
# Check if any Bibles downloaded. If there are, they will be
|
# Check if any Bibles downloaded. If there are, they will be
|
||||||
# processed.
|
# processed.
|
||||||
Receiver.send_message(u'bibles_load_list', True)
|
Receiver.send_message(u'bibles_load_list', True)
|
||||||
|
|
||||||
def blankCheck(self):
|
def blankCheck(self):
|
||||||
"""
|
"""
|
||||||
@ -917,42 +918,43 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||||||
QtGui.QMessageBox.No)
|
QtGui.QMessageBox.No)
|
||||||
if answer == QtGui.QMessageBox.No:
|
if answer == QtGui.QMessageBox.No:
|
||||||
return
|
return
|
||||||
importFileName = unicode(QtGui.QFileDialog.getOpenFileName(self,
|
import_file_name = unicode(QtGui.QFileDialog.getOpenFileName(self,
|
||||||
translate('OpenLP.MainWindow', 'Open File'),
|
translate('OpenLP.MainWindow', 'Open File'),
|
||||||
'',
|
'',
|
||||||
translate('OpenLP.MainWindow',
|
translate('OpenLP.MainWindow',
|
||||||
'OpenLP Export Settings Files (*.conf)')))
|
'OpenLP Export Settings Files (*.conf)')))
|
||||||
if not importFileName:
|
if not import_file_name:
|
||||||
return
|
return
|
||||||
settingSections = []
|
setting_sections = []
|
||||||
# Add main sections.
|
# Add main sections.
|
||||||
settingSections.extend([self.generalSettingsSection])
|
setting_sections.extend([self.generalSettingsSection])
|
||||||
settingSections.extend([self.advancedlSettingsSection])
|
setting_sections.extend([self.advancedlSettingsSection])
|
||||||
settingSections.extend([self.uiSettingsSection])
|
setting_sections.extend([self.uiSettingsSection])
|
||||||
settingSections.extend([self.servicemanagerSettingsSection])
|
setting_sections.extend([self.shortcutsSettingsSection])
|
||||||
settingSections.extend([self.themesSettingsSection])
|
setting_sections.extend([self.servicemanagerSettingsSection])
|
||||||
settingSections.extend([self.displayTagsSection])
|
setting_sections.extend([self.themesSettingsSection])
|
||||||
settingSections.extend([self.headerSection])
|
setting_sections.extend([self.displayTagsSection])
|
||||||
|
setting_sections.extend([self.headerSection])
|
||||||
# Add plugin sections.
|
# Add plugin sections.
|
||||||
for plugin in self.pluginManager.plugins:
|
for plugin in self.pluginManager.plugins:
|
||||||
settingSections.extend([plugin.name])
|
setting_sections.extend([plugin.name])
|
||||||
settings = QtCore.QSettings()
|
settings = QtCore.QSettings()
|
||||||
importSettings = QtCore.QSettings(importFileName,
|
import_settings = QtCore.QSettings(import_file_name,
|
||||||
QtCore.QSettings.IniFormat)
|
QtCore.QSettings.IniFormat)
|
||||||
importKeys = importSettings.allKeys()
|
import_keys = import_settings.allKeys()
|
||||||
for sectionKey in importKeys:
|
for section_key in import_keys:
|
||||||
# We need to handle the really bad files.
|
# We need to handle the really bad files.
|
||||||
try:
|
try:
|
||||||
section, key = sectionKey.split(u'/')
|
section, key = section_key.split(u'/')
|
||||||
except ValueError:
|
except ValueError:
|
||||||
section = u'unknown'
|
section = u'unknown'
|
||||||
key = u''
|
key = u''
|
||||||
# Switch General back to lowercase.
|
# Switch General back to lowercase.
|
||||||
if section == u'General':
|
if section == u'General':
|
||||||
section = u'general'
|
section = u'general'
|
||||||
sectionKey = section + "/" + key
|
section_key = section + "/" + key
|
||||||
# Make sure it's a valid section for us.
|
# Make sure it's a valid section for us.
|
||||||
if not section in settingSections:
|
if not section in setting_sections:
|
||||||
QtGui.QMessageBox.critical(self,
|
QtGui.QMessageBox.critical(self,
|
||||||
translate('OpenLP.MainWindow', 'Import settings'),
|
translate('OpenLP.MainWindow', 'Import settings'),
|
||||||
translate('OpenLP.MainWindow',
|
translate('OpenLP.MainWindow',
|
||||||
@ -965,13 +967,13 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||||||
QtGui.QMessageBox.Ok))
|
QtGui.QMessageBox.Ok))
|
||||||
return
|
return
|
||||||
# We have a good file, import it.
|
# We have a good file, import it.
|
||||||
for sectionKey in importKeys:
|
for section_key in import_keys:
|
||||||
value = importSettings.value(sectionKey)
|
value = import_settings.value(section_key)
|
||||||
settings.setValue(u'%s' % (sectionKey) ,
|
settings.setValue(u'%s' % (section_key) ,
|
||||||
QtCore.QVariant(value))
|
QtCore.QVariant(value))
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
settings.beginGroup(self.headerSection)
|
settings.beginGroup(self.headerSection)
|
||||||
settings.setValue( u'file_imported' , QtCore.QVariant(importFileName))
|
settings.setValue( u'file_imported' , QtCore.QVariant(import_file_name))
|
||||||
settings.setValue(u'file_date_imported',
|
settings.setValue(u'file_date_imported',
|
||||||
now.strftime("%Y-%m-%d %H:%M"))
|
now.strftime("%Y-%m-%d %H:%M"))
|
||||||
settings.endGroup()
|
settings.endGroup()
|
||||||
@ -990,78 +992,76 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||||||
self.cleanUp()
|
self.cleanUp()
|
||||||
QtCore.QCoreApplication.exit()
|
QtCore.QCoreApplication.exit()
|
||||||
|
|
||||||
def onSettingsExportItemClicked(self, exportFileName=None):
|
def onSettingsExportItemClicked(self):
|
||||||
"""
|
"""
|
||||||
Export settings to an INI file
|
Export settings to a .conf file in INI format
|
||||||
"""
|
"""
|
||||||
if not exportFileName:
|
export_file_name = unicode(QtGui.QFileDialog.getSaveFileName(self,
|
||||||
exportFileName = unicode(QtGui.QFileDialog.getSaveFileName(self,
|
translate('OpenLP.MainWindow', 'Export Settings File'), '',
|
||||||
translate('OpenLP.MainWindow', 'Export Settings File'), '',
|
translate('OpenLP.MainWindow',
|
||||||
translate('OpenLP.MainWindow',
|
'OpenLP Export Settings File (*.conf)')))
|
||||||
'OpenLP Export Settings File (*.conf)')))
|
if not export_file_name:
|
||||||
if not exportFileName:
|
|
||||||
return
|
return
|
||||||
# Make sure it's an .ini file.
|
# Make sure it's a .conf file.
|
||||||
if not exportFileName.endswith(u'conf'):
|
if not export_file_name.endswith(u'conf'):
|
||||||
exportFileName = exportFileName + u'.conf'
|
export_file_name = export_file_name + u'.conf'
|
||||||
temp_file = os.path.join(unicode(gettempdir()),
|
temp_file = os.path.join(unicode(gettempdir()),
|
||||||
u'openlp', u'exportIni.tmp')
|
u'openlp', u'exportConf.tmp')
|
||||||
self.saveSettings()
|
self.saveSettings()
|
||||||
settingSections = []
|
setting_sections = []
|
||||||
# Add main sections.
|
# Add main sections.
|
||||||
settingSections.extend([self.generalSettingsSection])
|
setting_sections.extend([self.generalSettingsSection])
|
||||||
settingSections.extend([self.advancedlSettingsSection])
|
setting_sections.extend([self.advancedlSettingsSection])
|
||||||
settingSections.extend([self.uiSettingsSection])
|
setting_sections.extend([self.uiSettingsSection])
|
||||||
settingSections.extend([self.servicemanagerSettingsSection])
|
setting_sections.extend([self.shortcutsSettingsSection])
|
||||||
settingSections.extend([self.themesSettingsSection])
|
setting_sections.extend([self.servicemanagerSettingsSection])
|
||||||
settingSections.extend([self.displayTagsSection])
|
setting_sections.extend([self.themesSettingsSection])
|
||||||
|
setting_sections.extend([self.displayTagsSection])
|
||||||
# Add plugin sections.
|
# Add plugin sections.
|
||||||
for plugin in self.pluginManager.plugins:
|
for plugin in self.pluginManager.plugins:
|
||||||
settingSections.extend([plugin.name])
|
setting_sections.extend([plugin.name])
|
||||||
# Delete old files if found.
|
# Delete old files if found.
|
||||||
if os.path.exists(temp_file):
|
if os.path.exists(temp_file):
|
||||||
os.remove(temp_file)
|
os.remove(temp_file)
|
||||||
if os.path.exists(exportFileName):
|
if os.path.exists(export_file_name):
|
||||||
os.remove(exportFileName)
|
os.remove(export_file_name)
|
||||||
settings = QtCore.QSettings()
|
settings = QtCore.QSettings()
|
||||||
settings.remove(self.headerSection)
|
settings.remove(self.headerSection)
|
||||||
# Get the settings.
|
# Get the settings.
|
||||||
keys = settings.allKeys()
|
keys = settings.allKeys()
|
||||||
exportSettings = QtCore.QSettings(temp_file,
|
export_settings = QtCore.QSettings(temp_file,
|
||||||
QtCore.QSettings.IniFormat)
|
QtCore.QSettings.IniFormat)
|
||||||
# Add a header section.
|
# Add a header section.
|
||||||
# This is to insure it's our ini file for import.
|
# This is to insure it's our conf file for import.
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
applicationVersion = get_application_version()
|
application_version = get_application_version()
|
||||||
# Write INI format using Qsettings.
|
# Write INI format using Qsettings.
|
||||||
# Write our header.
|
# Write our header.
|
||||||
exportSettings.beginGroup(self.headerSection)
|
export_settings.beginGroup(self.headerSection)
|
||||||
exportSettings.setValue(u'Make_Changes', u'At_Own_RISK')
|
export_settings.setValue(u'Make_Changes', u'At_Own_RISK')
|
||||||
exportSettings.setValue(u'type', u'OpenLP_settings_export')
|
export_settings.setValue(u'type', u'OpenLP_settings_export')
|
||||||
exportSettings.setValue(u'file_date_created',
|
export_settings.setValue(u'file_date_created',
|
||||||
now.strftime("%Y-%m-%d %H:%M"))
|
now.strftime("%Y-%m-%d %H:%M"))
|
||||||
exportSettings.setValue(u'version', applicationVersion[u'full'])
|
export_settings.setValue(u'version', application_version[u'full'])
|
||||||
exportSettings.endGroup()
|
export_settings.endGroup()
|
||||||
# Write all the sections and keys.
|
# Write all the sections and keys.
|
||||||
for sectionKey in keys:
|
for section_key in keys:
|
||||||
section, key = sectionKey.split(u'/')
|
section, key = section_key.split(u'/')
|
||||||
keyValue = settings.value(sectionKey)
|
key_value = settings.value(section_key)
|
||||||
sectionKey = section + u"/" + key
|
export_settings.setValue(section_key, key_value)
|
||||||
# Change the service section to servicemanager.
|
export_settings.sync()
|
||||||
if section == u'service':
|
# Temp CONF file has been written. Blanks in keys are now '%20'.
|
||||||
sectionKey = u'servicemanager/' + key
|
# Read the temp file and output the user's CONF file with blanks to
|
||||||
exportSettings.setValue(sectionKey, keyValue)
|
|
||||||
exportSettings.sync()
|
|
||||||
# Temp INI file has been written. Blanks in keys are now '%20'.
|
|
||||||
# Read the temp file and output the user's INI file with blanks to
|
|
||||||
# make it more readable.
|
# make it more readable.
|
||||||
tempIni = open(temp_file, u'r')
|
temp_conf = open(temp_file, u'r')
|
||||||
exportIni = open(exportFileName, u'w')
|
export_conf = open(export_file_name, u'w')
|
||||||
for fileRecord in tempIni:
|
for file_record in temp_conf:
|
||||||
fileRecord = fileRecord.replace(u'%20', u' ')
|
# Get rid of any invalid entries.
|
||||||
exportIni.write(fileRecord)
|
if file_record.find(u'@Invalid()') == -1:
|
||||||
tempIni.close()
|
file_record = file_record.replace(u'%20', u' ')
|
||||||
exportIni.close()
|
export_conf.write(file_record)
|
||||||
|
temp_conf.close()
|
||||||
|
export_conf.close()
|
||||||
os.remove(temp_file)
|
os.remove(temp_file)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -1295,6 +1295,9 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||||||
"""
|
"""
|
||||||
log.debug(u'Loading QSettings')
|
log.debug(u'Loading QSettings')
|
||||||
settings = QtCore.QSettings()
|
settings = QtCore.QSettings()
|
||||||
|
# Remove obsolete entries.
|
||||||
|
settings.remove(u'custom slide')
|
||||||
|
settings.remove(u'service')
|
||||||
settings.beginGroup(self.generalSettingsSection)
|
settings.beginGroup(self.generalSettingsSection)
|
||||||
self.recentFiles = settings.value(u'recent files').toStringList()
|
self.recentFiles = settings.value(u'recent files').toStringList()
|
||||||
settings.endGroup()
|
settings.endGroup()
|
||||||
|
@ -36,6 +36,7 @@ class MediaState(object):
|
|||||||
Paused = 4
|
Paused = 4
|
||||||
Off = 6
|
Off = 6
|
||||||
|
|
||||||
|
|
||||||
class MediaType(object):
|
class MediaType(object):
|
||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
@ -46,6 +47,7 @@ class MediaType(object):
|
|||||||
DVD = 4
|
DVD = 4
|
||||||
Folder = 5
|
Folder = 5
|
||||||
|
|
||||||
|
|
||||||
class MediaInfo(object):
|
class MediaInfo(object):
|
||||||
"""
|
"""
|
||||||
This class hold the media related infos
|
This class hold the media related infos
|
||||||
|
@ -39,6 +39,7 @@ class MediaAPI(object):
|
|||||||
self.available = self.check_available()
|
self.available = self.check_available()
|
||||||
self.isActive = False
|
self.isActive = False
|
||||||
self.canBackground = False
|
self.canBackground = False
|
||||||
|
self.canFolder = False
|
||||||
self.state = MediaState.Off
|
self.state = MediaState.Off
|
||||||
self.hasOwnWidget = False
|
self.hasOwnWidget = False
|
||||||
self.audio_extensions_list = []
|
self.audio_extensions_list = []
|
||||||
|
@ -39,35 +39,24 @@ log = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class MediaController(object):
|
class MediaController(object):
|
||||||
"""
|
"""
|
||||||
The implementation of a Media Manager
|
The implementation of the Media Controller
|
||||||
The idea is to separate the media related implementation
|
The Media Controller adds an own class for every API
|
||||||
into the plugin files and unify the access from other parts of code
|
|
||||||
The media manager adds an own class for every API
|
|
||||||
Currently these are QtWebkit, Phonon and planed Vlc.
|
Currently these are QtWebkit, Phonon and planed Vlc.
|
||||||
Manager
|
|
||||||
- different API classes with specialised Access functions
|
|
||||||
|
|
||||||
Controller
|
|
||||||
- have general and API specific control Elements
|
|
||||||
- have one or more displays (Preview, Live, ...) with different settings
|
|
||||||
|
|
||||||
Display
|
|
||||||
- have API-Specific Display Elements
|
|
||||||
- have media info for current media
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.APIs = {}
|
self.APIs = {}
|
||||||
self.controller = []
|
self.controller = []
|
||||||
|
self.overridenApi = ''
|
||||||
self.curDisplayMediaAPI = {}
|
self.curDisplayMediaAPI = {}
|
||||||
# Timer for video state
|
# Timer for video state
|
||||||
self.Timer = QtCore.QTimer()
|
self.timer = QtCore.QTimer()
|
||||||
self.Timer.setInterval(200)
|
self.timer.setInterval(200)
|
||||||
self.withLivePreview = False
|
self.withLivePreview = False
|
||||||
self.check_available_media_apis()
|
self.check_available_media_apis()
|
||||||
# Signals
|
# Signals
|
||||||
QtCore.QObject.connect(self.Timer,
|
QtCore.QObject.connect(self.timer,
|
||||||
QtCore.SIGNAL("timeout()"), self.video_state)
|
QtCore.SIGNAL("timeout()"), self.video_state)
|
||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
QtCore.SIGNAL(u'Media Start'), self.video_play)
|
QtCore.SIGNAL(u'Media Start'), self.video_play)
|
||||||
@ -85,6 +74,8 @@ class MediaController(object):
|
|||||||
QtCore.SIGNAL(u'media_blank'), self.video_blank)
|
QtCore.SIGNAL(u'media_blank'), self.video_blank)
|
||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
QtCore.SIGNAL(u'media_unblank'), self.video_unblank)
|
QtCore.SIGNAL(u'media_unblank'), self.video_unblank)
|
||||||
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
|
QtCore.SIGNAL(u'media_overrideApi'), self.override_api)
|
||||||
|
|
||||||
def register_controllers(self, controller):
|
def register_controllers(self, controller):
|
||||||
"""
|
"""
|
||||||
@ -132,7 +123,7 @@ class MediaController(object):
|
|||||||
"""
|
"""
|
||||||
isAnyonePlaying = False
|
isAnyonePlaying = False
|
||||||
if len(self.curDisplayMediaAPI.keys()) == 0:
|
if len(self.curDisplayMediaAPI.keys()) == 0:
|
||||||
self.Timer.stop()
|
self.timer.stop()
|
||||||
else:
|
else:
|
||||||
for display in self.curDisplayMediaAPI.keys():
|
for display in self.curDisplayMediaAPI.keys():
|
||||||
self.curDisplayMediaAPI[display].resize(display)
|
self.curDisplayMediaAPI[display].resize(display)
|
||||||
@ -141,7 +132,7 @@ class MediaController(object):
|
|||||||
.state == MediaState.Playing:
|
.state == MediaState.Playing:
|
||||||
isAnyonePlaying = True
|
isAnyonePlaying = True
|
||||||
if not isAnyonePlaying:
|
if not isAnyonePlaying:
|
||||||
self.Timer.stop()
|
self.timer.stop()
|
||||||
|
|
||||||
def get_media_display_css(self):
|
def get_media_display_css(self):
|
||||||
"""
|
"""
|
||||||
@ -287,10 +278,12 @@ class MediaController(object):
|
|||||||
if self.video_play([controller]):
|
if self.video_play([controller]):
|
||||||
self.video_pause([controller])
|
self.video_pause([controller])
|
||||||
self.video_seek([controller, [0]])
|
self.video_seek([controller, [0]])
|
||||||
if self.video_play([controller]):
|
if controller.isLive and QtCore.QSettings().value(u'general/auto unblank',
|
||||||
self.set_controls_visible(controller, True)
|
QtCore.QVariant(False)).toBool():
|
||||||
log.debug(u'use %s controller' % self.curDisplayMediaAPI[display])
|
self.video_play([controller])
|
||||||
return True
|
self.set_controls_visible(controller, True)
|
||||||
|
log.debug(u'use %s controller' % self.curDisplayMediaAPI[display])
|
||||||
|
return True
|
||||||
else:
|
else:
|
||||||
critical_error_message_box(
|
critical_error_message_box(
|
||||||
translate('MediaPlugin.MediaItem', 'Unsupported File'),
|
translate('MediaPlugin.MediaItem', 'Unsupported File'),
|
||||||
@ -306,6 +299,10 @@ class MediaController(object):
|
|||||||
apiSettings = str(QtCore.QSettings().value(u'media/apis',
|
apiSettings = str(QtCore.QSettings().value(u'media/apis',
|
||||||
QtCore.QVariant(u'Webkit')).toString())
|
QtCore.QVariant(u'Webkit')).toString())
|
||||||
usedAPIs = apiSettings.split(u',')
|
usedAPIs = apiSettings.split(u',')
|
||||||
|
if QtCore.QSettings().value(u'media/override api',
|
||||||
|
QtCore.QVariant(QtCore.Qt.Unchecked)) == QtCore.Qt.Checked:
|
||||||
|
if self.overridenApi != '':
|
||||||
|
usedAPIs = [self.overridenApi]
|
||||||
if controller.media_info.file_info.isFile():
|
if controller.media_info.file_info.isFile():
|
||||||
suffix = u'*.%s' % controller.media_info.file_info.suffix()
|
suffix = u'*.%s' % controller.media_info.file_info.suffix()
|
||||||
for title in usedAPIs:
|
for title in usedAPIs:
|
||||||
@ -323,6 +320,15 @@ class MediaController(object):
|
|||||||
self.curDisplayMediaAPI[display] = api
|
self.curDisplayMediaAPI[display] = api
|
||||||
controller.media_info.media_type = MediaType.Audio
|
controller.media_info.media_type = MediaType.Audio
|
||||||
return True
|
return True
|
||||||
|
else:
|
||||||
|
for title in usedAPIs:
|
||||||
|
api = self.APIs[title]
|
||||||
|
if api.canFolder:
|
||||||
|
self.resize(controller, display, api)
|
||||||
|
if api.load(display):
|
||||||
|
self.curDisplayMediaAPI[display] = api
|
||||||
|
controller.media_info.media_type = MediaType.Video
|
||||||
|
return True
|
||||||
# no valid api found
|
# no valid api found
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -341,8 +347,8 @@ class MediaController(object):
|
|||||||
return False
|
return False
|
||||||
self.curDisplayMediaAPI[display].set_visible(display, True)
|
self.curDisplayMediaAPI[display].set_visible(display, True)
|
||||||
# Start Timer for ui updates
|
# Start Timer for ui updates
|
||||||
if not self.Timer.isActive():
|
if not self.timer.isActive():
|
||||||
self.Timer.start()
|
self.timer.start()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def video_pause(self, msg):
|
def video_pause(self, msg):
|
||||||
@ -463,7 +469,16 @@ class MediaController(object):
|
|||||||
video_list.append(item)
|
video_list.append(item)
|
||||||
return video_list
|
return video_list
|
||||||
|
|
||||||
|
def override_api(self, override_api):
|
||||||
|
apiSettings = str(QtCore.QSettings().value(u'media/apis',
|
||||||
|
QtCore.QVariant(u'Webkit')).toString())
|
||||||
|
usedAPIs = apiSettings.split(u',')
|
||||||
|
if override_api in usedAPIs:
|
||||||
|
self.overridenApi = override_api
|
||||||
|
else:
|
||||||
|
self.overridenApi = ''
|
||||||
|
|
||||||
def finalise(self):
|
def finalise(self):
|
||||||
self.Timer.stop()
|
self.timer.stop()
|
||||||
for controller in self.controller:
|
for controller in self.controller:
|
||||||
self.video_reset(controller)
|
self.video_reset(controller)
|
||||||
|
@ -28,6 +28,7 @@ import cgi
|
|||||||
import cPickle
|
import cPickle
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import shutil
|
||||||
import zipfile
|
import zipfile
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -471,23 +472,34 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
if not self.fileName():
|
if not self.fileName():
|
||||||
return self.saveFileAs()
|
return self.saveFileAs()
|
||||||
path_file_name = unicode(self.fileName())
|
path_file_name = unicode(self.fileName())
|
||||||
(path, file_name) = os.path.split(path_file_name)
|
path, file_name = os.path.split(path_file_name)
|
||||||
(basename, extension) = os.path.splitext(file_name)
|
basename, extension = os.path.splitext(file_name)
|
||||||
service_file_name = basename + '.osd'
|
service_file_name = '%s.osd' % basename
|
||||||
log.debug(u'ServiceManager.saveFile - %s' % path_file_name)
|
log.debug(u'ServiceManager.saveFile - %s' % path_file_name)
|
||||||
SettingsManager.set_last_dir(
|
SettingsManager.set_last_dir(
|
||||||
self.mainwindow.servicemanagerSettingsSection,
|
self.mainwindow.servicemanagerSettingsSection,
|
||||||
path)
|
path)
|
||||||
service = []
|
service = []
|
||||||
write_list = []
|
write_list = []
|
||||||
|
audio_files = []
|
||||||
total_size = 0
|
total_size = 0
|
||||||
Receiver.send_message(u'cursor_busy')
|
Receiver.send_message(u'cursor_busy')
|
||||||
# Number of items + 1 to zip it
|
# Number of items + 1 to zip it
|
||||||
self.mainwindow.displayProgressBar(len(self.serviceItems) + 1)
|
self.mainwindow.displayProgressBar(len(self.serviceItems) + 1)
|
||||||
for item in self.serviceItems:
|
for item in self.serviceItems:
|
||||||
self.mainwindow.incrementProgressBar()
|
self.mainwindow.incrementProgressBar()
|
||||||
service.append({u'serviceitem':
|
service_item = item[u'service_item'].get_service_repr()
|
||||||
item[u'service_item'].get_service_repr()})
|
# Get all the audio files, and ready them for embedding in the
|
||||||
|
# service file.
|
||||||
|
if len(service_item[u'header'][u'background_audio']) > 0:
|
||||||
|
for i, filename in \
|
||||||
|
enumerate(service_item[u'header'][u'background_audio']):
|
||||||
|
new_file = os.path.join(u'audio', item[u'service_item']._uuid,
|
||||||
|
os.path.split(filename)[1])
|
||||||
|
audio_files.append((filename, new_file))
|
||||||
|
service_item[u'header'][u'background_audio'][i] = new_file
|
||||||
|
# Add the service item to the service.
|
||||||
|
service.append({u'serviceitem': service_item})
|
||||||
if not item[u'service_item'].uses_file():
|
if not item[u'service_item'].uses_file():
|
||||||
continue
|
continue
|
||||||
skipMissing = False
|
skipMissing = False
|
||||||
@ -541,6 +553,20 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
# Finally add all the listed media files.
|
# Finally add all the listed media files.
|
||||||
for path_from in write_list:
|
for path_from in write_list:
|
||||||
zip.write(path_from, path_from.encode(u'utf-8'))
|
zip.write(path_from, path_from.encode(u'utf-8'))
|
||||||
|
for path_from, path_to in audio_files:
|
||||||
|
if path_from == path_to:
|
||||||
|
# If this file has already been saved, let's use set the
|
||||||
|
# from path to the real location of the files
|
||||||
|
path_from = os.path.join(self.servicePath, path_from)
|
||||||
|
else:
|
||||||
|
# If this file has not yet been saved, let's copy the file
|
||||||
|
# to the service manager path
|
||||||
|
save_file = os.path.join(self.servicePath, path_to)
|
||||||
|
save_path = os.path.split(save_file)[0]
|
||||||
|
if not os.path.exists(save_path):
|
||||||
|
os.makedirs(save_path)
|
||||||
|
shutil.copy(path_from, save_file)
|
||||||
|
zip.write(path_from, path_to.encode(u'utf-8'))
|
||||||
except IOError:
|
except IOError:
|
||||||
log.exception(u'Failed to save service to disk')
|
log.exception(u'Failed to save service to disk')
|
||||||
success = False
|
success = False
|
||||||
@ -586,8 +612,8 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
fileTo = None
|
fileTo = None
|
||||||
try:
|
try:
|
||||||
zip = zipfile.ZipFile(fileName)
|
zip = zipfile.ZipFile(fileName)
|
||||||
for file in zip.namelist():
|
for zipinfo in zip.infolist():
|
||||||
ucsfile = file_is_unicode(file)
|
ucsfile = file_is_unicode(zipinfo.filename)
|
||||||
if not ucsfile:
|
if not ucsfile:
|
||||||
critical_error_message_box(
|
critical_error_message_box(
|
||||||
message=translate('OpenLP.ServiceManager',
|
message=translate('OpenLP.ServiceManager',
|
||||||
@ -595,14 +621,12 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
'The content encoding is not UTF-8.'))
|
'The content encoding is not UTF-8.'))
|
||||||
continue
|
continue
|
||||||
osfile = unicode(QtCore.QDir.toNativeSeparators(ucsfile))
|
osfile = unicode(QtCore.QDir.toNativeSeparators(ucsfile))
|
||||||
filePath = os.path.join(self.servicePath,
|
if not osfile.startswith(u'audio'):
|
||||||
os.path.split(osfile)[1])
|
osfile = os.path.split(osfile)[1]
|
||||||
fileTo = open(filePath, u'wb')
|
zipinfo.filename = osfile
|
||||||
fileTo.write(zip.read(file))
|
zip.extract(zipinfo, self.servicePath)
|
||||||
fileTo.flush()
|
if osfile.endswith(u'osd'):
|
||||||
fileTo.close()
|
p_file = os.path.join(self.servicePath, osfile)
|
||||||
if filePath.endswith(u'osd'):
|
|
||||||
p_file = filePath
|
|
||||||
if 'p_file' in locals():
|
if 'p_file' in locals():
|
||||||
Receiver.send_message(u'cursor_busy')
|
Receiver.send_message(u'cursor_busy')
|
||||||
fileTo = open(p_file, u'r')
|
fileTo = open(p_file, u'r')
|
||||||
@ -633,10 +657,10 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
'File is not a valid service.'))
|
'File is not a valid service.'))
|
||||||
log.exception(u'File contains no service data')
|
log.exception(u'File contains no service data')
|
||||||
except (IOError, NameError, zipfile.BadZipfile):
|
except (IOError, NameError, zipfile.BadZipfile):
|
||||||
|
log.exception(u'Problem loading service file %s' % fileName)
|
||||||
critical_error_message_box(
|
critical_error_message_box(
|
||||||
message=translate('OpenLP.ServiceManager',
|
message=translate('OpenLP.ServiceManager',
|
||||||
'File could not be opened because it is corrupt.'))
|
'File could not be opened because it is corrupt.'))
|
||||||
log.exception(u'Problem loading service file %s' % fileName)
|
|
||||||
except zipfile.BadZipfile:
|
except zipfile.BadZipfile:
|
||||||
if os.path.getsize(fileName) == 0:
|
if os.path.getsize(fileName) == 0:
|
||||||
log.exception(u'Service file is zero sized: %s' % fileName)
|
log.exception(u'Service file is zero sized: %s' % fileName)
|
||||||
@ -685,16 +709,16 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
self.maintainAction.setVisible(False)
|
self.maintainAction.setVisible(False)
|
||||||
self.notesAction.setVisible(False)
|
self.notesAction.setVisible(False)
|
||||||
self.timeAction.setVisible(False)
|
self.timeAction.setVisible(False)
|
||||||
if serviceItem[u'service_item'].is_capable(ItemCapabilities.AllowsEdit)\
|
if serviceItem[u'service_item'].is_capable(ItemCapabilities.CanEdit)\
|
||||||
and serviceItem[u'service_item'].edit_id:
|
and serviceItem[u'service_item'].edit_id:
|
||||||
self.editAction.setVisible(True)
|
self.editAction.setVisible(True)
|
||||||
if serviceItem[u'service_item']\
|
if serviceItem[u'service_item']\
|
||||||
.is_capable(ItemCapabilities.AllowsMaintain):
|
.is_capable(ItemCapabilities.CanMaintain):
|
||||||
self.maintainAction.setVisible(True)
|
self.maintainAction.setVisible(True)
|
||||||
if item.parent() is None:
|
if item.parent() is None:
|
||||||
self.notesAction.setVisible(True)
|
self.notesAction.setVisible(True)
|
||||||
if serviceItem[u'service_item']\
|
if serviceItem[u'service_item']\
|
||||||
.is_capable(ItemCapabilities.AllowsVariableStartTime):
|
.is_capable(ItemCapabilities.HasVariableStartTime):
|
||||||
self.timeAction.setVisible(True)
|
self.timeAction.setVisible(True)
|
||||||
self.themeMenu.menuAction().setVisible(False)
|
self.themeMenu.menuAction().setVisible(False)
|
||||||
# Set up the theme menu.
|
# Set up the theme menu.
|
||||||
@ -965,7 +989,7 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
(unicode(translate('OpenLP.ServiceManager', 'Notes')),
|
(unicode(translate('OpenLP.ServiceManager', 'Notes')),
|
||||||
cgi.escape(unicode(serviceitem.notes))))
|
cgi.escape(unicode(serviceitem.notes))))
|
||||||
if item[u'service_item'] \
|
if item[u'service_item'] \
|
||||||
.is_capable(ItemCapabilities.AllowsVariableStartTime):
|
.is_capable(ItemCapabilities.HasVariableStartTime):
|
||||||
tips.append(item[u'service_item'].get_media_time())
|
tips.append(item[u'service_item'].get_media_time())
|
||||||
treewidgetitem.setToolTip(0, u'<br>'.join(tips))
|
treewidgetitem.setToolTip(0, u'<br>'.join(tips))
|
||||||
treewidgetitem.setData(0, QtCore.Qt.UserRole,
|
treewidgetitem.setData(0, QtCore.Qt.UserRole,
|
||||||
@ -1001,6 +1025,8 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
for file in os.listdir(self.servicePath):
|
for file in os.listdir(self.servicePath):
|
||||||
file_path = os.path.join(self.servicePath, file)
|
file_path = os.path.join(self.servicePath, file)
|
||||||
delete_file(file_path)
|
delete_file(file_path)
|
||||||
|
if os.path.exists(os.path.join(self.servicePath, u'audio')):
|
||||||
|
shutil.rmtree(os.path.join(self.servicePath, u'audio'), False)
|
||||||
|
|
||||||
def onThemeComboBoxSelected(self, currentIndex):
|
def onThemeComboBoxSelected(self, currentIndex):
|
||||||
"""
|
"""
|
||||||
@ -1199,7 +1225,7 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
item += 1
|
item += 1
|
||||||
if self.serviceItems and item < len(self.serviceItems) and \
|
if self.serviceItems and item < len(self.serviceItems) and \
|
||||||
self.serviceItems[item][u'service_item'].is_capable(
|
self.serviceItems[item][u'service_item'].is_capable(
|
||||||
ItemCapabilities.AllowsPreview):
|
ItemCapabilities.CanPreview):
|
||||||
self.mainwindow.previewController.addServiceManagerItem(
|
self.mainwindow.previewController.addServiceManagerItem(
|
||||||
self.serviceItems[item][u'service_item'], 0)
|
self.serviceItems[item][u'service_item'], 0)
|
||||||
self.mainwindow.liveController.previewListWidget.setFocus()
|
self.mainwindow.liveController.previewListWidget.setFocus()
|
||||||
@ -1217,7 +1243,7 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
"""
|
"""
|
||||||
item = self.findServiceItem()[0]
|
item = self.findServiceItem()[0]
|
||||||
if self.serviceItems[item][u'service_item']\
|
if self.serviceItems[item][u'service_item']\
|
||||||
.is_capable(ItemCapabilities.AllowsEdit):
|
.is_capable(ItemCapabilities.CanEdit):
|
||||||
Receiver.send_message(u'%s_edit' %
|
Receiver.send_message(u'%s_edit' %
|
||||||
self.serviceItems[item][u'service_item'].name.lower(),
|
self.serviceItems[item][u'service_item'].name.lower(),
|
||||||
u'L:%s' % self.serviceItems[item][u'service_item'].edit_id)
|
u'L:%s' % self.serviceItems[item][u'service_item'].edit_id)
|
||||||
@ -1300,7 +1326,7 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
serviceItem = self.serviceItems[pos]
|
serviceItem = self.serviceItems[pos]
|
||||||
if (plugin == serviceItem[u'service_item'].name and
|
if (plugin == serviceItem[u'service_item'].name and
|
||||||
serviceItem[u'service_item'].is_capable(
|
serviceItem[u'service_item'].is_capable(
|
||||||
ItemCapabilities.AllowsAdditions)):
|
ItemCapabilities.CanAppend)):
|
||||||
action = self.dndMenu.exec_(QtGui.QCursor.pos())
|
action = self.dndMenu.exec_(QtGui.QCursor.pos())
|
||||||
# New action required
|
# New action required
|
||||||
if action == self.newAction:
|
if action == self.newAction:
|
||||||
|
@ -263,6 +263,12 @@ class SlideController(Controller):
|
|||||||
self.songMenu.setMenu(QtGui.QMenu(
|
self.songMenu.setMenu(QtGui.QMenu(
|
||||||
translate('OpenLP.SlideController', 'Go To'), self.toolbar))
|
translate('OpenLP.SlideController', 'Go To'), self.toolbar))
|
||||||
self.toolbar.makeWidgetsInvisible([u'Song Menu'])
|
self.toolbar.makeWidgetsInvisible([u'Song Menu'])
|
||||||
|
# Stuff for items with background audio.
|
||||||
|
self.audioPauseItem = self.toolbar.addToolbarButton(
|
||||||
|
u'Pause Audio', u':/slides/media_playback_pause.png',
|
||||||
|
translate('OpenLP.SlideController', 'Pause audio.'),
|
||||||
|
self.onAudioPauseClicked, True)
|
||||||
|
self.audioPauseItem.setVisible(False)
|
||||||
# Screen preview area
|
# Screen preview area
|
||||||
self.previewFrame = QtGui.QFrame(self.splitter)
|
self.previewFrame = QtGui.QFrame(self.splitter)
|
||||||
self.previewFrame.setGeometry(QtCore.QRect(0, 0, 300, 300 * self.ratio))
|
self.previewFrame.setGeometry(QtCore.QRect(0, 0, 300, 300 * self.ratio))
|
||||||
@ -507,7 +513,7 @@ class SlideController(Controller):
|
|||||||
self.parent().songsSettingsSection + u'/display songbar',
|
self.parent().songsSettingsSection + u'/display songbar',
|
||||||
QtCore.QVariant(True)).toBool() and len(self.slideList) > 0:
|
QtCore.QVariant(True)).toBool() and len(self.slideList) > 0:
|
||||||
self.toolbar.makeWidgetsVisible([u'Song Menu'])
|
self.toolbar.makeWidgetsVisible([u'Song Menu'])
|
||||||
if item.is_capable(ItemCapabilities.AllowsLoop) and \
|
if item.is_capable(ItemCapabilities.CanLoop) and \
|
||||||
len(item.get_frames()) > 1:
|
len(item.get_frames()) > 1:
|
||||||
self.toolbar.makeWidgetsVisible(self.loopList)
|
self.toolbar.makeWidgetsVisible(self.loopList)
|
||||||
if item.is_media():
|
if item.is_media():
|
||||||
@ -530,7 +536,7 @@ class SlideController(Controller):
|
|||||||
self.toolbar.hide()
|
self.toolbar.hide()
|
||||||
self.mediabar.setVisible(False)
|
self.mediabar.setVisible(False)
|
||||||
self.toolbar.makeWidgetsInvisible(self.songEditList)
|
self.toolbar.makeWidgetsInvisible(self.songEditList)
|
||||||
if item.is_capable(ItemCapabilities.AllowsEdit) and item.from_plugin:
|
if item.is_capable(ItemCapabilities.CanEdit) and item.from_plugin:
|
||||||
self.toolbar.makeWidgetsVisible(self.songEditList)
|
self.toolbar.makeWidgetsVisible(self.songEditList)
|
||||||
elif item.is_media():
|
elif item.is_media():
|
||||||
self.toolbar.setVisible(False)
|
self.toolbar.setVisible(False)
|
||||||
@ -568,7 +574,7 @@ class SlideController(Controller):
|
|||||||
"""
|
"""
|
||||||
Replacement item following a remote edit
|
Replacement item following a remote edit
|
||||||
"""
|
"""
|
||||||
if item.__eq__(self.serviceItem):
|
if item == self.serviceItem:
|
||||||
self._processItem(item, self.previewListWidget.currentRow())
|
self._processItem(item, self.previewListWidget.currentRow())
|
||||||
|
|
||||||
def addServiceManagerItem(self, item, slideno):
|
def addServiceManagerItem(self, item, slideno):
|
||||||
@ -578,15 +584,17 @@ class SlideController(Controller):
|
|||||||
Called by ServiceManager
|
Called by ServiceManager
|
||||||
"""
|
"""
|
||||||
log.debug(u'addServiceManagerItem live = %s' % self.isLive)
|
log.debug(u'addServiceManagerItem live = %s' % self.isLive)
|
||||||
# If no valid slide number is specified we take the first one.
|
# If no valid slide number is specified we take the first one, but we
|
||||||
|
# remember the initial value to see if we should reload the song or not
|
||||||
|
slidenum = slideno
|
||||||
if slideno == -1:
|
if slideno == -1:
|
||||||
slideno = 0
|
slidenum = 0
|
||||||
# If service item is the same as the current on only change slide
|
# If service item is the same as the current one, only change slide
|
||||||
if item.__eq__(self.serviceItem):
|
if slideno >= 0 and item == self.serviceItem:
|
||||||
self.__checkUpdateSelectedSlide(slideno)
|
self.__checkUpdateSelectedSlide(slidenum)
|
||||||
self.slideSelected()
|
self.slideSelected()
|
||||||
return
|
else:
|
||||||
self._processItem(item, slideno)
|
self._processItem(item, slidenum)
|
||||||
|
|
||||||
def _processItem(self, serviceItem, slideno):
|
def _processItem(self, serviceItem, slideno):
|
||||||
"""
|
"""
|
||||||
@ -610,6 +618,22 @@ class SlideController(Controller):
|
|||||||
self.previewListWidget.setColumnWidth(0, width)
|
self.previewListWidget.setColumnWidth(0, width)
|
||||||
if self.isLive:
|
if self.isLive:
|
||||||
self.songMenu.menu().clear()
|
self.songMenu.menu().clear()
|
||||||
|
self.display.audioPlayer.reset()
|
||||||
|
self.setAudioItemsVisibility(False)
|
||||||
|
self.audioPauseItem.setChecked(False)
|
||||||
|
if self.serviceItem.is_capable(ItemCapabilities.HasBackgroundAudio):
|
||||||
|
log.debug(u'Starting to play...')
|
||||||
|
self.display.audioPlayer.addToPlaylist(
|
||||||
|
self.serviceItem.background_audio)
|
||||||
|
if QtCore.QSettings().value(
|
||||||
|
self.parent().generalSettingsSection + \
|
||||||
|
u'/audio start paused',
|
||||||
|
QtCore.QVariant(True)).toBool():
|
||||||
|
self.audioPauseItem.setChecked(True)
|
||||||
|
self.display.audioPlayer.pause()
|
||||||
|
else:
|
||||||
|
self.display.audioPlayer.play()
|
||||||
|
self.setAudioItemsVisibility(True)
|
||||||
row = 0
|
row = 0
|
||||||
text = []
|
text = []
|
||||||
for framenumber, frame in enumerate(self.serviceItem.get_frames()):
|
for framenumber, frame in enumerate(self.serviceItem.get_frames()):
|
||||||
@ -759,6 +783,8 @@ class SlideController(Controller):
|
|||||||
self.onBlankDisplay(True)
|
self.onBlankDisplay(True)
|
||||||
else:
|
else:
|
||||||
Receiver.send_message(u'maindisplay_show')
|
Receiver.send_message(u'maindisplay_show')
|
||||||
|
else:
|
||||||
|
Receiver.send_message(u'maindisplay_hide', HideMode.Screen)
|
||||||
|
|
||||||
def onSlideBlank(self):
|
def onSlideBlank(self):
|
||||||
"""
|
"""
|
||||||
@ -1089,6 +1115,17 @@ class SlideController(Controller):
|
|||||||
self.playSlidesLoop.setChecked(False)
|
self.playSlidesLoop.setChecked(False)
|
||||||
self.onToggleLoop()
|
self.onToggleLoop()
|
||||||
|
|
||||||
|
def setAudioItemsVisibility(self, visible):
|
||||||
|
self.audioPauseItem.setVisible(visible)
|
||||||
|
|
||||||
|
def onAudioPauseClicked(self, checked):
|
||||||
|
if not self.audioPauseItem.isVisible():
|
||||||
|
return
|
||||||
|
if checked:
|
||||||
|
self.display.audioPlayer.pause()
|
||||||
|
else:
|
||||||
|
self.display.audioPlayer.play()
|
||||||
|
|
||||||
def timerEvent(self, event):
|
def timerEvent(self, event):
|
||||||
"""
|
"""
|
||||||
If the timer event is for this window select next slide
|
If the timer event is for this window select next slide
|
||||||
|
@ -218,7 +218,7 @@ class BSExtract(object):
|
|||||||
send_error_message(u'parse')
|
send_error_message(u'parse')
|
||||||
return None
|
return None
|
||||||
content = content.find(u'div').findAll(u'div')
|
content = content.find(u'div').findAll(u'div')
|
||||||
verse_number = re.compile(r'v(\d{1,2})(\d{3})(\d{3}) verse')
|
verse_number = re.compile(r'v(\d{1,2})(\d{3})(\d{3}) verse.*')
|
||||||
verses = {}
|
verses = {}
|
||||||
for verse in content:
|
for verse in content:
|
||||||
Receiver.send_message(u'openlp_process_events')
|
Receiver.send_message(u'openlp_process_events')
|
||||||
|
@ -67,7 +67,7 @@ class BibleMediaItem(MediaManagerItem):
|
|||||||
self.hasSearch = True
|
self.hasSearch = True
|
||||||
self.search_results = {}
|
self.search_results = {}
|
||||||
self.second_search_results = {}
|
self.second_search_results = {}
|
||||||
self.check_search_result()
|
self.checkSearchResult()
|
||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
QtCore.SIGNAL(u'bibles_load_list'), self.reloadBibles)
|
QtCore.SIGNAL(u'bibles_load_list'), self.reloadBibles)
|
||||||
|
|
||||||
@ -651,7 +651,7 @@ class BibleMediaItem(MediaManagerItem):
|
|||||||
elif self.search_results:
|
elif self.search_results:
|
||||||
self.displayResults(bible, second_bible)
|
self.displayResults(bible, second_bible)
|
||||||
self.advancedSearchButton.setEnabled(True)
|
self.advancedSearchButton.setEnabled(True)
|
||||||
self.check_search_result()
|
self.checkSearchResult()
|
||||||
Receiver.send_message(u'cursor_normal')
|
Receiver.send_message(u'cursor_normal')
|
||||||
Receiver.send_message(u'openlp_process_events')
|
Receiver.send_message(u'openlp_process_events')
|
||||||
|
|
||||||
@ -715,7 +715,7 @@ class BibleMediaItem(MediaManagerItem):
|
|||||||
elif self.search_results:
|
elif self.search_results:
|
||||||
self.displayResults(bible, second_bible)
|
self.displayResults(bible, second_bible)
|
||||||
self.quickSearchButton.setEnabled(True)
|
self.quickSearchButton.setEnabled(True)
|
||||||
self.check_search_result()
|
self.checkSearchResult()
|
||||||
Receiver.send_message(u'cursor_normal')
|
Receiver.send_message(u'cursor_normal')
|
||||||
Receiver.send_message(u'openlp_process_events')
|
Receiver.send_message(u'openlp_process_events')
|
||||||
|
|
||||||
@ -788,7 +788,8 @@ class BibleMediaItem(MediaManagerItem):
|
|||||||
items.append(bible_verse)
|
items.append(bible_verse)
|
||||||
return items
|
return items
|
||||||
|
|
||||||
def generateSlideData(self, service_item, item=None, xmlVersion=False):
|
def generateSlideData(self, service_item, item=None, xmlVersion=False,
|
||||||
|
remote=False):
|
||||||
"""
|
"""
|
||||||
Generates and formats the slides for the service item as well as the
|
Generates and formats the slides for the service item as well as the
|
||||||
service item's title.
|
service item's title.
|
||||||
@ -863,9 +864,9 @@ class BibleMediaItem(MediaManagerItem):
|
|||||||
not second_bible:
|
not second_bible:
|
||||||
# Split the line but do not replace line breaks in renderer.
|
# Split the line but do not replace line breaks in renderer.
|
||||||
service_item.add_capability(ItemCapabilities.NoLineBreaks)
|
service_item.add_capability(ItemCapabilities.NoLineBreaks)
|
||||||
service_item.add_capability(ItemCapabilities.AllowsPreview)
|
service_item.add_capability(ItemCapabilities.CanPreview)
|
||||||
service_item.add_capability(ItemCapabilities.AllowsLoop)
|
service_item.add_capability(ItemCapabilities.CanLoop)
|
||||||
service_item.add_capability(ItemCapabilities.AllowsWordSplit)
|
service_item.add_capability(ItemCapabilities.CanWordSplit)
|
||||||
# Service Item: Title
|
# Service Item: Title
|
||||||
service_item.title = u', '.join(raw_title)
|
service_item.title = u', '.join(raw_title)
|
||||||
# Service Item: Theme
|
# Service Item: Theme
|
||||||
|
@ -135,7 +135,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
|||||||
self.customSlide.credits = unicode(self.creditEdit.text())
|
self.customSlide.credits = unicode(self.creditEdit.text())
|
||||||
self.customSlide.theme_name = unicode(self.themeComboBox.currentText())
|
self.customSlide.theme_name = unicode(self.themeComboBox.currentText())
|
||||||
success = self.manager.save_object(self.customSlide)
|
success = self.manager.save_object(self.customSlide)
|
||||||
self.mediaitem.auto_select_id = self.customSlide.id
|
self.mediaitem.autoSelectId = self.customSlide.id
|
||||||
return success
|
return success
|
||||||
|
|
||||||
def onUpButtonClicked(self):
|
def onUpButtonClicked(self):
|
||||||
|
@ -132,7 +132,7 @@ class CustomMediaItem(MediaManagerItem):
|
|||||||
|
|
||||||
def loadList(self, custom_slides):
|
def loadList(self, custom_slides):
|
||||||
# Sort out what custom we want to select after loading the list.
|
# Sort out what custom we want to select after loading the list.
|
||||||
self.save_auto_select_id()
|
self.saveAutoSelectId()
|
||||||
self.listView.clear()
|
self.listView.clear()
|
||||||
# Sort the customs by its title considering language specific
|
# Sort the customs by its title considering language specific
|
||||||
# characters. lower() is needed for windows!
|
# characters. lower() is needed for windows!
|
||||||
@ -144,9 +144,9 @@ class CustomMediaItem(MediaManagerItem):
|
|||||||
QtCore.Qt.UserRole, QtCore.QVariant(custom_slide.id))
|
QtCore.Qt.UserRole, QtCore.QVariant(custom_slide.id))
|
||||||
self.listView.addItem(custom_name)
|
self.listView.addItem(custom_name)
|
||||||
# Auto-select the custom.
|
# Auto-select the custom.
|
||||||
if custom_slide.id == self.auto_select_id:
|
if custom_slide.id == self.autoSelectId:
|
||||||
self.listView.setCurrentItem(custom_name)
|
self.listView.setCurrentItem(custom_name)
|
||||||
self.auto_select_id = -1
|
self.autoSelectId = -1
|
||||||
# Called to redisplay the custom list screen edith from a search
|
# Called to redisplay the custom list screen edith from a search
|
||||||
# or from the exit of the Custom edit dialog. If remote editing is
|
# or from the exit of the Custom edit dialog. If remote editing is
|
||||||
# active trigger it and clean up so it will not update again.
|
# active trigger it and clean up so it will not update again.
|
||||||
@ -180,7 +180,7 @@ class CustomMediaItem(MediaManagerItem):
|
|||||||
self.remoteTriggered = remote_type
|
self.remoteTriggered = remote_type
|
||||||
self.edit_custom_form.loadCustom(custom_id, (remote_type == u'P'))
|
self.edit_custom_form.loadCustom(custom_id, (remote_type == u'P'))
|
||||||
self.edit_custom_form.exec_()
|
self.edit_custom_form.exec_()
|
||||||
self.auto_select_id = -1
|
self.autoSelectId = -1
|
||||||
self.onSearchTextButtonClick()
|
self.onSearchTextButtonClick()
|
||||||
|
|
||||||
def onEditClick(self):
|
def onEditClick(self):
|
||||||
@ -192,7 +192,7 @@ class CustomMediaItem(MediaManagerItem):
|
|||||||
item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
|
item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
|
||||||
self.edit_custom_form.loadCustom(item_id, False)
|
self.edit_custom_form.loadCustom(item_id, False)
|
||||||
self.edit_custom_form.exec_()
|
self.edit_custom_form.exec_()
|
||||||
self.auto_select_id = -1
|
self.autoSelectId = -1
|
||||||
self.onSearchTextButtonClick()
|
self.onSearchTextButtonClick()
|
||||||
|
|
||||||
def onDeleteClick(self):
|
def onDeleteClick(self):
|
||||||
@ -222,15 +222,16 @@ class CustomMediaItem(MediaManagerItem):
|
|||||||
def onFocus(self):
|
def onFocus(self):
|
||||||
self.searchTextEdit.setFocus()
|
self.searchTextEdit.setFocus()
|
||||||
|
|
||||||
def generateSlideData(self, service_item, item=None, xmlVersion=False):
|
def generateSlideData(self, service_item, item=None, xmlVersion=False,
|
||||||
|
remote=False):
|
||||||
raw_footer = []
|
raw_footer = []
|
||||||
slide = None
|
slide = None
|
||||||
theme = None
|
theme = None
|
||||||
item_id = self._getIdOfItemToGenerate(item, self.remoteCustom)
|
item_id = self._getIdOfItemToGenerate(item, self.remoteCustom)
|
||||||
service_item.add_capability(ItemCapabilities.AllowsEdit)
|
service_item.add_capability(ItemCapabilities.CanEdit)
|
||||||
service_item.add_capability(ItemCapabilities.AllowsPreview)
|
service_item.add_capability(ItemCapabilities.CanPreview)
|
||||||
service_item.add_capability(ItemCapabilities.AllowsLoop)
|
service_item.add_capability(ItemCapabilities.CanLoop)
|
||||||
service_item.add_capability(ItemCapabilities.AllowsVirtualSplit)
|
service_item.add_capability(ItemCapabilities.CanSoftBreak)
|
||||||
customSlide = self.plugin.manager.get_object(CustomSlide, item_id)
|
customSlide = self.plugin.manager.get_object(CustomSlide, item_id)
|
||||||
title = customSlide.title
|
title = customSlide.title
|
||||||
credit = customSlide.credits
|
credit = customSlide.credits
|
||||||
@ -273,7 +274,7 @@ class CustomMediaItem(MediaManagerItem):
|
|||||||
CustomSlide.theme_name.like(u'%' + self.whitespace.sub(u' ',
|
CustomSlide.theme_name.like(u'%' + self.whitespace.sub(u' ',
|
||||||
search_keywords) + u'%'), order_by_ref=CustomSlide.title)
|
search_keywords) + u'%'), order_by_ref=CustomSlide.title)
|
||||||
self.loadList(search_results)
|
self.loadList(search_results)
|
||||||
self.check_search_result()
|
self.checkSearchResult()
|
||||||
|
|
||||||
def onSearchTextEditChanged(self, text):
|
def onSearchTextEditChanged(self, text):
|
||||||
"""
|
"""
|
||||||
|
@ -99,6 +99,8 @@ class ImageMediaItem(MediaManagerItem):
|
|||||||
"""
|
"""
|
||||||
Remove an image item from the list
|
Remove an image item from the list
|
||||||
"""
|
"""
|
||||||
|
# Turn off auto preview triggers.
|
||||||
|
self.listView.blockSignals(True)
|
||||||
if check_item_selected(self.listView, translate('ImagePlugin.MediaItem',
|
if check_item_selected(self.listView, translate('ImagePlugin.MediaItem',
|
||||||
'You must select an image to delete.')):
|
'You must select an image to delete.')):
|
||||||
row_list = [item.row() for item in self.listView.selectedIndexes()]
|
row_list = [item.row() for item in self.listView.selectedIndexes()]
|
||||||
@ -111,6 +113,7 @@ class ImageMediaItem(MediaManagerItem):
|
|||||||
self.listView.takeItem(row)
|
self.listView.takeItem(row)
|
||||||
SettingsManager.set_list(self.settingsSection,
|
SettingsManager.set_list(self.settingsSection,
|
||||||
u'images', self.getFileList())
|
u'images', self.getFileList())
|
||||||
|
self.listView.blockSignals(False)
|
||||||
|
|
||||||
def loadList(self, images, initialLoad=False):
|
def loadList(self, images, initialLoad=False):
|
||||||
if not initialLoad:
|
if not initialLoad:
|
||||||
@ -139,7 +142,8 @@ class ImageMediaItem(MediaManagerItem):
|
|||||||
if not initialLoad:
|
if not initialLoad:
|
||||||
self.plugin.formparent.finishedProgressBar()
|
self.plugin.formparent.finishedProgressBar()
|
||||||
|
|
||||||
def generateSlideData(self, service_item, item=None, xmlVersion=False):
|
def generateSlideData(self, service_item, item=None, xmlVersion=False,
|
||||||
|
remote=False):
|
||||||
background = QtGui.QColor(QtCore.QSettings().value(self.settingsSection
|
background = QtGui.QColor(QtCore.QSettings().value(self.settingsSection
|
||||||
+ u'/background color', QtCore.QVariant(u'#000000')))
|
+ u'/background color', QtCore.QVariant(u'#000000')))
|
||||||
if item:
|
if item:
|
||||||
@ -149,10 +153,10 @@ class ImageMediaItem(MediaManagerItem):
|
|||||||
if not items:
|
if not items:
|
||||||
return False
|
return False
|
||||||
service_item.title = unicode(self.plugin.nameStrings[u'plural'])
|
service_item.title = unicode(self.plugin.nameStrings[u'plural'])
|
||||||
service_item.add_capability(ItemCapabilities.AllowsMaintain)
|
service_item.add_capability(ItemCapabilities.CanMaintain)
|
||||||
service_item.add_capability(ItemCapabilities.AllowsPreview)
|
service_item.add_capability(ItemCapabilities.CanPreview)
|
||||||
service_item.add_capability(ItemCapabilities.AllowsLoop)
|
service_item.add_capability(ItemCapabilities.CanLoop)
|
||||||
service_item.add_capability(ItemCapabilities.AllowsAdditions)
|
service_item.add_capability(ItemCapabilities.CanAppend)
|
||||||
# force a nonexistent theme
|
# force a nonexistent theme
|
||||||
service_item.theme = -1
|
service_item.theme = -1
|
||||||
missing_items = []
|
missing_items = []
|
||||||
@ -166,11 +170,12 @@ class ImageMediaItem(MediaManagerItem):
|
|||||||
items.remove(item)
|
items.remove(item)
|
||||||
# We cannot continue, as all images do not exist.
|
# We cannot continue, as all images do not exist.
|
||||||
if not items:
|
if not items:
|
||||||
critical_error_message_box(
|
if not remote:
|
||||||
translate('ImagePlugin.MediaItem', 'Missing Image(s)'),
|
critical_error_message_box(
|
||||||
unicode(translate('ImagePlugin.MediaItem',
|
translate('ImagePlugin.MediaItem', 'Missing Image(s)'),
|
||||||
'The following image(s) no longer exist: %s')) %
|
unicode(translate('ImagePlugin.MediaItem',
|
||||||
u'\n'.join(missing_items_filenames))
|
'The following image(s) no longer exist: %s')) %
|
||||||
|
u'\n'.join(missing_items_filenames))
|
||||||
return False
|
return False
|
||||||
# We have missing as well as existing images. We ask what to do.
|
# We have missing as well as existing images. We ask what to do.
|
||||||
elif missing_items and QtGui.QMessageBox.question(self,
|
elif missing_items and QtGui.QMessageBox.question(self,
|
||||||
|
28
openlp/plugins/media/forms/__init__.py
Normal file
28
openlp/plugins/media/forms/__init__.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||||
|
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
|
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
from mediaopenform import MediaOpenForm
|
297
openlp/plugins/media/forms/mediaopendialog.py
Normal file
297
openlp/plugins/media/forms/mediaopendialog.py
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||||
|
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
|
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
|
from openlp.core.lib import translate
|
||||||
|
from openlp.core.lib.ui import UiStrings
|
||||||
|
|
||||||
|
class Ui_MediaOpenDialog(object):
|
||||||
|
def setupUi(self, mediaOpenDialog):
|
||||||
|
mediaOpenDialog.setObjectName(u'mediaOpenDialog')
|
||||||
|
mediaOpenDialog.resize(574, 431)
|
||||||
|
self.verticalLayout = QtGui.QVBoxLayout(mediaOpenDialog)
|
||||||
|
self.verticalLayout.setObjectName(u'verticalLayout')
|
||||||
|
self.MediaOpenWidget = QtGui.QTabWidget(mediaOpenDialog)
|
||||||
|
self.MediaOpenWidget.setTabPosition(QtGui.QTabWidget.North)
|
||||||
|
self.MediaOpenWidget.setTabShape(QtGui.QTabWidget.Rounded)
|
||||||
|
self.MediaOpenWidget.setTabsClosable(False)
|
||||||
|
self.MediaOpenWidget.setObjectName(u'MediaOpenWidget')
|
||||||
|
self.FileTab = QtGui.QWidget()
|
||||||
|
self.FileTab.setObjectName(u'FileTab')
|
||||||
|
self.verticalLayout_2 = QtGui.QVBoxLayout(self.FileTab)
|
||||||
|
self.verticalLayout_2.setObjectName(u'verticalLayout_2')
|
||||||
|
self.FileSelectionGroupBox = QtGui.QGroupBox(self.FileTab)
|
||||||
|
self.FileSelectionGroupBox.setObjectName(u'FileSelectionGroupBox')
|
||||||
|
self.verticalLayout_7 = QtGui.QVBoxLayout(self.FileSelectionGroupBox)
|
||||||
|
self.verticalLayout_7.setObjectName(u'verticalLayout_7')
|
||||||
|
self.ChooseFilesLabel = QtGui.QLabel(self.FileSelectionGroupBox)
|
||||||
|
self.ChooseFilesLabel.setObjectName(u'ChooseFilesLabel')
|
||||||
|
self.verticalLayout_7.addWidget(self.ChooseFilesLabel)
|
||||||
|
self.horizontalLayout = QtGui.QHBoxLayout()
|
||||||
|
self.horizontalLayout.setObjectName(u'horizontalLayout')
|
||||||
|
self.FilesListView = QtGui.QListWidget(self.FileSelectionGroupBox)
|
||||||
|
self.FilesListView.setObjectName(u'FilesListView')
|
||||||
|
self.horizontalLayout.addWidget(self.FilesListView)
|
||||||
|
self.AddRemovelLayout = QtGui.QVBoxLayout()
|
||||||
|
self.AddRemovelLayout.setObjectName(u'AddRemovelLayout')
|
||||||
|
self.FileAddButton = QtGui.QPushButton(self.FileSelectionGroupBox)
|
||||||
|
self.FileAddButton.setObjectName(u'FileAddButton')
|
||||||
|
self.AddRemovelLayout.addWidget(self.FileAddButton)
|
||||||
|
self.FileRemoveButton = QtGui.QPushButton(self.FileSelectionGroupBox)
|
||||||
|
self.FileRemoveButton.setObjectName(u'FileRemoveButton')
|
||||||
|
self.AddRemovelLayout.addWidget(self.FileRemoveButton)
|
||||||
|
spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum,
|
||||||
|
QtGui.QSizePolicy.Expanding)
|
||||||
|
self.AddRemovelLayout.addItem(spacerItem)
|
||||||
|
self.horizontalLayout.addLayout(self.AddRemovelLayout)
|
||||||
|
self.verticalLayout_7.addLayout(self.horizontalLayout)
|
||||||
|
spacerItem1 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum,
|
||||||
|
QtGui.QSizePolicy.Expanding)
|
||||||
|
self.verticalLayout_7.addItem(spacerItem1)
|
||||||
|
self.verticalLayout_2.addWidget(self.FileSelectionGroupBox)
|
||||||
|
self.MediaOpenWidget.addTab(self.FileTab, u'')
|
||||||
|
self.MediaTab = QtGui.QWidget()
|
||||||
|
self.MediaTab.setObjectName(u'MediaTab')
|
||||||
|
self.verticalLayout_3 = QtGui.QVBoxLayout(self.MediaTab)
|
||||||
|
self.verticalLayout_3.setObjectName(u'verticalLayout_3')
|
||||||
|
self.ChooseMediaGroupBox = QtGui.QGroupBox(self.MediaTab)
|
||||||
|
self.ChooseMediaGroupBox.setObjectName(u'ChooseMediaGroupBox')
|
||||||
|
self.verticalLayout_8 = QtGui.QVBoxLayout(self.ChooseMediaGroupBox)
|
||||||
|
self.verticalLayout_8.setObjectName(u'verticalLayout_8')
|
||||||
|
self.MediaTypeWidget = QtGui.QWidget(self.ChooseMediaGroupBox)
|
||||||
|
self.MediaTypeWidget.setObjectName(u'MediaTypeWidget')
|
||||||
|
self.horizontalLayout_2 = QtGui.QHBoxLayout(self.MediaTypeWidget)
|
||||||
|
self.horizontalLayout_2.setMargin(0)
|
||||||
|
self.horizontalLayout_2.setObjectName(u'horizontalLayout_2')
|
||||||
|
self.DvdRadioButton = QtGui.QRadioButton(self.MediaTypeWidget)
|
||||||
|
self.DvdRadioButton.setObjectName(u'DvdRadioButton')
|
||||||
|
self.horizontalLayout_2.addWidget(self.DvdRadioButton)
|
||||||
|
self.AudioCdRadioButton = QtGui.QRadioButton(self.MediaTypeWidget)
|
||||||
|
self.AudioCdRadioButton.setObjectName(u'AudioCdRadioButton')
|
||||||
|
self.horizontalLayout_2.addWidget(self.AudioCdRadioButton)
|
||||||
|
self.verticalLayout_8.addWidget(self.MediaTypeWidget)
|
||||||
|
self.DeviceWidget = QtGui.QWidget(self.ChooseMediaGroupBox)
|
||||||
|
self.DeviceWidget.setObjectName(u'DeviceWidget')
|
||||||
|
self.horizontalLayout_10 = QtGui.QHBoxLayout(self.DeviceWidget)
|
||||||
|
self.horizontalLayout_10.setMargin(0)
|
||||||
|
self.horizontalLayout_10.setObjectName(u'horizontalLayout_10')
|
||||||
|
self.DeviceLabel = QtGui.QLabel(self.DeviceWidget)
|
||||||
|
self.DeviceLabel.setObjectName(u'DeviceLabel')
|
||||||
|
self.horizontalLayout_10.addWidget(self.DeviceLabel)
|
||||||
|
self.DeviceComboBox = QtGui.QComboBox(self.DeviceWidget)
|
||||||
|
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding,
|
||||||
|
QtGui.QSizePolicy.Fixed)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth( \
|
||||||
|
self.DeviceComboBox.sizePolicy().hasHeightForWidth())
|
||||||
|
self.DeviceComboBox.setSizePolicy(sizePolicy)
|
||||||
|
self.DeviceComboBox.setObjectName(u'DeviceComboBox')
|
||||||
|
self.horizontalLayout_10.addWidget(self.DeviceComboBox)
|
||||||
|
self.DeviceEject = QtGui.QPushButton(self.DeviceWidget)
|
||||||
|
sizePolicy = QtGui.QSizePolicy( \
|
||||||
|
QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth( \
|
||||||
|
self.DeviceEject.sizePolicy().hasHeightForWidth())
|
||||||
|
self.DeviceEject.setSizePolicy(sizePolicy)
|
||||||
|
self.DeviceEject.setObjectName(u'DeviceEject')
|
||||||
|
self.horizontalLayout_10.addWidget(self.DeviceEject)
|
||||||
|
self.DeviceSearchButton = QtGui.QPushButton(self.DeviceWidget)
|
||||||
|
self.DeviceSearchButton.setObjectName(u'DeviceSearchButton')
|
||||||
|
self.horizontalLayout_10.addWidget(self.DeviceSearchButton)
|
||||||
|
self.verticalLayout_8.addWidget(self.DeviceWidget)
|
||||||
|
self.verticalLayout_3.addWidget(self.ChooseMediaGroupBox)
|
||||||
|
self.StartpositionGroupBox = QtGui.QGroupBox(self.MediaTab)
|
||||||
|
self.StartpositionGroupBox.setObjectName(u'StartpositionGroupBox')
|
||||||
|
self.horizontalLayout_4 = QtGui.QHBoxLayout(self.StartpositionGroupBox)
|
||||||
|
self.horizontalLayout_4.setObjectName(u'horizontalLayout_4')
|
||||||
|
self.TitleWidget = QtGui.QWidget(self.StartpositionGroupBox)
|
||||||
|
self.TitleWidget.setObjectName(u'TitleWidget')
|
||||||
|
self.horizontalLayout_7 = QtGui.QHBoxLayout(self.TitleWidget)
|
||||||
|
self.horizontalLayout_7.setMargin(0)
|
||||||
|
self.horizontalLayout_7.setObjectName(u'horizontalLayout_7')
|
||||||
|
self.TitleLabel = QtGui.QLabel(self.TitleWidget)
|
||||||
|
sizePolicy = QtGui.QSizePolicy( \
|
||||||
|
QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Preferred)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth( \
|
||||||
|
self.TitleLabel.sizePolicy().hasHeightForWidth())
|
||||||
|
self.TitleLabel.setSizePolicy(sizePolicy)
|
||||||
|
self.TitleLabel.setObjectName(u'TitleLabel')
|
||||||
|
self.horizontalLayout_7.addWidget(self.TitleLabel)
|
||||||
|
self.horizontalLayout_4.addWidget(self.TitleWidget)
|
||||||
|
self.ChapterWidget = QtGui.QWidget(self.StartpositionGroupBox)
|
||||||
|
self.ChapterWidget.setEnabled(True)
|
||||||
|
self.ChapterWidget.setObjectName(u'ChapterWidget')
|
||||||
|
self.horizontalLayout_6 = QtGui.QHBoxLayout(self.ChapterWidget)
|
||||||
|
self.horizontalLayout_6.setMargin(0)
|
||||||
|
self.horizontalLayout_6.setObjectName(u'horizontalLayout_6')
|
||||||
|
self.ChapterLabel = QtGui.QLabel(self.ChapterWidget)
|
||||||
|
sizePolicy = QtGui.QSizePolicy( \
|
||||||
|
QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Preferred)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth( \
|
||||||
|
self.ChapterLabel.sizePolicy().hasHeightForWidth())
|
||||||
|
self.ChapterLabel.setSizePolicy(sizePolicy)
|
||||||
|
self.ChapterLabel.setObjectName(u'ChapterLabel')
|
||||||
|
self.horizontalLayout_6.addWidget(self.ChapterLabel)
|
||||||
|
self.ChapterSpinBox = QtGui.QSpinBox(self.ChapterWidget)
|
||||||
|
self.ChapterSpinBox.setObjectName(u'ChapterSpinBox')
|
||||||
|
self.horizontalLayout_6.addWidget(self.ChapterSpinBox)
|
||||||
|
self.horizontalLayout_4.addWidget(self.ChapterWidget)
|
||||||
|
self.verticalLayout_3.addWidget(self.StartpositionGroupBox)
|
||||||
|
self.AudioSubtitleGroupBox = QtGui.QGroupBox(self.MediaTab)
|
||||||
|
self.AudioSubtitleGroupBox.setObjectName(u'AudioSubtitleGroupBox')
|
||||||
|
self.horizontalLayout_5 = QtGui.QHBoxLayout(self.AudioSubtitleGroupBox)
|
||||||
|
self.horizontalLayout_5.setObjectName(u'horizontalLayout_5')
|
||||||
|
self.AudioTrackWidget = QtGui.QWidget(self.AudioSubtitleGroupBox)
|
||||||
|
self.AudioTrackWidget.setObjectName(u'AudioTrackWidget')
|
||||||
|
self.horizontalLayout_9 = QtGui.QHBoxLayout(self.AudioTrackWidget)
|
||||||
|
self.horizontalLayout_9.setMargin(0)
|
||||||
|
self.horizontalLayout_9.setObjectName(u'horizontalLayout_9')
|
||||||
|
self.AudioTrackLabel_2 = QtGui.QLabel(self.AudioTrackWidget)
|
||||||
|
sizePolicy = QtGui.QSizePolicy( \
|
||||||
|
QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Preferred)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth( \
|
||||||
|
self.AudioTrackLabel_2.sizePolicy().hasHeightForWidth())
|
||||||
|
self.AudioTrackLabel_2.setSizePolicy(sizePolicy)
|
||||||
|
self.AudioTrackLabel_2.setObjectName(u'AudioTrackLabel_2')
|
||||||
|
self.horizontalLayout_9.addWidget(self.AudioTrackLabel_2)
|
||||||
|
self.TitleSpinBox = QtGui.QSpinBox(self.AudioTrackWidget)
|
||||||
|
self.TitleSpinBox.setObjectName(u'TitleSpinBox')
|
||||||
|
self.horizontalLayout_9.addWidget(self.TitleSpinBox)
|
||||||
|
self.horizontalLayout_5.addWidget(self.AudioTrackWidget)
|
||||||
|
self.SubtitleTrackWidget = QtGui.QWidget(self.AudioSubtitleGroupBox)
|
||||||
|
self.SubtitleTrackWidget.setObjectName(u'SubtitleTrackWidget')
|
||||||
|
self.horizontalLayout_8 = QtGui.QHBoxLayout(self.SubtitleTrackWidget)
|
||||||
|
self.horizontalLayout_8.setMargin(0)
|
||||||
|
self.horizontalLayout_8.setObjectName(u'horizontalLayout_8')
|
||||||
|
self.SubtitleTrackLabel = QtGui.QLabel(self.SubtitleTrackWidget)
|
||||||
|
sizePolicy = QtGui.QSizePolicy( \
|
||||||
|
QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Preferred)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth( \
|
||||||
|
self.SubtitleTrackLabel.sizePolicy().hasHeightForWidth())
|
||||||
|
self.SubtitleTrackLabel.setSizePolicy(sizePolicy)
|
||||||
|
self.SubtitleTrackLabel.setObjectName(u'SubtitleTrackLabel')
|
||||||
|
self.horizontalLayout_8.addWidget(self.SubtitleTrackLabel)
|
||||||
|
self.SubtitleTrackSpinBox = QtGui.QSpinBox(self.SubtitleTrackWidget)
|
||||||
|
self.SubtitleTrackSpinBox.setObjectName(u'SubtitleTrackSpinBox')
|
||||||
|
self.horizontalLayout_8.addWidget(self.SubtitleTrackSpinBox)
|
||||||
|
self.horizontalLayout_5.addWidget(self.SubtitleTrackWidget)
|
||||||
|
self.verticalLayout_3.addWidget(self.AudioSubtitleGroupBox)
|
||||||
|
spacerItem2 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, \
|
||||||
|
QtGui.QSizePolicy.Expanding)
|
||||||
|
self.verticalLayout_3.addItem(spacerItem2)
|
||||||
|
self.MediaOpenWidget.addTab(self.MediaTab, u'')
|
||||||
|
self.NetworkTab = QtGui.QWidget()
|
||||||
|
self.NetworkTab.setObjectName(u'NetworkTab')
|
||||||
|
self.verticalLayout_4 = QtGui.QVBoxLayout(self.NetworkTab)
|
||||||
|
self.verticalLayout_4.setObjectName(u'verticalLayout_4')
|
||||||
|
self.NetworkprotocolGroupBox = QtGui.QGroupBox(self.NetworkTab)
|
||||||
|
self.NetworkprotocolGroupBox.setObjectName(u'NetworkprotocolGroupBox')
|
||||||
|
self.verticalLayout_5 = QtGui.QVBoxLayout(self.NetworkprotocolGroupBox)
|
||||||
|
self.verticalLayout_5.setObjectName(u'verticalLayout_5')
|
||||||
|
self.NetworkAdressLabel = QtGui.QLabel(self.NetworkprotocolGroupBox)
|
||||||
|
self.NetworkAdressLabel.setObjectName(u'NetworkAdressLabel')
|
||||||
|
self.verticalLayout_5.addWidget(self.NetworkAdressLabel)
|
||||||
|
self.NetworkAdressEdit = QtGui.QLineEdit(self.NetworkprotocolGroupBox)
|
||||||
|
self.NetworkAdressEdit.setObjectName(u'NetworkAdressEdit')
|
||||||
|
self.verticalLayout_5.addWidget(self.NetworkAdressEdit)
|
||||||
|
spacerItem3 = QtGui.QSpacerItem(20, 259, QtGui.QSizePolicy.Minimum, \
|
||||||
|
QtGui.QSizePolicy.Expanding)
|
||||||
|
self.verticalLayout_5.addItem(spacerItem3)
|
||||||
|
self.verticalLayout_4.addWidget(self.NetworkprotocolGroupBox)
|
||||||
|
self.MediaOpenWidget.addTab(self.NetworkTab, u'')
|
||||||
|
self.verticalLayout.addWidget(self.MediaOpenWidget)
|
||||||
|
self.ButtonBox = QtGui.QDialogButtonBox(mediaOpenDialog)
|
||||||
|
self.ButtonBox.setStandardButtons( \
|
||||||
|
QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
|
||||||
|
self.ButtonBox.setObjectName(u'ButtonBox')
|
||||||
|
self.verticalLayout.addWidget(self.ButtonBox)
|
||||||
|
|
||||||
|
self.retranslateUi(mediaOpenDialog)
|
||||||
|
self.MediaOpenWidget.setCurrentIndex(0)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(mediaOpenDialog)
|
||||||
|
|
||||||
|
def retranslateUi(self, mediaOpenDialog):
|
||||||
|
mediaOpenDialog.setWindowTitle(
|
||||||
|
translate('MediaPlugin', 'mediaOpenForm'))
|
||||||
|
self.FileSelectionGroupBox.setTitle(translate(
|
||||||
|
'MediaPlugin', 'File Selection'))
|
||||||
|
self.ChooseFilesLabel.setText(
|
||||||
|
translate('MediaPlugin', 'Choose Files with the Buttons right.'))
|
||||||
|
self.FileAddButton.setText(
|
||||||
|
translate('MediaPlugin', 'Add ...'))
|
||||||
|
self.FileRemoveButton.setText(
|
||||||
|
translate('MediaPlugin', 'Remove'))
|
||||||
|
self.MediaOpenWidget.setTabText(
|
||||||
|
self.MediaOpenWidget.indexOf(self.FileTab),
|
||||||
|
translate('MediaPlugin', 'File(s)'))
|
||||||
|
self.ChooseMediaGroupBox.setTitle(
|
||||||
|
translate('MediaPlugin', 'Choose Media'))
|
||||||
|
self.DvdRadioButton.setText(
|
||||||
|
translate('MediaPlugin', 'DVD'))
|
||||||
|
self.AudioCdRadioButton.setText(
|
||||||
|
translate('MediaPlugin', 'Audio-CD'))
|
||||||
|
self.DeviceLabel.setText(
|
||||||
|
translate('MediaPlugin', 'Device'))
|
||||||
|
self.DeviceEject.setText(
|
||||||
|
translate('MediaPlugin', 'Eject'))
|
||||||
|
self.DeviceSearchButton.setText(
|
||||||
|
translate('MediaPlugin', 'Search ...'))
|
||||||
|
self.StartpositionGroupBox.setTitle(
|
||||||
|
translate('MediaPlugin', 'Startposition'))
|
||||||
|
self.TitleLabel.setText(
|
||||||
|
translate('MediaPlugin', 'Title'))
|
||||||
|
self.ChapterLabel.setText(
|
||||||
|
translate('MediaPlugin', 'Chapter'))
|
||||||
|
self.AudioSubtitleGroupBox.setTitle(
|
||||||
|
translate('MediaPlugin', 'Audio and Subtitle'))
|
||||||
|
self.AudioTrackLabel_2.setText(
|
||||||
|
translate('MediaPlugin', 'Audiotrack'))
|
||||||
|
self.SubtitleTrackLabel.setText(
|
||||||
|
translate('MediaPlugin', 'Subtitletrack'))
|
||||||
|
self.MediaOpenWidget.setTabText(
|
||||||
|
self.MediaOpenWidget.indexOf(self.MediaTab),
|
||||||
|
translate('MediaPlugin', 'Location'))
|
||||||
|
self.NetworkprotocolGroupBox.setTitle(
|
||||||
|
translate('MediaPlugin', 'Networkprotocol'))
|
||||||
|
self.NetworkAdressLabel.setText(
|
||||||
|
translate('MediaPlugin', 'Network adress:'))
|
||||||
|
self.MediaOpenWidget.setTabText(
|
||||||
|
self.MediaOpenWidget.indexOf(self.NetworkTab),
|
||||||
|
translate('MediaPlugin', 'Network'))
|
||||||
|
|
50
openlp/plugins/media/forms/mediaopenform.py
Normal file
50
openlp/plugins/media/forms/mediaopenform.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||||
|
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
|
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
|
from openlp.core.lib import Receiver, translate
|
||||||
|
from mediaopendialog import Ui_MediaOpenDialog
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class MediaOpenForm(QtGui.QDialog, Ui_MediaOpenDialog):
|
||||||
|
"""
|
||||||
|
Class documentation goes here.
|
||||||
|
"""
|
||||||
|
log.info(u'Media Open Form loaded')
|
||||||
|
def __init__(self, parent):
|
||||||
|
"""
|
||||||
|
Constructor
|
||||||
|
"""
|
||||||
|
QtGui.QDialog.__init__(self, parent)
|
||||||
|
self.setupUi(self)
|
||||||
|
# Connecting signals and slots
|
||||||
|
# QtCore.QObject.connect(self.previewButton,
|
||||||
|
# QtCore.SIGNAL(u'pressed()'), self.onPreviewButtonPressed)
|
@ -30,11 +30,14 @@ import os
|
|||||||
import locale
|
import locale
|
||||||
|
|
||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
|
from PyQt4.phonon import Phonon
|
||||||
|
|
||||||
from openlp.core.lib import MediaManagerItem, build_icon, ItemCapabilities, \
|
from openlp.core.lib import MediaManagerItem, build_icon, ItemCapabilities, \
|
||||||
SettingsManager, translate, check_item_selected, Receiver
|
SettingsManager, translate, check_item_selected, Receiver, MediaType
|
||||||
from openlp.core.lib.ui import UiStrings, critical_error_message_box
|
from openlp.core.lib.ui import UiStrings, critical_error_message_box, \
|
||||||
|
media_item_combo_box
|
||||||
from openlp.core.ui import Controller, Display
|
from openlp.core.ui import Controller, Display
|
||||||
|
from openlp.plugins.media.forms import MediaOpenForm
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -47,11 +50,13 @@ class MediaMediaItem(MediaManagerItem):
|
|||||||
log.info(u'%s MediaMediaItem loaded', __name__)
|
log.info(u'%s MediaMediaItem loaded', __name__)
|
||||||
|
|
||||||
def __init__(self, parent, plugin, icon):
|
def __init__(self, parent, plugin, icon):
|
||||||
self.IconPath = u'images/image'
|
self.iconPath = u'images/image'
|
||||||
self.background = False
|
self.background = False
|
||||||
self.PreviewFunction = CLAPPERBOARD
|
self.PreviewFunction = CLAPPERBOARD
|
||||||
|
self.Automatic = u''
|
||||||
MediaManagerItem.__init__(self, parent, plugin, icon)
|
MediaManagerItem.__init__(self, parent, plugin, icon)
|
||||||
self.singleServiceItem = False
|
self.singleServiceItem = False
|
||||||
|
self.mediaOpenForm = MediaOpenForm(self.plugin.formparent)
|
||||||
self.hasSearch = True
|
self.hasSearch = True
|
||||||
self.mediaObject = None
|
self.mediaObject = None
|
||||||
self.mediaController = Controller(parent)
|
self.mediaController = Controller(parent)
|
||||||
@ -70,6 +75,8 @@ class MediaMediaItem(MediaManagerItem):
|
|||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
QtCore.SIGNAL(u'video_background_replaced'),
|
QtCore.SIGNAL(u'video_background_replaced'),
|
||||||
self.videobackgroundReplaced)
|
self.videobackgroundReplaced)
|
||||||
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
|
QtCore.SIGNAL(u'mediaitem_media_rebuild'), self.rebuild)
|
||||||
# Allow DnD from the desktop
|
# Allow DnD from the desktop
|
||||||
self.listView.activateDnD()
|
self.listView.activateDnD()
|
||||||
|
|
||||||
@ -83,10 +90,19 @@ class MediaMediaItem(MediaManagerItem):
|
|||||||
self.replaceAction.setToolTip(UiStrings().ReplaceLiveBG)
|
self.replaceAction.setToolTip(UiStrings().ReplaceLiveBG)
|
||||||
self.resetAction.setText(UiStrings().ResetBG)
|
self.resetAction.setText(UiStrings().ResetBG)
|
||||||
self.resetAction.setToolTip(UiStrings().ResetLiveBG)
|
self.resetAction.setToolTip(UiStrings().ResetLiveBG)
|
||||||
|
self.Automatic = translate('MediaPlugin.MediaItem',
|
||||||
|
'Automatic')
|
||||||
|
self.displayTypeLabel.setText(
|
||||||
|
translate('MediaPlugin.MediaItem', 'Use Api:'))
|
||||||
|
|
||||||
def requiredIcons(self):
|
def requiredIcons(self):
|
||||||
MediaManagerItem.requiredIcons(self)
|
MediaManagerItem.requiredIcons(self)
|
||||||
self.hasFileIcon = True
|
self.hasFileIcon = True
|
||||||
|
apiSettings = str(QtCore.QSettings().value(u'media/apis',
|
||||||
|
QtCore.QVariant(u'Webkit')).toString())
|
||||||
|
usedAPIs = apiSettings.split(u',')
|
||||||
|
for title in usedAPIs:
|
||||||
|
api = self.plugin.mediaController.APIs[title]
|
||||||
self.hasNewIcon = False
|
self.hasNewIcon = False
|
||||||
self.hasEditIcon = False
|
self.hasEditIcon = False
|
||||||
|
|
||||||
@ -94,6 +110,11 @@ class MediaMediaItem(MediaManagerItem):
|
|||||||
MediaManagerItem.addListViewToToolBar(self)
|
MediaManagerItem.addListViewToToolBar(self)
|
||||||
self.listView.addAction(self.replaceAction)
|
self.listView.addAction(self.replaceAction)
|
||||||
|
|
||||||
|
# TODO activate own media open menu
|
||||||
|
# def addStartHeaderBar(self):
|
||||||
|
# self.replaceAction = self.addToolbarButton(u'', u'',
|
||||||
|
# u':/general/general_open.png', self.onMediaOpenClick, False)
|
||||||
|
|
||||||
def addEndHeaderBar(self):
|
def addEndHeaderBar(self):
|
||||||
# Replace backgrounds do not work at present so remove functionality.
|
# Replace backgrounds do not work at present so remove functionality.
|
||||||
self.replaceAction = self.addToolbarButton(u'', u'',
|
self.replaceAction = self.addToolbarButton(u'', u'',
|
||||||
@ -101,6 +122,36 @@ class MediaMediaItem(MediaManagerItem):
|
|||||||
self.resetAction = self.addToolbarButton(u'', u'',
|
self.resetAction = self.addToolbarButton(u'', u'',
|
||||||
u':/system/system_close.png', self.onResetClick, False)
|
u':/system/system_close.png', self.onResetClick, False)
|
||||||
self.resetAction.setVisible(False)
|
self.resetAction.setVisible(False)
|
||||||
|
self.mediaWidget = QtGui.QWidget(self)
|
||||||
|
self.mediaWidget.setObjectName(u'mediaWidget')
|
||||||
|
self.displayLayout = QtGui.QFormLayout(self.mediaWidget)
|
||||||
|
self.displayLayout.setMargin(self.displayLayout.spacing())
|
||||||
|
self.displayLayout.setObjectName(u'displayLayout')
|
||||||
|
self.displayTypeLabel = QtGui.QLabel(self.mediaWidget)
|
||||||
|
self.displayTypeLabel.setObjectName(u'displayTypeLabel')
|
||||||
|
self.displayTypeComboBox = media_item_combo_box(
|
||||||
|
self.mediaWidget, u'displayTypeComboBox')
|
||||||
|
self.displayTypeLabel.setBuddy(self.displayTypeComboBox)
|
||||||
|
self.displayLayout.addRow(self.displayTypeLabel,
|
||||||
|
self.displayTypeComboBox)
|
||||||
|
# Add the Media widget to the page layout
|
||||||
|
self.pageLayout.addWidget(self.mediaWidget)
|
||||||
|
QtCore.QObject.connect(self.displayTypeComboBox,
|
||||||
|
QtCore.SIGNAL(u'currentIndexChanged (int)'), self.overrideApiChanged)
|
||||||
|
|
||||||
|
def onMediaOpenClick(self):
|
||||||
|
"""
|
||||||
|
Add a folder to the list widget to make it available for showing
|
||||||
|
"""
|
||||||
|
self.mediaOpenForm.exec_()
|
||||||
|
# folder = QtGui.QFileDialog.getExistingDirectory (
|
||||||
|
# self, self.onNewPrompt,
|
||||||
|
# SettingsManager.get_last_dir(self.settingsSection))
|
||||||
|
# log.info(u'New folder(s) %s', unicode(folder))
|
||||||
|
|
||||||
|
def overrideApiChanged(self, index):
|
||||||
|
Receiver.send_message(u'media_overrideApi', \
|
||||||
|
u'%s' % self.displayTypeComboBox.currentText())
|
||||||
|
|
||||||
def onResetClick(self):
|
def onResetClick(self):
|
||||||
"""
|
"""
|
||||||
@ -139,18 +190,20 @@ class MediaMediaItem(MediaManagerItem):
|
|||||||
'There was a problem replacing your background, '
|
'There was a problem replacing your background, '
|
||||||
'the media file "%s" no longer exists.')) % filename)
|
'the media file "%s" no longer exists.')) % filename)
|
||||||
|
|
||||||
def generateSlideData(self, service_item, item=None, xmlVersion=False):
|
def generateSlideData(self, service_item, item=None, xmlVersion=False,
|
||||||
|
remote=False):
|
||||||
if item is None:
|
if item is None:
|
||||||
item = self.listView.currentItem()
|
item = self.listView.currentItem()
|
||||||
if item is None:
|
if item is None:
|
||||||
return False
|
return False
|
||||||
filename = unicode(item.data(QtCore.Qt.UserRole).toString())
|
filename = unicode(item.data(QtCore.Qt.UserRole).toString())
|
||||||
if not os.path.exists(filename):
|
if not os.path.exists(filename):
|
||||||
# File is no longer present
|
if not remote:
|
||||||
critical_error_message_box(
|
# File is no longer present
|
||||||
translate('MediaPlugin.MediaItem', 'Missing Media File'),
|
critical_error_message_box(
|
||||||
unicode(translate('MediaPlugin.MediaItem',
|
translate('MediaPlugin.MediaItem', 'Missing Media File'),
|
||||||
'The file %s no longer exists.')) % filename)
|
unicode(translate('MediaPlugin.MediaItem',
|
||||||
|
'The file %s no longer exists.')) % filename)
|
||||||
return False
|
return False
|
||||||
self.mediaLength = 0
|
self.mediaLength = 0
|
||||||
if self.plugin.mediaController.video( \
|
if self.plugin.mediaController.video( \
|
||||||
@ -160,7 +213,7 @@ class MediaMediaItem(MediaManagerItem):
|
|||||||
self.plugin.mediaController.video_reset(self.mediaController)
|
self.plugin.mediaController.video_reset(self.mediaController)
|
||||||
if self.mediaLength > 0:
|
if self.mediaLength > 0:
|
||||||
service_item.add_capability(
|
service_item.add_capability(
|
||||||
ItemCapabilities.AllowsVariableStartTime)
|
ItemCapabilities.HasVariableStartTime)
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
service_item.media_length = self.mediaLength
|
service_item.media_length = self.mediaLength
|
||||||
@ -177,6 +230,36 @@ class MediaMediaItem(MediaManagerItem):
|
|||||||
self.listView.clear()
|
self.listView.clear()
|
||||||
self.listView.setIconSize(QtCore.QSize(88, 50))
|
self.listView.setIconSize(QtCore.QSize(88, 50))
|
||||||
self.loadList(SettingsManager.load_list(self.settingsSection, u'media'))
|
self.loadList(SettingsManager.load_list(self.settingsSection, u'media'))
|
||||||
|
self.populateDisplayTypes()
|
||||||
|
|
||||||
|
def rebuild(self):
|
||||||
|
"""
|
||||||
|
Rebuild the tab in the media manager when changes are made in
|
||||||
|
the settings
|
||||||
|
"""
|
||||||
|
self.populateDisplayTypes()
|
||||||
|
|
||||||
|
def populateDisplayTypes(self):
|
||||||
|
"""
|
||||||
|
Load the combobox with the enabled media apis,
|
||||||
|
allowing user to select a specific api if settings allow
|
||||||
|
"""
|
||||||
|
self.displayTypeComboBox.clear()
|
||||||
|
apiSettings = str(QtCore.QSettings().value(u'media/apis',
|
||||||
|
QtCore.QVariant(u'Webkit')).toString())
|
||||||
|
usedAPIs = apiSettings.split(u',')
|
||||||
|
for title in usedAPIs:
|
||||||
|
# load the drop down selection
|
||||||
|
self.displayTypeComboBox.addItem(title)
|
||||||
|
if self.displayTypeComboBox.count() > 1:
|
||||||
|
self.displayTypeComboBox.insertItem(0, self.Automatic)
|
||||||
|
self.displayTypeComboBox.setCurrentIndex(0)
|
||||||
|
if QtCore.QSettings().value(self.settingsSection + u'/override api',
|
||||||
|
QtCore.QVariant(QtCore.Qt.Unchecked)) == QtCore.Qt.Checked:
|
||||||
|
self.mediaWidget.show()
|
||||||
|
else:
|
||||||
|
self.mediaWidget.hide()
|
||||||
|
|
||||||
|
|
||||||
def onDeleteClick(self):
|
def onDeleteClick(self):
|
||||||
"""
|
"""
|
||||||
@ -197,13 +280,36 @@ class MediaMediaItem(MediaManagerItem):
|
|||||||
media.sort(cmp=locale.strcoll,
|
media.sort(cmp=locale.strcoll,
|
||||||
key=lambda filename: os.path.split(unicode(filename))[1].lower())
|
key=lambda filename: os.path.split(unicode(filename))[1].lower())
|
||||||
for track in media:
|
for track in media:
|
||||||
filename = os.path.split(unicode(track))[1]
|
track_info = QtCore.QFileInfo(track)
|
||||||
item_name = QtGui.QListWidgetItem(filename)
|
if not track_info.isFile():
|
||||||
item_name.setIcon(build_icon(CLAPPERBOARD))
|
filename = os.path.split(unicode(track))[1]
|
||||||
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(track))
|
item_name = QtGui.QListWidgetItem(filename)
|
||||||
|
item_name.setIcon(build_icon(CLAPPERBOARD))
|
||||||
|
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(track))
|
||||||
|
else:
|
||||||
|
filename = os.path.split(unicode(track))[1]
|
||||||
|
item_name = QtGui.QListWidgetItem(filename)
|
||||||
|
#imageFile = u'F:/Computer/Platform/pythonDev/openlp/branches/media/media-optical-dvd-video.png'
|
||||||
|
#thumbFile = u'F:/Computer/Platform/pythonDev/openlp/branches/media/media-optical-dvd-video_thumb.png'
|
||||||
|
#icon = self.iconFromFile(imageFile, thumbFile)
|
||||||
|
#item_name.setIcon(icon)
|
||||||
|
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(track))
|
||||||
item_name.setToolTip(track)
|
item_name.setToolTip(track)
|
||||||
self.listView.addItem(item_name)
|
self.listView.addItem(item_name)
|
||||||
|
|
||||||
|
def getList(self, type=MediaType.Audio):
|
||||||
|
media = SettingsManager.load_list(self.settingsSection, u'media')
|
||||||
|
media.sort(cmp=locale.strcoll,
|
||||||
|
key=lambda filename: os.path.split(unicode(filename))[1].lower())
|
||||||
|
ext = []
|
||||||
|
if type == MediaType.Audio:
|
||||||
|
ext = self.plugin.audio_extensions_list
|
||||||
|
else:
|
||||||
|
ext = self.plugin.video_extensions_list
|
||||||
|
ext = map(lambda x: x[1:], ext)
|
||||||
|
media = filter(lambda x: os.path.splitext(x)[1] in ext, media)
|
||||||
|
return media
|
||||||
|
|
||||||
def search(self, string):
|
def search(self, string):
|
||||||
files = SettingsManager.load_list(self.settingsSection, u'media')
|
files = SettingsManager.load_list(self.settingsSection, u'media')
|
||||||
results = []
|
results = []
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
from openlp.core.lib import SettingsTab, translate, Receiver
|
from openlp.core.lib import SettingsTab, translate, Receiver
|
||||||
|
from openlp.core.lib.ui import UiStrings
|
||||||
|
|
||||||
class MediaTab(SettingsTab):
|
class MediaTab(SettingsTab):
|
||||||
"""
|
"""
|
||||||
@ -87,6 +88,14 @@ class MediaTab(SettingsTab):
|
|||||||
self.orderingButtonLayout.addWidget(self.orderingUpButton)
|
self.orderingButtonLayout.addWidget(self.orderingUpButton)
|
||||||
self.apiOrderLayout.addWidget(self.orderingButtonsWidget)
|
self.apiOrderLayout.addWidget(self.orderingButtonsWidget)
|
||||||
self.leftLayout.addWidget(self.apiOrderGroupBox)
|
self.leftLayout.addWidget(self.apiOrderGroupBox)
|
||||||
|
self.AdvancedGroupBox = QtGui.QGroupBox(self.leftColumn)
|
||||||
|
self.AdvancedGroupBox.setObjectName(u'AdvancedGroupBox')
|
||||||
|
self.AdvancedLayout = QtGui.QVBoxLayout(self.AdvancedGroupBox)
|
||||||
|
self.AdvancedLayout.setObjectName(u'AdvancedLayout')
|
||||||
|
self.OverrideApiCheckBox = QtGui.QCheckBox(self.AdvancedGroupBox)
|
||||||
|
self.OverrideApiCheckBox.setObjectName(u'OverrideApiCheckBox')
|
||||||
|
self.AdvancedLayout.addWidget(self.OverrideApiCheckBox)
|
||||||
|
self.leftLayout.addWidget(self.AdvancedGroupBox)
|
||||||
self.leftLayout.addStretch()
|
self.leftLayout.addStretch()
|
||||||
self.rightLayout.addStretch()
|
self.rightLayout.addStretch()
|
||||||
for key in self.apis:
|
for key in self.apis:
|
||||||
@ -102,7 +111,7 @@ class MediaTab(SettingsTab):
|
|||||||
|
|
||||||
def retranslateUi(self):
|
def retranslateUi(self):
|
||||||
self.mediaAPIGroupBox.setTitle(
|
self.mediaAPIGroupBox.setTitle(
|
||||||
translate('MediaPlugin.MediaTab', 'Media APIs'))
|
translate('MediaPlugin.MediaTab', 'Available Media APIs'))
|
||||||
for key in self.apis:
|
for key in self.apis:
|
||||||
api = self.apis[key]
|
api = self.apis[key]
|
||||||
checkbox = self.ApiCheckBoxes[api.name]
|
checkbox = self.ApiCheckBoxes[api.name]
|
||||||
@ -118,6 +127,10 @@ class MediaTab(SettingsTab):
|
|||||||
translate('MediaPlugin.MediaTab', 'Down'))
|
translate('MediaPlugin.MediaTab', 'Down'))
|
||||||
self.orderingUpButton.setText(
|
self.orderingUpButton.setText(
|
||||||
translate('MediaPlugin.MediaTab', 'Up'))
|
translate('MediaPlugin.MediaTab', 'Up'))
|
||||||
|
self.AdvancedGroupBox.setTitle(UiStrings().Advanced)
|
||||||
|
self.OverrideApiCheckBox.setText(
|
||||||
|
translate('MediaPlugin.MediaTab',
|
||||||
|
'Allow media api to be overriden'))
|
||||||
|
|
||||||
def onApiCheckBoxChanged(self, check_state):
|
def onApiCheckBoxChanged(self, check_state):
|
||||||
api = self.sender().text()
|
api = self.sender().text()
|
||||||
@ -160,8 +173,13 @@ class MediaTab(SettingsTab):
|
|||||||
if api.available and api.name in self.usedAPIs:
|
if api.available and api.name in self.usedAPIs:
|
||||||
checkbox.setChecked(True)
|
checkbox.setChecked(True)
|
||||||
self.updateApiList()
|
self.updateApiList()
|
||||||
|
self.OverrideApiCheckBox.setChecked(QtCore.QSettings().value(
|
||||||
|
self.settingsSection + u'/override api',
|
||||||
|
QtCore.QVariant(QtCore.Qt.Unchecked)).toInt()[0])
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
|
override_changed = False
|
||||||
|
api_string_changed = False
|
||||||
oldApiString = QtCore.QSettings().value(
|
oldApiString = QtCore.QSettings().value(
|
||||||
self.settingsSection + u'/apis',
|
self.settingsSection + u'/apis',
|
||||||
QtCore.QVariant(u'Webkit')).toString()
|
QtCore.QVariant(u'Webkit')).toString()
|
||||||
@ -170,4 +188,16 @@ class MediaTab(SettingsTab):
|
|||||||
# clean old Media stuff
|
# clean old Media stuff
|
||||||
QtCore.QSettings().setValue(self.settingsSection + u'/apis',
|
QtCore.QSettings().setValue(self.settingsSection + u'/apis',
|
||||||
QtCore.QVariant(newApiString))
|
QtCore.QVariant(newApiString))
|
||||||
|
api_string_changed = True
|
||||||
|
override_changed = True
|
||||||
|
setting_key = self.settingsSection + u'/override api'
|
||||||
|
if QtCore.QSettings().value(setting_key) != \
|
||||||
|
self.OverrideApiCheckBox.checkState():
|
||||||
|
QtCore.QSettings().setValue(setting_key,
|
||||||
|
QtCore.QVariant(self.OverrideApiCheckBox.checkState()))
|
||||||
|
override_changed = True
|
||||||
|
if override_changed:
|
||||||
|
Receiver.send_message(u'mediaitem_media_rebuild')
|
||||||
|
if api_string_changed:
|
||||||
Receiver.send_message(u'config_screen_changed')
|
Receiver.send_message(u'config_screen_changed')
|
||||||
|
|
||||||
|
@ -56,6 +56,7 @@ class PresentationMediaItem(MediaManagerItem):
|
|||||||
MediaManagerItem.__init__(self, parent, plugin, icon)
|
MediaManagerItem.__init__(self, parent, plugin, icon)
|
||||||
self.message_listener = MessageListener(self)
|
self.message_listener = MessageListener(self)
|
||||||
self.hasSearch = True
|
self.hasSearch = True
|
||||||
|
self.singleServiceItem = False
|
||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
QtCore.SIGNAL(u'mediaitem_presentation_rebuild'), self.rebuild)
|
QtCore.SIGNAL(u'mediaitem_presentation_rebuild'), self.rebuild)
|
||||||
# Allow DnD from the desktop
|
# Allow DnD from the desktop
|
||||||
@ -233,7 +234,8 @@ class PresentationMediaItem(MediaManagerItem):
|
|||||||
SettingsManager.set_list(self.settingsSection,
|
SettingsManager.set_list(self.settingsSection,
|
||||||
u'presentations', self.getFileList())
|
u'presentations', self.getFileList())
|
||||||
|
|
||||||
def generateSlideData(self, service_item, item=None, xmlVersion=False):
|
def generateSlideData(self, service_item, item=None, xmlVersion=False,
|
||||||
|
remote=False):
|
||||||
"""
|
"""
|
||||||
Load the relevant information for displaying the presentation
|
Load the relevant information for displaying the presentation
|
||||||
in the slidecontroller. In the case of powerpoints, an image
|
in the slidecontroller. In the case of powerpoints, an image
|
||||||
@ -248,7 +250,7 @@ class PresentationMediaItem(MediaManagerItem):
|
|||||||
service_item.title = unicode(self.displayTypeComboBox.currentText())
|
service_item.title = unicode(self.displayTypeComboBox.currentText())
|
||||||
service_item.shortname = unicode(self.displayTypeComboBox.currentText())
|
service_item.shortname = unicode(self.displayTypeComboBox.currentText())
|
||||||
service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay)
|
service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay)
|
||||||
service_item.add_capability(ItemCapabilities.AllowsDetailedTitleDisplay)
|
service_item.add_capability(ItemCapabilities.HasDetailedTitleDisplay)
|
||||||
shortname = service_item.shortname
|
shortname = service_item.shortname
|
||||||
if shortname:
|
if shortname:
|
||||||
for bitem in items:
|
for bitem in items:
|
||||||
@ -275,12 +277,13 @@ class PresentationMediaItem(MediaManagerItem):
|
|||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
# File is no longer present
|
# File is no longer present
|
||||||
critical_error_message_box(
|
if not remote:
|
||||||
translate('PresentationPlugin.MediaItem',
|
critical_error_message_box(
|
||||||
'Missing Presentation'),
|
translate('PresentationPlugin.MediaItem',
|
||||||
unicode(translate('PresentationPlugin.MediaItem',
|
'Missing Presentation'),
|
||||||
'The Presentation %s is incomplete,'
|
unicode(translate('PresentationPlugin.MediaItem',
|
||||||
' please reload.')) % filename)
|
'The Presentation %s is incomplete,'
|
||||||
|
' please reload.')) % filename)
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
# File is no longer present
|
# File is no longer present
|
||||||
|
@ -528,7 +528,7 @@ class HttpConnection(object):
|
|||||||
id = json.loads(self.url_params[u'data'][0])[u'request'][u'id']
|
id = json.loads(self.url_params[u'data'][0])[u'request'][u'id']
|
||||||
plugin = self.parent.plugin.pluginManager.get_plugin_by_name(type)
|
plugin = self.parent.plugin.pluginManager.get_plugin_by_name(type)
|
||||||
if plugin.status == PluginStatus.Active and plugin.mediaItem:
|
if plugin.status == PluginStatus.Active and plugin.mediaItem:
|
||||||
plugin.mediaItem.goLive(id)
|
plugin.mediaItem.goLive(id, remote=True)
|
||||||
|
|
||||||
def add_to_service(self, type):
|
def add_to_service(self, type):
|
||||||
"""
|
"""
|
||||||
@ -538,7 +538,7 @@ class HttpConnection(object):
|
|||||||
plugin = self.parent.plugin.pluginManager.get_plugin_by_name(type)
|
plugin = self.parent.plugin.pluginManager.get_plugin_by_name(type)
|
||||||
if plugin.status == PluginStatus.Active and plugin.mediaItem:
|
if plugin.status == PluginStatus.Active and plugin.mediaItem:
|
||||||
item_id = plugin.mediaItem.createItemFromId(id)
|
item_id = plugin.mediaItem.createItemFromId(id)
|
||||||
plugin.mediaItem.addToService(item_id)
|
plugin.mediaItem.addToService(item_id, remote=True)
|
||||||
|
|
||||||
def send_response(self, response):
|
def send_response(self, response):
|
||||||
http = u'HTTP/1.1 %s\r\n' % response.code
|
http = u'HTTP/1.1 %s\r\n' % response.code
|
||||||
|
@ -52,6 +52,7 @@ them separate from the functionality, so that it is easier to recreate the GUI
|
|||||||
from the .ui files later if necessary.
|
from the .ui files later if necessary.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from mediafilesform import MediaFilesForm
|
||||||
from authorsform import AuthorsForm
|
from authorsform import AuthorsForm
|
||||||
from topicsform import TopicsForm
|
from topicsform import TopicsForm
|
||||||
from songbookform import SongBookForm
|
from songbookform import SongBookForm
|
||||||
|
@ -28,7 +28,8 @@
|
|||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
from openlp.core.lib import build_icon, translate
|
from openlp.core.lib import build_icon, translate
|
||||||
from openlp.core.lib.ui import UiStrings, create_accept_reject_button_box
|
from openlp.core.lib.ui import UiStrings, create_accept_reject_button_box, \
|
||||||
|
create_up_down_push_button_set
|
||||||
from openlp.plugins.songs.lib.ui import SongStrings
|
from openlp.plugins.songs.lib.ui import SongStrings
|
||||||
|
|
||||||
class Ui_EditSongDialog(object):
|
class Ui_EditSongDialog(object):
|
||||||
@ -36,9 +37,11 @@ class Ui_EditSongDialog(object):
|
|||||||
editSongDialog.setObjectName(u'editSongDialog')
|
editSongDialog.setObjectName(u'editSongDialog')
|
||||||
editSongDialog.resize(650, 400)
|
editSongDialog.resize(650, 400)
|
||||||
editSongDialog.setWindowIcon(
|
editSongDialog.setWindowIcon(
|
||||||
build_icon(u':/icon/openlp.org-icon-32.bmp'))
|
build_icon(u':/icon/openlp-logo-16x16.png'))
|
||||||
editSongDialog.setModal(True)
|
editSongDialog.setModal(True)
|
||||||
self.dialogLayout = QtGui.QVBoxLayout(editSongDialog)
|
self.dialogLayout = QtGui.QVBoxLayout(editSongDialog)
|
||||||
|
self.dialogLayout.setSpacing(8)
|
||||||
|
self.dialogLayout.setContentsMargins(8, 8, 8, 8)
|
||||||
self.dialogLayout.setObjectName(u'dialogLayout')
|
self.dialogLayout.setObjectName(u'dialogLayout')
|
||||||
self.songTabWidget = QtGui.QTabWidget(editSongDialog)
|
self.songTabWidget = QtGui.QTabWidget(editSongDialog)
|
||||||
self.songTabWidget.setObjectName(u'songTabWidget')
|
self.songTabWidget.setObjectName(u'songTabWidget')
|
||||||
@ -246,6 +249,36 @@ class Ui_EditSongDialog(object):
|
|||||||
self.commentsLayout.addWidget(self.commentsEdit)
|
self.commentsLayout.addWidget(self.commentsEdit)
|
||||||
self.themeTabLayout.addWidget(self.commentsGroupBox)
|
self.themeTabLayout.addWidget(self.commentsGroupBox)
|
||||||
self.songTabWidget.addTab(self.themeTab, u'')
|
self.songTabWidget.addTab(self.themeTab, u'')
|
||||||
|
# audio tab
|
||||||
|
self.audioTab = QtGui.QWidget()
|
||||||
|
self.audioTab.setObjectName(u'audioTab')
|
||||||
|
self.audioLayout = QtGui.QHBoxLayout(self.audioTab)
|
||||||
|
self.audioLayout.setObjectName(u'audioLayout')
|
||||||
|
self.audioListWidget = QtGui.QListWidget(self.audioTab)
|
||||||
|
self.audioListWidget.setObjectName(u'audioListWidget')
|
||||||
|
self.audioLayout.addWidget(self.audioListWidget)
|
||||||
|
self.audioButtonsLayout = QtGui.QVBoxLayout()
|
||||||
|
self.audioButtonsLayout.setObjectName(u'audioButtonsLayout')
|
||||||
|
self.audioAddFromFileButton = QtGui.QPushButton(self.audioTab)
|
||||||
|
self.audioAddFromFileButton.setObjectName(u'audioAddFromFileButton')
|
||||||
|
self.audioButtonsLayout.addWidget(self.audioAddFromFileButton)
|
||||||
|
self.audioAddFromMediaButton = QtGui.QPushButton(self.audioTab)
|
||||||
|
self.audioAddFromMediaButton.setObjectName(u'audioAddFromMediaButton')
|
||||||
|
self.audioButtonsLayout.addWidget(self.audioAddFromMediaButton)
|
||||||
|
self.audioRemoveButton = QtGui.QPushButton(self.audioTab)
|
||||||
|
self.audioRemoveButton.setObjectName(u'audioRemoveButton')
|
||||||
|
self.audioButtonsLayout.addWidget(self.audioRemoveButton)
|
||||||
|
self.audioRemoveAllButton = QtGui.QPushButton(self.audioTab)
|
||||||
|
self.audioRemoveAllButton.setObjectName(u'audioRemoveAllButton')
|
||||||
|
self.audioButtonsLayout.addWidget(self.audioRemoveAllButton)
|
||||||
|
self.audioButtonsLayout.addStretch(1)
|
||||||
|
self.upButton, self.downButton = \
|
||||||
|
create_up_down_push_button_set(self)
|
||||||
|
self.audioButtonsLayout.addWidget(self.upButton)
|
||||||
|
self.audioButtonsLayout.addWidget(self.downButton)
|
||||||
|
self.audioLayout.addLayout(self.audioButtonsLayout)
|
||||||
|
self.songTabWidget.addTab(self.audioTab, u'')
|
||||||
|
# Last few bits
|
||||||
self.dialogLayout.addWidget(self.songTabWidget)
|
self.dialogLayout.addWidget(self.songTabWidget)
|
||||||
self.buttonBox = create_accept_reject_button_box(editSongDialog)
|
self.buttonBox = create_accept_reject_button_box(editSongDialog)
|
||||||
self.dialogLayout.addWidget(self.buttonBox)
|
self.dialogLayout.addWidget(self.buttonBox)
|
||||||
@ -305,6 +338,17 @@ class Ui_EditSongDialog(object):
|
|||||||
self.songTabWidget.indexOf(self.themeTab),
|
self.songTabWidget.indexOf(self.themeTab),
|
||||||
translate('SongsPlugin.EditSongForm',
|
translate('SongsPlugin.EditSongForm',
|
||||||
'Theme, Copyright Info && Comments'))
|
'Theme, Copyright Info && Comments'))
|
||||||
|
self.songTabWidget.setTabText(
|
||||||
|
self.songTabWidget.indexOf(self.audioTab),
|
||||||
|
translate('SongsPlugin.EditSongForm', 'Linked Audio'))
|
||||||
|
self.audioAddFromFileButton.setText(
|
||||||
|
translate('SongsPlugin.EditSongForm', 'Add &File(s)'))
|
||||||
|
self.audioAddFromMediaButton.setText(
|
||||||
|
translate('SongsPlugin.EditSongForm', 'Add &Media'))
|
||||||
|
self.audioRemoveButton.setText(
|
||||||
|
translate('SongsPlugin.EditSongForm', '&Remove'))
|
||||||
|
self.audioRemoveAllButton.setText(
|
||||||
|
translate('SongsPlugin.EditSongForm', 'Remove &All'))
|
||||||
|
|
||||||
def editSongDialogComboBox(parent, name):
|
def editSongDialogComboBox(parent, name):
|
||||||
"""
|
"""
|
||||||
|
@ -27,15 +27,18 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
from openlp.core.lib import Receiver, translate
|
from openlp.core.lib import PluginStatus, Receiver, MediaType, translate
|
||||||
from openlp.core.lib.ui import UiStrings, add_widget_completer, \
|
from openlp.core.lib.ui import UiStrings, add_widget_completer, \
|
||||||
critical_error_message_box, find_and_set_in_combo_box
|
critical_error_message_box, find_and_set_in_combo_box
|
||||||
from openlp.plugins.songs.forms import EditVerseForm
|
from openlp.core.utils import AppLocation
|
||||||
|
from openlp.plugins.songs.forms import EditVerseForm, MediaFilesForm
|
||||||
from openlp.plugins.songs.lib import SongXML, VerseType, clean_song
|
from openlp.plugins.songs.lib import SongXML, VerseType, clean_song
|
||||||
from openlp.plugins.songs.lib.db import Book, Song, Author, Topic
|
from openlp.plugins.songs.lib.db import Book, Song, Author, Topic, MediaFile
|
||||||
from openlp.plugins.songs.lib.ui import SongStrings
|
from openlp.plugins.songs.lib.ui import SongStrings
|
||||||
from editsongdialog import Ui_EditSongDialog
|
from editsongdialog import Ui_EditSongDialog
|
||||||
|
|
||||||
@ -93,6 +96,14 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
self.mediaitem.plugin.renderer.themeManager.onAddTheme)
|
self.mediaitem.plugin.renderer.themeManager.onAddTheme)
|
||||||
QtCore.QObject.connect(self.maintenanceButton,
|
QtCore.QObject.connect(self.maintenanceButton,
|
||||||
QtCore.SIGNAL(u'clicked()'), self.onMaintenanceButtonClicked)
|
QtCore.SIGNAL(u'clicked()'), self.onMaintenanceButtonClicked)
|
||||||
|
QtCore.QObject.connect(self.audioAddFromFileButton,
|
||||||
|
QtCore.SIGNAL(u'clicked()'), self.onAudioAddFromFileButtonClicked)
|
||||||
|
QtCore.QObject.connect(self.audioAddFromMediaButton,
|
||||||
|
QtCore.SIGNAL(u'clicked()'), self.onAudioAddFromMediaButtonClicked)
|
||||||
|
QtCore.QObject.connect(self.audioRemoveButton,
|
||||||
|
QtCore.SIGNAL(u'clicked()'), self.onAudioRemoveButtonClicked)
|
||||||
|
QtCore.QObject.connect(self.audioRemoveAllButton,
|
||||||
|
QtCore.SIGNAL(u'clicked()'), self.onAudioRemoveAllButtonClicked)
|
||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
QtCore.SIGNAL(u'theme_update_list'), self.loadThemes)
|
QtCore.SIGNAL(u'theme_update_list'), self.loadThemes)
|
||||||
self.previewButton = QtGui.QPushButton()
|
self.previewButton = QtGui.QPushButton()
|
||||||
@ -104,12 +115,14 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
QtCore.SIGNAL(u'clicked(QAbstractButton*)'), self.onPreview)
|
QtCore.SIGNAL(u'clicked(QAbstractButton*)'), self.onPreview)
|
||||||
# Create other objects and forms
|
# Create other objects and forms
|
||||||
self.manager = manager
|
self.manager = manager
|
||||||
self.verse_form = EditVerseForm(self)
|
self.verseForm = EditVerseForm(self)
|
||||||
|
self.mediaForm = MediaFilesForm(self)
|
||||||
self.initialise()
|
self.initialise()
|
||||||
self.authorsListView.setSortingEnabled(False)
|
self.authorsListView.setSortingEnabled(False)
|
||||||
self.authorsListView.setAlternatingRowColors(True)
|
self.authorsListView.setAlternatingRowColors(True)
|
||||||
self.topicsListView.setSortingEnabled(False)
|
self.topicsListView.setSortingEnabled(False)
|
||||||
self.topicsListView.setAlternatingRowColors(True)
|
self.topicsListView.setAlternatingRowColors(True)
|
||||||
|
self.audioListWidget.setAlternatingRowColors(True)
|
||||||
self.findVerseSplit = re.compile(u'---\[\]---\n', re.UNICODE)
|
self.findVerseSplit = re.compile(u'---\[\]---\n', re.UNICODE)
|
||||||
self.whitespace = re.compile(r'\W+', re.UNICODE)
|
self.whitespace = re.compile(r'\W+', re.UNICODE)
|
||||||
|
|
||||||
@ -161,6 +174,16 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
self.themes.append(theme)
|
self.themes.append(theme)
|
||||||
add_widget_completer(self.themes, self.themeComboBox)
|
add_widget_completer(self.themes, self.themeComboBox)
|
||||||
|
|
||||||
|
def loadMediaFiles(self):
|
||||||
|
self.audioAddFromMediaButton.setVisible(False)
|
||||||
|
for plugin in self.parent().pluginManager.plugins:
|
||||||
|
if plugin.name == u'media' and \
|
||||||
|
plugin.status == PluginStatus.Active:
|
||||||
|
self.audioAddFromMediaButton.setVisible(True)
|
||||||
|
self.mediaForm.populateFiles(
|
||||||
|
plugin.getMediaManagerItem().getList(MediaType.Audio))
|
||||||
|
break
|
||||||
|
|
||||||
def newSong(self):
|
def newSong(self):
|
||||||
log.debug(u'New Song')
|
log.debug(u'New Song')
|
||||||
self.song = None
|
self.song = None
|
||||||
@ -176,11 +199,13 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
self.verseListWidget.setRowCount(0)
|
self.verseListWidget.setRowCount(0)
|
||||||
self.authorsListView.clear()
|
self.authorsListView.clear()
|
||||||
self.topicsListView.clear()
|
self.topicsListView.clear()
|
||||||
|
self.audioListWidget.clear()
|
||||||
self.titleEdit.setFocus(QtCore.Qt.OtherFocusReason)
|
self.titleEdit.setFocus(QtCore.Qt.OtherFocusReason)
|
||||||
self.songBookNumberEdit.setText(u'')
|
self.songBookNumberEdit.setText(u'')
|
||||||
self.loadAuthors()
|
self.loadAuthors()
|
||||||
self.loadTopics()
|
self.loadTopics()
|
||||||
self.loadBooks()
|
self.loadBooks()
|
||||||
|
self.loadMediaFiles()
|
||||||
self.themeComboBox.setCurrentIndex(0)
|
self.themeComboBox.setCurrentIndex(0)
|
||||||
# it's a new song to preview is not possible
|
# it's a new song to preview is not possible
|
||||||
self.previewButton.setVisible(False)
|
self.previewButton.setVisible(False)
|
||||||
@ -201,6 +226,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
self.loadAuthors()
|
self.loadAuthors()
|
||||||
self.loadTopics()
|
self.loadTopics()
|
||||||
self.loadBooks()
|
self.loadBooks()
|
||||||
|
self.loadMediaFiles()
|
||||||
self.song = self.manager.get_object(Song, id)
|
self.song = self.manager.get_object(Song, id)
|
||||||
self.titleEdit.setText(self.song.title)
|
self.titleEdit.setText(self.song.title)
|
||||||
if self.song.alternate_title:
|
if self.song.alternate_title:
|
||||||
@ -303,6 +329,11 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
topic_name = QtGui.QListWidgetItem(unicode(topic.name))
|
topic_name = QtGui.QListWidgetItem(unicode(topic.name))
|
||||||
topic_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(topic.id))
|
topic_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(topic.id))
|
||||||
self.topicsListView.addItem(topic_name)
|
self.topicsListView.addItem(topic_name)
|
||||||
|
self.audioListWidget.clear()
|
||||||
|
for media in self.song.media_files:
|
||||||
|
media_file = QtGui.QListWidgetItem(os.path.split(media.file_name)[1])
|
||||||
|
media_file.setData(QtCore.Qt.UserRole, QtCore.QVariant(media.file_name))
|
||||||
|
self.audioListWidget.addItem(media_file)
|
||||||
self.titleEdit.setFocus(QtCore.Qt.OtherFocusReason)
|
self.titleEdit.setFocus(QtCore.Qt.OtherFocusReason)
|
||||||
# Hide or show the preview button.
|
# Hide or show the preview button.
|
||||||
self.previewButton.setVisible(preview)
|
self.previewButton.setVisible(preview)
|
||||||
@ -436,9 +467,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
self.verseDeleteButton.setEnabled(True)
|
self.verseDeleteButton.setEnabled(True)
|
||||||
|
|
||||||
def onVerseAddButtonClicked(self):
|
def onVerseAddButtonClicked(self):
|
||||||
self.verse_form.setVerse(u'', True)
|
self.verseForm.setVerse(u'', True)
|
||||||
if self.verse_form.exec_():
|
if self.verseForm.exec_():
|
||||||
after_text, verse_tag, verse_num = self.verse_form.getVerse()
|
after_text, verse_tag, verse_num = self.verseForm.getVerse()
|
||||||
verse_def = u'%s%s' % (verse_tag, verse_num)
|
verse_def = u'%s%s' % (verse_tag, verse_num)
|
||||||
item = QtGui.QTableWidgetItem(after_text)
|
item = QtGui.QTableWidgetItem(after_text)
|
||||||
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(verse_def))
|
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(verse_def))
|
||||||
@ -454,20 +485,21 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
if item:
|
if item:
|
||||||
tempText = item.text()
|
tempText = item.text()
|
||||||
verseId = unicode(item.data(QtCore.Qt.UserRole).toString())
|
verseId = unicode(item.data(QtCore.Qt.UserRole).toString())
|
||||||
self.verse_form.setVerse(tempText, True, verseId)
|
self.verseForm.setVerse(tempText, True, verseId)
|
||||||
if self.verse_form.exec_():
|
if self.verseForm.exec_():
|
||||||
after_text, verse_tag, verse_num = self.verse_form.getVerse()
|
after_text, verse_tag, verse_num = self.verseForm.getVerse()
|
||||||
verse_def = u'%s%s' % (verse_tag, verse_num)
|
verse_def = u'%s%s' % (verse_tag, verse_num)
|
||||||
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(verse_def))
|
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(verse_def))
|
||||||
item.setText(after_text)
|
item.setText(after_text)
|
||||||
# number of lines has change so repaint the list moving the data
|
# number of lines has changed, repaint the list moving the data
|
||||||
if len(tempText.split(u'\n')) != len(after_text.split(u'\n')):
|
if len(tempText.split(u'\n')) != len(after_text.split(u'\n')):
|
||||||
tempList = {}
|
tempList = {}
|
||||||
tempId = {}
|
tempId = {}
|
||||||
for row in range(0, self.verseListWidget.rowCount()):
|
for row in range(0, self.verseListWidget.rowCount()):
|
||||||
tempList[row] = self.verseListWidget.item(row, 0).text()
|
tempList[row] = self.verseListWidget.item(row, 0)\
|
||||||
tempId[row] = self.verseListWidget.item(row, 0).\
|
.text()
|
||||||
data(QtCore.Qt.UserRole)
|
tempId[row] = self.verseListWidget.item(row, 0)\
|
||||||
|
.data(QtCore.Qt.UserRole)
|
||||||
self.verseListWidget.clear()
|
self.verseListWidget.clear()
|
||||||
for row in range (0, len(tempList)):
|
for row in range (0, len(tempList)):
|
||||||
item = QtGui.QTableWidgetItem(tempList[row], 0)
|
item = QtGui.QTableWidgetItem(tempList[row], 0)
|
||||||
@ -486,12 +518,12 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
verse_list += u'---[%s:%s]---\n' % (verse_tag, verse_num)
|
verse_list += u'---[%s:%s]---\n' % (verse_tag, verse_num)
|
||||||
verse_list += item.text()
|
verse_list += item.text()
|
||||||
verse_list += u'\n'
|
verse_list += u'\n'
|
||||||
self.verse_form.setVerse(verse_list)
|
self.verseForm.setVerse(verse_list)
|
||||||
else:
|
else:
|
||||||
self.verse_form.setVerse(u'')
|
self.verseForm.setVerse(u'')
|
||||||
if not self.verse_form.exec_():
|
if not self.verseForm.exec_():
|
||||||
return
|
return
|
||||||
verse_list = self.verse_form.getVerseAll()
|
verse_list = self.verseForm.getVerseAll()
|
||||||
verse_list = unicode(verse_list.replace(u'\r\n', u'\n'))
|
verse_list = unicode(verse_list.replace(u'\r\n', u'\n'))
|
||||||
self.verseListWidget.clear()
|
self.verseListWidget.clear()
|
||||||
self.verseListWidget.setRowCount(0)
|
self.verseListWidget.setRowCount(0)
|
||||||
@ -670,6 +702,66 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
self.saveSong(True)
|
self.saveSong(True)
|
||||||
Receiver.send_message(u'songs_preview')
|
Receiver.send_message(u'songs_preview')
|
||||||
|
|
||||||
|
def onAudioAddFromFileButtonClicked(self):
|
||||||
|
"""
|
||||||
|
Loads file(s) from the filesystem.
|
||||||
|
"""
|
||||||
|
filters = u'%s (*)' % UiStrings().AllFiles
|
||||||
|
filenames = QtGui.QFileDialog.getOpenFileNames(self,
|
||||||
|
translate('SongsPlugin.EditSongForm', 'Open File(s)'),
|
||||||
|
QtCore.QString(), filters)
|
||||||
|
for filename in filenames:
|
||||||
|
item = QtGui.QListWidgetItem(os.path.split(unicode(filename))[1])
|
||||||
|
item.setData(QtCore.Qt.UserRole, filename)
|
||||||
|
self.audioListWidget.addItem(item)
|
||||||
|
|
||||||
|
def onAudioAddFromMediaButtonClicked(self):
|
||||||
|
"""
|
||||||
|
Loads file(s) from the media plugin.
|
||||||
|
"""
|
||||||
|
if self.mediaForm.exec_():
|
||||||
|
for filename in self.mediaForm.getSelectedFiles():
|
||||||
|
item = QtGui.QListWidgetItem(os.path.split(unicode(filename))[1])
|
||||||
|
item.setData(QtCore.Qt.UserRole, filename)
|
||||||
|
self.audioListWidget.addItem(item)
|
||||||
|
|
||||||
|
def onAudioRemoveButtonClicked(self):
|
||||||
|
"""
|
||||||
|
Removes a file from the list.
|
||||||
|
"""
|
||||||
|
row = self.audioListWidget.currentRow()
|
||||||
|
if row == -1:
|
||||||
|
return
|
||||||
|
self.audioListWidget.takeItem(row)
|
||||||
|
|
||||||
|
def onAudioRemoveAllButtonClicked(self):
|
||||||
|
"""
|
||||||
|
Removes all files from the list.
|
||||||
|
"""
|
||||||
|
self.audioListWidget.clear()
|
||||||
|
|
||||||
|
def onUpButtonClicked(self):
|
||||||
|
"""
|
||||||
|
Moves a file up when the user clicks the up button on the audio tab.
|
||||||
|
"""
|
||||||
|
row = self.audioListWidget.currentRow()
|
||||||
|
if row <= 0:
|
||||||
|
return
|
||||||
|
item = self.audioListWidget.takeItem(row)
|
||||||
|
self.audioListWidget.insertItem(row - 1, item)
|
||||||
|
self.audioListWidget.setCurrentRow(row - 1)
|
||||||
|
|
||||||
|
def onDownButtonClicked(self):
|
||||||
|
"""
|
||||||
|
Moves a file down when the user clicks the up button on the audio tab.
|
||||||
|
"""
|
||||||
|
row = self.audioListWidget.currentRow()
|
||||||
|
if row == -1 or row > self.audioListWidget.count() - 1:
|
||||||
|
return
|
||||||
|
item = self.audioListWidget.takeItem(row)
|
||||||
|
self.audioListWidget.insertItem(row + 1, item)
|
||||||
|
self.audioListWidget.setCurrentRow(row + 1)
|
||||||
|
|
||||||
def clearCaches(self):
|
def clearCaches(self):
|
||||||
"""
|
"""
|
||||||
Free up autocompletion memory on dialog exit
|
Free up autocompletion memory on dialog exit
|
||||||
@ -744,18 +836,55 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
self.song.theme_name = None
|
self.song.theme_name = None
|
||||||
self._processLyrics()
|
self._processLyrics()
|
||||||
self.song.authors = []
|
self.song.authors = []
|
||||||
for row in range(self.authorsListView.count()):
|
for row in xrange(self.authorsListView.count()):
|
||||||
item = self.authorsListView.item(row)
|
item = self.authorsListView.item(row)
|
||||||
authorId = (item.data(QtCore.Qt.UserRole)).toInt()[0]
|
authorId = (item.data(QtCore.Qt.UserRole)).toInt()[0]
|
||||||
self.song.authors.append(self.manager.get_object(Author, authorId))
|
self.song.authors.append(self.manager.get_object(Author, authorId))
|
||||||
self.song.topics = []
|
self.song.topics = []
|
||||||
for row in range(self.topicsListView.count()):
|
for row in xrange(self.topicsListView.count()):
|
||||||
item = self.topicsListView.item(row)
|
item = self.topicsListView.item(row)
|
||||||
topicId = (item.data(QtCore.Qt.UserRole)).toInt()[0]
|
topicId = (item.data(QtCore.Qt.UserRole)).toInt()[0]
|
||||||
self.song.topics.append(self.manager.get_object(Topic, topicId))
|
self.song.topics.append(self.manager.get_object(Topic, topicId))
|
||||||
|
# Save the song here because we need a valid id for the audio files.
|
||||||
clean_song(self.manager, self.song)
|
clean_song(self.manager, self.song)
|
||||||
self.manager.save_object(self.song)
|
self.manager.save_object(self.song)
|
||||||
self.mediaitem.auto_select_id = self.song.id
|
audio_files = map(lambda a: a.file_name, self.song.media_files)
|
||||||
|
log.debug(audio_files)
|
||||||
|
save_path = os.path.join(
|
||||||
|
AppLocation.get_section_data_path(self.mediaitem.plugin.name),
|
||||||
|
'audio', str(self.song.id))
|
||||||
|
if not os.path.exists(save_path):
|
||||||
|
os.makedirs(save_path)
|
||||||
|
self.song.media_files = []
|
||||||
|
files = []
|
||||||
|
for row in xrange(self.audioListWidget.count()):
|
||||||
|
item = self.audioListWidget.item(row)
|
||||||
|
filename = unicode(item.data(QtCore.Qt.UserRole).toString())
|
||||||
|
if not filename.startswith(save_path):
|
||||||
|
oldfile, filename = filename, os.path.join(save_path,
|
||||||
|
os.path.split(filename)[1])
|
||||||
|
shutil.copyfile(oldfile, filename)
|
||||||
|
files.append(filename)
|
||||||
|
media_file = MediaFile()
|
||||||
|
media_file.file_name = filename
|
||||||
|
media_file.type = u'audio'
|
||||||
|
media_file.weight = row
|
||||||
|
self.song.media_files.append(media_file)
|
||||||
|
for audio in audio_files:
|
||||||
|
if audio not in files:
|
||||||
|
try:
|
||||||
|
os.remove(audio)
|
||||||
|
except:
|
||||||
|
log.exception('Could not remove file: %s', audio)
|
||||||
|
pass
|
||||||
|
if not files:
|
||||||
|
try:
|
||||||
|
os.rmdir(save_path)
|
||||||
|
except OSError:
|
||||||
|
log.exception(u'Could not remove directory: %s', save_path)
|
||||||
|
clean_song(self.manager, self.song)
|
||||||
|
self.manager.save_object(self.song)
|
||||||
|
self.mediaitem.autoSelectId = self.song.id
|
||||||
|
|
||||||
def _processLyrics(self):
|
def _processLyrics(self):
|
||||||
"""
|
"""
|
||||||
@ -783,3 +912,4 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
except:
|
except:
|
||||||
log.exception(u'Problem processing song Lyrics \n%s',
|
log.exception(u'Problem processing song Lyrics \n%s',
|
||||||
sxml.dump_xml())
|
sxml.dump_xml())
|
||||||
|
|
||||||
|
75
openlp/plugins/songs/forms/mediafilesdialog.py
Normal file
75
openlp/plugins/songs/forms/mediafilesdialog.py
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||||
|
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
|
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
|
from openlp.core.lib import translate, build_icon
|
||||||
|
|
||||||
|
class Ui_MediaFilesDialog(object):
|
||||||
|
def setupUi(self, mediaFilesDialog):
|
||||||
|
mediaFilesDialog.setObjectName(u'mediaFilesDialog')
|
||||||
|
mediaFilesDialog.setWindowModality(QtCore.Qt.ApplicationModal)
|
||||||
|
mediaFilesDialog.resize(400, 300)
|
||||||
|
mediaFilesDialog.setModal(True)
|
||||||
|
mediaFilesDialog.setWindowIcon(
|
||||||
|
build_icon(u':/icon/openlp-logo-16x16.png'))
|
||||||
|
self.filesVerticalLayout = QtGui.QVBoxLayout(mediaFilesDialog)
|
||||||
|
self.filesVerticalLayout.setSpacing(8)
|
||||||
|
self.filesVerticalLayout.setMargin(8)
|
||||||
|
self.filesVerticalLayout.setObjectName(u'filesVerticalLayout')
|
||||||
|
self.selectLabel = QtGui.QLabel(mediaFilesDialog)
|
||||||
|
self.selectLabel.setWordWrap(True)
|
||||||
|
self.selectLabel.setObjectName(u'selectLabel')
|
||||||
|
self.filesVerticalLayout.addWidget(self.selectLabel)
|
||||||
|
self.fileListWidget = QtGui.QListWidget(mediaFilesDialog)
|
||||||
|
self.fileListWidget.setAlternatingRowColors(True)
|
||||||
|
self.fileListWidget.setSelectionMode(
|
||||||
|
QtGui.QAbstractItemView.ExtendedSelection)
|
||||||
|
self.fileListWidget.setObjectName(u'fileListWidget')
|
||||||
|
self.filesVerticalLayout.addWidget(self.fileListWidget)
|
||||||
|
self.buttonBox = QtGui.QDialogButtonBox(mediaFilesDialog)
|
||||||
|
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
|
||||||
|
self.buttonBox.setStandardButtons(
|
||||||
|
QtGui.QDialogButtonBox.Cancel | QtGui.QDialogButtonBox.Ok)
|
||||||
|
self.buttonBox.setObjectName(u'buttonBox')
|
||||||
|
self.filesVerticalLayout.addWidget(self.buttonBox)
|
||||||
|
|
||||||
|
self.retranslateUi(mediaFilesDialog)
|
||||||
|
QtCore.QObject.connect(self.buttonBox,
|
||||||
|
QtCore.SIGNAL(u'accepted()'), mediaFilesDialog.accept)
|
||||||
|
QtCore.QObject.connect(self.buttonBox,
|
||||||
|
QtCore.SIGNAL(u'rejected()'), mediaFilesDialog.reject)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(mediaFilesDialog)
|
||||||
|
|
||||||
|
def retranslateUi(self, mediaFilesDialog):
|
||||||
|
mediaFilesDialog.setWindowTitle(
|
||||||
|
translate('SongsPlugin.MediaFilesForm', 'Select Media File(s)'))
|
||||||
|
self.selectLabel.setText(
|
||||||
|
translate('SongsPlugin.MediaFilesForm', u'Select one or more '
|
||||||
|
'audio files from the list below, and click OK to import them '
|
||||||
|
'into this song.'))
|
||||||
|
|
57
openlp/plugins/songs/forms/mediafilesform.py
Normal file
57
openlp/plugins/songs/forms/mediafilesform.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||||
|
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
|
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
|
from mediafilesdialog import Ui_MediaFilesDialog
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class MediaFilesForm(QtGui.QDialog, Ui_MediaFilesDialog):
|
||||||
|
"""
|
||||||
|
Class to show a list of files from the
|
||||||
|
"""
|
||||||
|
log.info(u'%s MediaFilesForm loaded', __name__)
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
QtGui.QDialog.__init__(self)
|
||||||
|
self.setupUi(self)
|
||||||
|
|
||||||
|
def populateFiles(self, files):
|
||||||
|
self.fileListWidget.clear()
|
||||||
|
for file in files:
|
||||||
|
item = QtGui.QListWidgetItem(os.path.split(file)[1])
|
||||||
|
item.setData(QtCore.Qt.UserRole, file)
|
||||||
|
self.fileListWidget.addItem(item)
|
||||||
|
|
||||||
|
def getSelectedFiles(self):
|
||||||
|
return map(lambda x: unicode(x.data(QtCore.Qt.UserRole).toString()),
|
||||||
|
self.fileListWidget.selectedItems())
|
||||||
|
|
@ -686,7 +686,7 @@ class SongImportForm(OpenLPWizard):
|
|||||||
def performWizard(self):
|
def performWizard(self):
|
||||||
"""
|
"""
|
||||||
Perform the actual import. This method pulls in the correct importer
|
Perform the actual import. This method pulls in the correct importer
|
||||||
class, and then runs the ``do_import`` method of the importer to do
|
class, and then runs the ``doImport`` method of the importer to do
|
||||||
the actual importing.
|
the actual importing.
|
||||||
"""
|
"""
|
||||||
source_format = self.formatComboBox.currentIndex()
|
source_format = self.formatComboBox.currentIndex()
|
||||||
@ -759,8 +759,8 @@ class SongImportForm(OpenLPWizard):
|
|||||||
importer = self.plugin.importSongs(SongFormat.FoilPresenter,
|
importer = self.plugin.importSongs(SongFormat.FoilPresenter,
|
||||||
filenames=self.getListOfFiles(self.foilPresenterFileListWidget)
|
filenames=self.getListOfFiles(self.foilPresenterFileListWidget)
|
||||||
)
|
)
|
||||||
importer.do_import()
|
importer.doImport()
|
||||||
if importer.error_log:
|
if importer.errorLog:
|
||||||
self.progressLabel.setText(translate(
|
self.progressLabel.setText(translate(
|
||||||
'SongsPlugin.SongImportForm', 'Your song import failed.'))
|
'SongsPlugin.SongImportForm', 'Your song import failed.'))
|
||||||
else:
|
else:
|
||||||
|
@ -184,10 +184,11 @@ class VerseType(object):
|
|||||||
verse_index = VerseType.from_translated_string(verse_name)
|
verse_index = VerseType.from_translated_string(verse_name)
|
||||||
if verse_index is None:
|
if verse_index is None:
|
||||||
verse_index = VerseType.from_string(verse_name)
|
verse_index = VerseType.from_string(verse_name)
|
||||||
if verse_index is None:
|
elif len(verse_name) == 1:
|
||||||
verse_index = VerseType.from_translated_tag(verse_name)
|
if verse_index is None:
|
||||||
if verse_index is None:
|
verse_index = VerseType.from_translated_tag(verse_name)
|
||||||
verse_index = VerseType.from_tag(verse_name)
|
if verse_index is None:
|
||||||
|
verse_index = VerseType.from_tag(verse_name)
|
||||||
return verse_index
|
return verse_index
|
||||||
|
|
||||||
def retrieve_windows_encoding(recommendation=None):
|
def retrieve_windows_encoding(recommendation=None):
|
||||||
|
@ -55,13 +55,13 @@ class CCLIFileImport(SongImport):
|
|||||||
"""
|
"""
|
||||||
SongImport.__init__(self, manager, **kwargs)
|
SongImport.__init__(self, manager, **kwargs)
|
||||||
|
|
||||||
def do_import(self):
|
def doImport(self):
|
||||||
"""
|
"""
|
||||||
Import either a ``.usr`` or a ``.txt`` SongSelect file.
|
Import either a ``.usr`` or a ``.txt`` SongSelect file.
|
||||||
"""
|
"""
|
||||||
log.debug(u'Starting CCLI File Import')
|
log.debug(u'Starting CCLI File Import')
|
||||||
self.import_wizard.progressBar.setMaximum(len(self.import_source))
|
self.importWizard.progressBar.setMaximum(len(self.importSource))
|
||||||
for filename in self.import_source:
|
for filename in self.importSource:
|
||||||
filename = unicode(filename)
|
filename = unicode(filename)
|
||||||
log.debug(u'Importing CCLI File: %s', filename)
|
log.debug(u'Importing CCLI File: %s', filename)
|
||||||
lines = []
|
lines = []
|
||||||
@ -80,23 +80,23 @@ class CCLIFileImport(SongImport):
|
|||||||
ext = os.path.splitext(filename)[1]
|
ext = os.path.splitext(filename)[1]
|
||||||
if ext.lower() == u'.usr':
|
if ext.lower() == u'.usr':
|
||||||
log.info(u'SongSelect .usr format file found: %s', filename)
|
log.info(u'SongSelect .usr format file found: %s', filename)
|
||||||
if not self.do_import_usr_file(lines):
|
if not self.doImportUsrFile(lines):
|
||||||
self.log_error(filename)
|
self.logError(filename)
|
||||||
elif ext.lower() == u'.txt':
|
elif ext.lower() == u'.txt':
|
||||||
log.info(u'SongSelect .txt format file found: %s', filename)
|
log.info(u'SongSelect .txt format file found: %s', filename)
|
||||||
if not self.do_import_txt_file(lines):
|
if not self.doImportTxtFile(lines):
|
||||||
self.log_error(filename)
|
self.logError(filename)
|
||||||
else:
|
else:
|
||||||
self.log_error(filename,
|
self.logError(filename,
|
||||||
translate('SongsPlugin.CCLIFileImport',
|
translate('SongsPlugin.CCLIFileImport',
|
||||||
'The file does not have a valid extension.'))
|
'The file does not have a valid extension.'))
|
||||||
log.info(u'Extension %s is not valid', filename)
|
log.info(u'Extension %s is not valid', filename)
|
||||||
if self.stop_import_flag:
|
if self.stopImportFlag:
|
||||||
return
|
return
|
||||||
|
|
||||||
def do_import_usr_file(self, textList):
|
def doImportUsrFile(self, textList):
|
||||||
"""
|
"""
|
||||||
The :func:`do_import_usr_file` method provides OpenLP with the ability
|
The :func:`doImport_usr_file` method provides OpenLP with the ability
|
||||||
to import CCLI SongSelect songs in *USR* file format.
|
to import CCLI SongSelect songs in *USR* file format.
|
||||||
|
|
||||||
``textList``
|
``textList``
|
||||||
@ -165,7 +165,7 @@ class CCLIFileImport(SongImport):
|
|||||||
elif line.startswith(u'Themes='):
|
elif line.startswith(u'Themes='):
|
||||||
song_topics = line[7:].strip()
|
song_topics = line[7:].strip()
|
||||||
elif line.startswith(u'[S A'):
|
elif line.startswith(u'[S A'):
|
||||||
self.ccli_number = line[4:-3].strip()
|
self.ccliNumber = line[4:-3].strip()
|
||||||
elif line.startswith(u'Fields='):
|
elif line.startswith(u'Fields='):
|
||||||
# Fields contain single line indicating verse, chorus, etc,
|
# Fields contain single line indicating verse, chorus, etc,
|
||||||
# /t delimited, same as with words field. store seperately
|
# /t delimited, same as with words field. store seperately
|
||||||
@ -204,7 +204,7 @@ class CCLIFileImport(SongImport):
|
|||||||
verse_type = VerseType.Tags[VerseType.Other]
|
verse_type = VerseType.Tags[VerseType.Other]
|
||||||
verse_text = verse_lines[1]
|
verse_text = verse_lines[1]
|
||||||
if len(verse_text) > 0:
|
if len(verse_text) > 0:
|
||||||
self.add_verse(verse_text, verse_type)
|
self.addVerse(verse_text, verse_type)
|
||||||
check_first_verse_line = False
|
check_first_verse_line = False
|
||||||
# Handle multiple authors
|
# Handle multiple authors
|
||||||
author_list = song_author.split(u'/')
|
author_list = song_author.split(u'/')
|
||||||
@ -213,15 +213,15 @@ class CCLIFileImport(SongImport):
|
|||||||
for author in author_list:
|
for author in author_list:
|
||||||
separated = author.split(u',')
|
separated = author.split(u',')
|
||||||
if len(separated) > 1:
|
if len(separated) > 1:
|
||||||
self.add_author(u' '.join(reversed(separated)))
|
self.addAuthor(u' '.join(reversed(separated)))
|
||||||
else:
|
else:
|
||||||
self.add_author(author)
|
self.addAuthor(author)
|
||||||
self.topics = [topic.strip() for topic in song_topics.split(u'/t')]
|
self.topics = [topic.strip() for topic in song_topics.split(u'/t')]
|
||||||
return self.finish()
|
return self.finish()
|
||||||
|
|
||||||
def do_import_txt_file(self, textList):
|
def doImportTxtFile(self, textList):
|
||||||
"""
|
"""
|
||||||
The :func:`do_import_txt_file` method provides OpenLP with the ability
|
The :func:`doImport_txt_file` method provides OpenLP with the ability
|
||||||
to import CCLI SongSelect songs in *TXT* file format.
|
to import CCLI SongSelect songs in *TXT* file format.
|
||||||
|
|
||||||
``textList``
|
``textList``
|
||||||
@ -264,7 +264,7 @@ class CCLIFileImport(SongImport):
|
|||||||
continue
|
continue
|
||||||
elif verse_start:
|
elif verse_start:
|
||||||
if verse_text:
|
if verse_text:
|
||||||
self.add_verse(verse_text, verse_type)
|
self.addVerse(verse_text, verse_type)
|
||||||
verse_text = u''
|
verse_text = u''
|
||||||
verse_start = False
|
verse_start = False
|
||||||
else:
|
else:
|
||||||
@ -278,7 +278,7 @@ class CCLIFileImport(SongImport):
|
|||||||
if clean_line.startswith(u'CCLI'):
|
if clean_line.startswith(u'CCLI'):
|
||||||
line_number += 1
|
line_number += 1
|
||||||
ccli_parts = clean_line.split(' ')
|
ccli_parts = clean_line.split(' ')
|
||||||
self.ccli_number = ccli_parts[len(ccli_parts) - 1]
|
self.ccliNumber = ccli_parts[len(ccli_parts) - 1]
|
||||||
elif not verse_start:
|
elif not verse_start:
|
||||||
# We have the verse descriptor
|
# We have the verse descriptor
|
||||||
verse_desc_parts = clean_line.split(u' ')
|
verse_desc_parts = clean_line.split(u' ')
|
||||||
@ -333,5 +333,5 @@ class CCLIFileImport(SongImport):
|
|||||||
if len(author_list) < 2:
|
if len(author_list) < 2:
|
||||||
author_list = song_author.split(u'|')
|
author_list = song_author.split(u'|')
|
||||||
# Clean spaces before and after author names.
|
# Clean spaces before and after author names.
|
||||||
[self.add_author(author_name.strip()) for author_name in author_list]
|
[self.addAuthor(author_name.strip()) for author_name in author_list]
|
||||||
return self.finish()
|
return self.finish()
|
||||||
|
@ -232,7 +232,8 @@ def init_schema(url):
|
|||||||
'authors': relation(Author, backref='songs',
|
'authors': relation(Author, backref='songs',
|
||||||
secondary=authors_songs_table, lazy=False),
|
secondary=authors_songs_table, lazy=False),
|
||||||
'book': relation(Book, backref='songs'),
|
'book': relation(Book, backref='songs'),
|
||||||
'media_files': relation(MediaFile, backref='songs'),
|
'media_files': relation(MediaFile, backref='songs',
|
||||||
|
order_by=media_files_table.c.weight),
|
||||||
'topics': relation(Topic, backref='songs',
|
'topics': relation(Topic, backref='songs',
|
||||||
secondary=songs_topics_table)
|
secondary=songs_topics_table)
|
||||||
})
|
})
|
||||||
|
@ -49,50 +49,50 @@ class EasiSlidesImport(SongImport):
|
|||||||
SongImport.__init__(self, manager, **kwargs)
|
SongImport.__init__(self, manager, **kwargs)
|
||||||
self.commit = True
|
self.commit = True
|
||||||
|
|
||||||
def do_import(self):
|
def doImport(self):
|
||||||
"""
|
"""
|
||||||
Import either each of the files in self.import_sources - each element of
|
Import either each of the files in self.importSources - each element of
|
||||||
which can be either a single opensong file, or a zipfile containing
|
which can be either a single opensong file, or a zipfile containing
|
||||||
multiple opensong files. If `self.commit` is set False, the
|
multiple opensong files. If `self.commit` is set False, the
|
||||||
import will not be committed to the database (useful for test scripts).
|
import will not be committed to the database (useful for test scripts).
|
||||||
"""
|
"""
|
||||||
log.info(u'Importing EasiSlides XML file %s', self.import_source)
|
log.info(u'Importing EasiSlides XML file %s', self.importSource)
|
||||||
parser = etree.XMLParser(remove_blank_text=True)
|
parser = etree.XMLParser(remove_blank_text=True)
|
||||||
parsed_file = etree.parse(self.import_source, parser)
|
parsed_file = etree.parse(self.importSource, parser)
|
||||||
xml = unicode(etree.tostring(parsed_file))
|
xml = unicode(etree.tostring(parsed_file))
|
||||||
song_xml = objectify.fromstring(xml)
|
song_xml = objectify.fromstring(xml)
|
||||||
self.import_wizard.progressBar.setMaximum(len(song_xml.Item))
|
self.importWizard.progressBar.setMaximum(len(song_xml.Item))
|
||||||
for song in song_xml.Item:
|
for song in song_xml.Item:
|
||||||
if self.stop_import_flag:
|
if self.stopImportFlag:
|
||||||
return
|
return
|
||||||
self._parse_song(song)
|
self._parseSong(song)
|
||||||
|
|
||||||
def _parse_song(self, song):
|
def _parseSong(self, song):
|
||||||
self._success = True
|
self._success = True
|
||||||
self._add_unicode_attribute(u'title', song.Title1, True)
|
self._addUnicodeAttribute(u'title', song.Title1, True)
|
||||||
if hasattr(song, u'Title2'):
|
if hasattr(song, u'Title2'):
|
||||||
self._add_unicode_attribute(u'alternate_title', song.Title2)
|
self._addUnicodeAttribute(u'alternateTitle', song.Title2)
|
||||||
if hasattr(song, u'SongNumber'):
|
if hasattr(song, u'SongNumber'):
|
||||||
self._add_unicode_attribute(u'song_number', song.SongNumber)
|
self._addUnicodeAttribute(u'songNumber', song.SongNumber)
|
||||||
if self.song_number == u'0':
|
if self.songNumber == u'0':
|
||||||
self.song_number = u''
|
self.songNumber = u''
|
||||||
self._add_authors(song)
|
self._addAuthors(song)
|
||||||
if hasattr(song, u'Copyright'):
|
if hasattr(song, u'Copyright'):
|
||||||
self._add_copyright(song.Copyright)
|
self._addCopyright(song.Copyright)
|
||||||
if hasattr(song, u'LicenceAdmin1'):
|
if hasattr(song, u'LicenceAdmin1'):
|
||||||
self._add_copyright(song.LicenceAdmin1)
|
self._addCopyright(song.LicenceAdmin1)
|
||||||
if hasattr(song, u'LicenceAdmin2'):
|
if hasattr(song, u'LicenceAdmin2'):
|
||||||
self._add_copyright(song.LicenceAdmin2)
|
self._addCopyright(song.LicenceAdmin2)
|
||||||
if hasattr(song, u'BookReference'):
|
if hasattr(song, u'BookReference'):
|
||||||
self._add_unicode_attribute(u'song_book_name', song.BookReference)
|
self._addUnicodeAttribute(u'songBookName', song.BookReference)
|
||||||
self._parse_and_add_lyrics(song)
|
self._parseAndAddLyrics(song)
|
||||||
if self._success:
|
if self._success:
|
||||||
if not self.finish():
|
if not self.finish():
|
||||||
self.log_error(song.Title1 if song.Title1 else u'')
|
self.logError(song.Title1 if song.Title1 else u'')
|
||||||
else:
|
else:
|
||||||
self.set_defaults()
|
self.setDefaults()
|
||||||
|
|
||||||
def _add_unicode_attribute(self, self_attribute, import_attribute,
|
def _addUnicodeAttribute(self, self_attribute, import_attribute,
|
||||||
mandatory=False):
|
mandatory=False):
|
||||||
"""
|
"""
|
||||||
Add imported values to the song model converting them to unicode at the
|
Add imported values to the song model converting them to unicode at the
|
||||||
@ -119,7 +119,7 @@ class EasiSlidesImport(SongImport):
|
|||||||
if mandatory:
|
if mandatory:
|
||||||
self._success = False
|
self._success = False
|
||||||
|
|
||||||
def _add_authors(self, song):
|
def _addAuthors(self, song):
|
||||||
try:
|
try:
|
||||||
authors = unicode(song.Writer).split(u',')
|
authors = unicode(song.Writer).split(u',')
|
||||||
self.authors = \
|
self.authors = \
|
||||||
@ -130,7 +130,7 @@ class EasiSlidesImport(SongImport):
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _add_copyright(self, element):
|
def _addCopyright(self, element):
|
||||||
"""
|
"""
|
||||||
Add a piece of copyright to the total copyright information for the
|
Add a piece of copyright to the total copyright information for the
|
||||||
song.
|
song.
|
||||||
@ -139,14 +139,14 @@ class EasiSlidesImport(SongImport):
|
|||||||
The imported variable to get the data from.
|
The imported variable to get the data from.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
self.add_copyright(unicode(element).strip())
|
self.addCopyright(unicode(element).strip())
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
log.exception(u'Unicode error on decoding copyright: %s' % element)
|
log.exception(u'Unicode error on decoding copyright: %s' % element)
|
||||||
self._success = False
|
self._success = False
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _parse_and_add_lyrics(self, song):
|
def _parseAndAddLyrics(self, song):
|
||||||
try:
|
try:
|
||||||
lyrics = unicode(song.Contents).strip()
|
lyrics = unicode(song.Contents).strip()
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
@ -295,7 +295,7 @@ class EasiSlidesImport(SongImport):
|
|||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
if tag in versetags:
|
if tag in versetags:
|
||||||
self.verse_order_list.append(tag)
|
self.verseOrderList.append(tag)
|
||||||
else:
|
else:
|
||||||
log.info(u'Got order item %s, which is not in versetags,'
|
log.info(u'Got order item %s, which is not in versetags,'
|
||||||
u'dropping item from presentation order', tag)
|
u'dropping item from presentation order', tag)
|
||||||
|
@ -155,24 +155,24 @@ class EasyWorshipSongImport(SongImport):
|
|||||||
def __init__(self, manager, **kwargs):
|
def __init__(self, manager, **kwargs):
|
||||||
SongImport.__init__(self, manager, **kwargs)
|
SongImport.__init__(self, manager, **kwargs)
|
||||||
|
|
||||||
def do_import(self):
|
def doImport(self):
|
||||||
# Open the DB and MB files if they exist
|
# Open the DB and MB files if they exist
|
||||||
import_source_mb = self.import_source.replace('.DB', '.MB')
|
import_source_mb = self.importSource.replace('.DB', '.MB')
|
||||||
if not os.path.isfile(self.import_source):
|
if not os.path.isfile(self.importSource):
|
||||||
return
|
return
|
||||||
if not os.path.isfile(import_source_mb):
|
if not os.path.isfile(import_source_mb):
|
||||||
return
|
return
|
||||||
db_size = os.path.getsize(self.import_source)
|
db_size = os.path.getsize(self.importSource)
|
||||||
if db_size < 0x800:
|
if db_size < 0x800:
|
||||||
return
|
return
|
||||||
db_file = open(self.import_source, 'rb')
|
db_file = open(self.importSource, 'rb')
|
||||||
self.memo_file = open(import_source_mb, 'rb')
|
self.memoFile = open(import_source_mb, 'rb')
|
||||||
# Don't accept files that are clearly not paradox files
|
# Don't accept files that are clearly not paradox files
|
||||||
record_size, header_size, block_size, first_block, num_fields \
|
record_size, header_size, block_size, first_block, num_fields \
|
||||||
= struct.unpack('<hhxb8xh17xh', db_file.read(35))
|
= struct.unpack('<hhxb8xh17xh', db_file.read(35))
|
||||||
if header_size != 0x800 or block_size < 1 or block_size > 4:
|
if header_size != 0x800 or block_size < 1 or block_size > 4:
|
||||||
db_file.close()
|
db_file.close()
|
||||||
self.memo_file.close()
|
self.memoFile.close()
|
||||||
return
|
return
|
||||||
# Take a stab at how text is encoded
|
# Take a stab at how text is encoded
|
||||||
self.encoding = u'cp1252'
|
self.encoding = u'cp1252'
|
||||||
@ -204,7 +204,7 @@ class EasyWorshipSongImport(SongImport):
|
|||||||
# There does not appear to be a _reliable_ way of getting the number
|
# There does not appear to be a _reliable_ way of getting the number
|
||||||
# of songs/records, so let's use file blocks for measuring progress.
|
# of songs/records, so let's use file blocks for measuring progress.
|
||||||
total_blocks = (db_size - header_size) / (block_size * 1024)
|
total_blocks = (db_size - header_size) / (block_size * 1024)
|
||||||
self.import_wizard.progressBar.setMaximum(total_blocks)
|
self.importWizard.progressBar.setMaximum(total_blocks)
|
||||||
# Read the field description information
|
# Read the field description information
|
||||||
db_file.seek(120)
|
db_file.seek(120)
|
||||||
field_info = db_file.read(num_fields * 2)
|
field_info = db_file.read(num_fields * 2)
|
||||||
@ -218,16 +218,16 @@ class EasyWorshipSongImport(SongImport):
|
|||||||
field_info, i * 2)
|
field_info, i * 2)
|
||||||
field_descs.append(FieldDescEntry(field_name, field_type,
|
field_descs.append(FieldDescEntry(field_name, field_type,
|
||||||
field_size))
|
field_size))
|
||||||
self.set_record_struct(field_descs)
|
self.setRecordStruct(field_descs)
|
||||||
# Pick out the field description indexes we will need
|
# Pick out the field description indexes we will need
|
||||||
try:
|
try:
|
||||||
success = True
|
success = True
|
||||||
fi_title = self.find_field(u'Title')
|
fi_title = self.findField(u'Title')
|
||||||
fi_author = self.find_field(u'Author')
|
fi_author = self.findField(u'Author')
|
||||||
fi_copy = self.find_field(u'Copyright')
|
fi_copy = self.findField(u'Copyright')
|
||||||
fi_admin = self.find_field(u'Administrator')
|
fi_admin = self.findField(u'Administrator')
|
||||||
fi_words = self.find_field(u'Words')
|
fi_words = self.findField(u'Words')
|
||||||
fi_ccli = self.find_field(u'Song Number')
|
fi_ccli = self.findField(u'Song Number')
|
||||||
except IndexError:
|
except IndexError:
|
||||||
# This is the wrong table
|
# This is the wrong table
|
||||||
success = False
|
success = False
|
||||||
@ -239,18 +239,18 @@ class EasyWorshipSongImport(SongImport):
|
|||||||
rec_count = (rec_count + record_size) / record_size
|
rec_count = (rec_count + record_size) / record_size
|
||||||
# Loop through each record within the current block
|
# Loop through each record within the current block
|
||||||
for i in range(rec_count):
|
for i in range(rec_count):
|
||||||
if self.stop_import_flag:
|
if self.stopImportFlag:
|
||||||
break
|
break
|
||||||
raw_record = db_file.read(record_size)
|
raw_record = db_file.read(record_size)
|
||||||
self.fields = self.record_struct.unpack(raw_record)
|
self.fields = self.record_struct.unpack(raw_record)
|
||||||
self.set_defaults()
|
self.setDefaults()
|
||||||
self.title = self.get_field(fi_title)
|
self.title = self.getField(fi_title)
|
||||||
# Get remaining fields.
|
# Get remaining fields.
|
||||||
copy = self.get_field(fi_copy)
|
copy = self.getField(fi_copy)
|
||||||
admin = self.get_field(fi_admin)
|
admin = self.getField(fi_admin)
|
||||||
ccli = self.get_field(fi_ccli)
|
ccli = self.getField(fi_ccli)
|
||||||
authors = self.get_field(fi_author)
|
authors = self.getField(fi_author)
|
||||||
words = self.get_field(fi_words)
|
words = self.getField(fi_words)
|
||||||
# Set the SongImport object members.
|
# Set the SongImport object members.
|
||||||
if copy:
|
if copy:
|
||||||
self.copyright = copy
|
self.copyright = copy
|
||||||
@ -261,7 +261,7 @@ class EasyWorshipSongImport(SongImport):
|
|||||||
unicode(translate('SongsPlugin.EasyWorshipSongImport',
|
unicode(translate('SongsPlugin.EasyWorshipSongImport',
|
||||||
'Administered by %s')) % admin
|
'Administered by %s')) % admin
|
||||||
if ccli:
|
if ccli:
|
||||||
self.ccli_number = ccli
|
self.ccliNumber = ccli
|
||||||
if authors:
|
if authors:
|
||||||
# Split up the authors
|
# Split up the authors
|
||||||
author_list = authors.split(u'/')
|
author_list = authors.split(u'/')
|
||||||
@ -270,7 +270,7 @@ class EasyWorshipSongImport(SongImport):
|
|||||||
if len(author_list) < 2:
|
if len(author_list) < 2:
|
||||||
author_list = authors.split(u',')
|
author_list = authors.split(u',')
|
||||||
for author_name in author_list:
|
for author_name in author_list:
|
||||||
self.add_author(author_name.strip())
|
self.addAuthor(author_name.strip())
|
||||||
if words:
|
if words:
|
||||||
# Format the lyrics
|
# Format the lyrics
|
||||||
words = strip_rtf(words, self.encoding)
|
words = strip_rtf(words, self.encoding)
|
||||||
@ -305,23 +305,24 @@ class EasyWorshipSongImport(SongImport):
|
|||||||
if not number_found:
|
if not number_found:
|
||||||
verse_type += u'1'
|
verse_type += u'1'
|
||||||
break
|
break
|
||||||
self.add_verse(
|
self.addVerse(
|
||||||
verse_split[-1].strip() if first_line_is_tag else verse,
|
verse_split[-1].strip() \
|
||||||
|
if first_line_is_tag else verse,
|
||||||
verse_type)
|
verse_type)
|
||||||
if len(self.comments) > 5:
|
if len(self.comments) > 5:
|
||||||
self.comments += unicode(
|
self.comments += unicode(
|
||||||
translate('SongsPlugin.EasyWorshipSongImport',
|
translate('SongsPlugin.EasyWorshipSongImport',
|
||||||
'\n[above are Song Tags with notes imported from \
|
'\n[above are Song Tags with notes imported from \
|
||||||
EasyWorship]'))
|
EasyWorship]'))
|
||||||
if self.stop_import_flag:
|
if self.stopImportFlag:
|
||||||
break
|
break
|
||||||
if not self.finish():
|
if not self.finish():
|
||||||
self.log_error(self.import_source)
|
self.logError(self.importSource)
|
||||||
db_file.close()
|
db_file.close()
|
||||||
self.memo_file.close()
|
self.memoFile.close()
|
||||||
|
|
||||||
def find_field(self, field_name):
|
def find_field(self, field_name):
|
||||||
return [i for i, x in enumerate(self.field_descs)
|
return [i for i, x in enumerate(self.fieldDescs)
|
||||||
if x.name == field_name][0]
|
if x.name == field_name][0]
|
||||||
|
|
||||||
def set_record_struct(self, field_descs):
|
def set_record_struct(self, field_descs):
|
||||||
@ -351,12 +352,12 @@ class EasyWorshipSongImport(SongImport):
|
|||||||
fsl.append('Q')
|
fsl.append('Q')
|
||||||
else:
|
else:
|
||||||
fsl.append('%ds' % field_desc.size)
|
fsl.append('%ds' % field_desc.size)
|
||||||
self.record_struct = struct.Struct(''.join(fsl))
|
self.recordStruct = struct.Struct(''.join(fsl))
|
||||||
self.field_descs = field_descs
|
self.fieldDescs = field_descs
|
||||||
|
|
||||||
def get_field(self, field_desc_index):
|
def get_field(self, field_desc_index):
|
||||||
field = self.fields[field_desc_index]
|
field = self.fields[field_desc_index]
|
||||||
field_desc = self.field_descs[field_desc_index]
|
field_desc = self.fieldDescs[field_desc_index]
|
||||||
# Return None in case of 'blank' entries
|
# Return None in case of 'blank' entries
|
||||||
if isinstance(field, str):
|
if isinstance(field, str):
|
||||||
if len(field.rstrip('\0')) == 0:
|
if len(field.rstrip('\0')) == 0:
|
||||||
@ -382,18 +383,18 @@ class EasyWorshipSongImport(SongImport):
|
|||||||
struct.unpack_from('<II', field, len(field)-10)
|
struct.unpack_from('<II', field, len(field)-10)
|
||||||
sub_block = block_start & 0xff
|
sub_block = block_start & 0xff
|
||||||
block_start &= ~0xff
|
block_start &= ~0xff
|
||||||
self.memo_file.seek(block_start)
|
self.memoFile.seek(block_start)
|
||||||
memo_block_type, = struct.unpack('b', self.memo_file.read(1))
|
memo_block_type, = struct.unpack('b', self.memoFile.read(1))
|
||||||
if memo_block_type == 2:
|
if memo_block_type == 2:
|
||||||
self.memo_file.seek(8, os.SEEK_CUR)
|
self.memoFile.seek(8, os.SEEK_CUR)
|
||||||
elif memo_block_type == 3:
|
elif memo_block_type == 3:
|
||||||
if sub_block > 63:
|
if sub_block > 63:
|
||||||
return u''
|
return u''
|
||||||
self.memo_file.seek(11 + (5 * sub_block), os.SEEK_CUR)
|
self.memoFile.seek(11 + (5 * sub_block), os.SEEK_CUR)
|
||||||
sub_block_start, = struct.unpack('B', self.memo_file.read(1))
|
sub_block_start, = struct.unpack('B', self.memoFile.read(1))
|
||||||
self.memo_file.seek(block_start + (sub_block_start * 16))
|
self.memoFile.seek(block_start + (sub_block_start * 16))
|
||||||
else:
|
else:
|
||||||
return u''
|
return u''
|
||||||
return self.memo_file.read(blob_size)
|
return self.memoFile.read(blob_size)
|
||||||
else:
|
else:
|
||||||
return 0
|
return 0
|
||||||
|
@ -115,23 +115,23 @@ class FoilPresenterImport(SongImport):
|
|||||||
SongImport.__init__(self, manager, **kwargs)
|
SongImport.__init__(self, manager, **kwargs)
|
||||||
self.FoilPresenter = FoilPresenter(self.manager)
|
self.FoilPresenter = FoilPresenter(self.manager)
|
||||||
|
|
||||||
def do_import(self):
|
def doImport(self):
|
||||||
"""
|
"""
|
||||||
Imports the songs.
|
Imports the songs.
|
||||||
"""
|
"""
|
||||||
self.import_wizard.progressBar.setMaximum(len(self.import_source))
|
self.importWizard.progressBar.setMaximum(len(self.importSource))
|
||||||
parser = etree.XMLParser(remove_blank_text=True)
|
parser = etree.XMLParser(remove_blank_text=True)
|
||||||
for file_path in self.import_source:
|
for file_path in self.importSource:
|
||||||
if self.stop_import_flag:
|
if self.stopImportFlag:
|
||||||
return
|
return
|
||||||
self.import_wizard.incrementProgressBar(
|
self.importWizard.incrementProgressBar(
|
||||||
WizardStrings.ImportingType % os.path.basename(file_path))
|
WizardStrings.ImportingType % os.path.basename(file_path))
|
||||||
try:
|
try:
|
||||||
parsed_file = etree.parse(file_path, parser)
|
parsed_file = etree.parse(file_path, parser)
|
||||||
xml = unicode(etree.tostring(parsed_file))
|
xml = unicode(etree.tostring(parsed_file))
|
||||||
self.FoilPresenter.xml_to_song(xml)
|
self.FoilPresenter.xml_to_song(xml)
|
||||||
except etree.XMLSyntaxError:
|
except etree.XMLSyntaxError:
|
||||||
self.log_error(file_path, SongStrings.XMLSyntaxError)
|
self.logError(file_path, SongStrings.XMLSyntaxError)
|
||||||
log.exception(u'XML syntax error in file %s' % file_path)
|
log.exception(u'XML syntax error in file %s' % file_path)
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,6 +28,8 @@
|
|||||||
import logging
|
import logging
|
||||||
import locale
|
import locale
|
||||||
import re
|
import re
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
from sqlalchemy.sql import or_
|
from sqlalchemy.sql import or_
|
||||||
@ -37,11 +39,12 @@ from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \
|
|||||||
from openlp.core.lib.searchedit import SearchEdit
|
from openlp.core.lib.searchedit import SearchEdit
|
||||||
from openlp.core.lib.ui import UiStrings, context_menu_action, \
|
from openlp.core.lib.ui import UiStrings, context_menu_action, \
|
||||||
context_menu_separator
|
context_menu_separator
|
||||||
|
from openlp.core.utils import AppLocation
|
||||||
from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \
|
from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \
|
||||||
SongImportForm, SongExportForm
|
SongImportForm, SongExportForm
|
||||||
from openlp.plugins.songs.lib import OpenLyrics, SongXML, VerseType, \
|
from openlp.plugins.songs.lib import OpenLyrics, SongXML, VerseType, \
|
||||||
clean_string
|
clean_string
|
||||||
from openlp.plugins.songs.lib.db import Author, Song
|
from openlp.plugins.songs.lib.db import Author, Song, MediaFile
|
||||||
from openlp.plugins.songs.lib.ui import SongStrings
|
from openlp.plugins.songs.lib.ui import SongStrings
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -66,11 +69,11 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
def __init__(self, parent, plugin, icon):
|
def __init__(self, parent, plugin, icon):
|
||||||
self.IconPath = u'songs/song'
|
self.IconPath = u'songs/song'
|
||||||
MediaManagerItem.__init__(self, parent, plugin, icon)
|
MediaManagerItem.__init__(self, parent, plugin, icon)
|
||||||
self.edit_song_form = EditSongForm(self, self.plugin.formparent,
|
self.editSongForm = EditSongForm(self, self.plugin.formparent,
|
||||||
self.plugin.manager)
|
self.plugin.manager)
|
||||||
self.openLyrics = OpenLyrics(self.plugin.manager)
|
self.openLyrics = OpenLyrics(self.plugin.manager)
|
||||||
self.singleServiceItem = False
|
self.singleServiceItem = False
|
||||||
self.song_maintenance_form = SongMaintenanceForm(
|
self.songMaintenanceForm = SongMaintenanceForm(
|
||||||
self.plugin.manager, self)
|
self.plugin.manager, self)
|
||||||
# Holds information about whether the edit is remotly triggered and
|
# Holds information about whether the edit is remotly triggered and
|
||||||
# which Song is required.
|
# which Song is required.
|
||||||
@ -79,6 +82,22 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
self.quickPreviewAllowed = True
|
self.quickPreviewAllowed = True
|
||||||
self.hasSearch = True
|
self.hasSearch = True
|
||||||
|
|
||||||
|
def _updateBackgroundAudio(self, song, item):
|
||||||
|
song.media_files = []
|
||||||
|
for i, bga in enumerate(item.background_audio):
|
||||||
|
dest_file = os.path.join(
|
||||||
|
AppLocation.get_section_data_path(self.plugin.name),
|
||||||
|
u'audio', str(song.id), os.path.split(bga)[1])
|
||||||
|
if not os.path.exists(os.path.split(dest_file)[0]):
|
||||||
|
os.makedirs(os.path.split(dest_file)[0])
|
||||||
|
shutil.copyfile(os.path.join(
|
||||||
|
AppLocation.get_section_data_path(
|
||||||
|
u'servicemanager'), bga),
|
||||||
|
dest_file)
|
||||||
|
song.media_files.append(MediaFile.populate(
|
||||||
|
weight=i, file_name=dest_file))
|
||||||
|
self.plugin.manager.save_object(song, True)
|
||||||
|
|
||||||
def addEndHeaderBar(self):
|
def addEndHeaderBar(self):
|
||||||
self.addToolbarSeparator()
|
self.addToolbarSeparator()
|
||||||
## Song Maintenance Button ##
|
## Song Maintenance Button ##
|
||||||
@ -210,7 +229,7 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
search_results = self.plugin.manager.get_all_objects(Song,
|
search_results = self.plugin.manager.get_all_objects(Song,
|
||||||
Song.theme_name.like(u'%' + search_keywords + u'%'))
|
Song.theme_name.like(u'%' + search_keywords + u'%'))
|
||||||
self.displayResultsSong(search_results)
|
self.displayResultsSong(search_results)
|
||||||
self.check_search_result()
|
self.checkSearchResult()
|
||||||
|
|
||||||
def searchEntire(self, search_keywords):
|
def searchEntire(self, search_keywords):
|
||||||
return self.plugin.manager.get_all_objects(Song,
|
return self.plugin.manager.get_all_objects(Song,
|
||||||
@ -244,7 +263,7 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
|
|
||||||
def displayResultsSong(self, searchresults):
|
def displayResultsSong(self, searchresults):
|
||||||
log.debug(u'display results Song')
|
log.debug(u'display results Song')
|
||||||
self.save_auto_select_id()
|
self.saveAutoSelectId()
|
||||||
self.listView.clear()
|
self.listView.clear()
|
||||||
# Sort the songs by its title considering language specific characters.
|
# Sort the songs by its title considering language specific characters.
|
||||||
# lower() is needed for windows!
|
# lower() is needed for windows!
|
||||||
@ -258,9 +277,9 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
song_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(song.id))
|
song_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(song.id))
|
||||||
self.listView.addItem(song_name)
|
self.listView.addItem(song_name)
|
||||||
# Auto-select the item if name has been set
|
# Auto-select the item if name has been set
|
||||||
if song.id == self.auto_select_id:
|
if song.id == self.autoSelectId:
|
||||||
self.listView.setCurrentItem(song_name)
|
self.listView.setCurrentItem(song_name)
|
||||||
self.auto_select_id = -1
|
self.autoSelectId = -1
|
||||||
|
|
||||||
def displayResultsAuthor(self, searchresults):
|
def displayResultsAuthor(self, searchresults):
|
||||||
log.debug(u'display results Author')
|
log.debug(u'display results Author')
|
||||||
@ -297,25 +316,26 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
self.onClearTextButtonClick()
|
self.onClearTextButtonClick()
|
||||||
|
|
||||||
def onImportClick(self):
|
def onImportClick(self):
|
||||||
if not hasattr(self, u'import_wizard'):
|
if not hasattr(self, u'importWizard'):
|
||||||
self.import_wizard = SongImportForm(self, self.plugin)
|
self.importWizard = SongImportForm(self, self.plugin)
|
||||||
if self.import_wizard.exec_() == QtGui.QDialog.Accepted:
|
if self.importWizard.exec_() == QtGui.QDialog.Accepted:
|
||||||
Receiver.send_message(u'songs_load_list')
|
Receiver.send_message(u'songs_load_list')
|
||||||
|
|
||||||
def onExportClick(self):
|
def onExportClick(self):
|
||||||
export_wizard = SongExportForm(self, self.plugin)
|
if not hasattr(self, u'exportWizard'):
|
||||||
export_wizard.exec_()
|
self.exportWizard = SongExportForm(self, self.plugin)
|
||||||
|
self.exportWizard.exec_()
|
||||||
|
|
||||||
def onNewClick(self):
|
def onNewClick(self):
|
||||||
log.debug(u'onNewClick')
|
log.debug(u'onNewClick')
|
||||||
self.edit_song_form.newSong()
|
self.editSongForm.newSong()
|
||||||
self.edit_song_form.exec_()
|
self.editSongForm.exec_()
|
||||||
self.onClearTextButtonClick()
|
self.onClearTextButtonClick()
|
||||||
self.onSelectionChange()
|
self.onSelectionChange()
|
||||||
self.auto_select_id = -1
|
self.autoSelectId = -1
|
||||||
|
|
||||||
def onSongMaintenanceClick(self):
|
def onSongMaintenanceClick(self):
|
||||||
self.song_maintenance_form.exec_()
|
self.songMaintenanceForm.exec_()
|
||||||
|
|
||||||
def onRemoteEditClear(self):
|
def onRemoteEditClear(self):
|
||||||
log.debug(u'onRemoteEditClear')
|
log.debug(u'onRemoteEditClear')
|
||||||
@ -335,9 +355,9 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
if valid:
|
if valid:
|
||||||
self.remoteSong = song_id
|
self.remoteSong = song_id
|
||||||
self.remoteTriggered = remote_type
|
self.remoteTriggered = remote_type
|
||||||
self.edit_song_form.loadSong(song_id, (remote_type == u'P'))
|
self.editSongForm.loadSong(song_id, remote_type == u'P')
|
||||||
self.edit_song_form.exec_()
|
self.editSongForm.exec_()
|
||||||
self.auto_select_id = -1
|
self.autoSelectId = -1
|
||||||
self.onSongListLoad()
|
self.onSongListLoad()
|
||||||
|
|
||||||
def onEditClick(self):
|
def onEditClick(self):
|
||||||
@ -348,9 +368,9 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
if check_item_selected(self.listView, UiStrings().SelectEdit):
|
if check_item_selected(self.listView, UiStrings().SelectEdit):
|
||||||
self.editItem = self.listView.currentItem()
|
self.editItem = self.listView.currentItem()
|
||||||
item_id = (self.editItem.data(QtCore.Qt.UserRole)).toInt()[0]
|
item_id = (self.editItem.data(QtCore.Qt.UserRole)).toInt()[0]
|
||||||
self.edit_song_form.loadSong(item_id, False)
|
self.editSongForm.loadSong(item_id, False)
|
||||||
self.edit_song_form.exec_()
|
self.editSongForm.exec_()
|
||||||
self.auto_select_id = -1
|
self.autoSelectId = -1
|
||||||
self.onSongListLoad()
|
self.onSongListLoad()
|
||||||
self.editItem = None
|
self.editItem = None
|
||||||
|
|
||||||
@ -371,6 +391,20 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
return
|
return
|
||||||
for item in items:
|
for item in items:
|
||||||
item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
|
item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
|
||||||
|
media_files = self.plugin.manager.get_all_objects(MediaFile,
|
||||||
|
MediaFile.song_id == item_id)
|
||||||
|
for media_file in media_files:
|
||||||
|
try:
|
||||||
|
os.remove(media_file.file_name)
|
||||||
|
except:
|
||||||
|
log.exception('Could not remove file: %s', audio)
|
||||||
|
try:
|
||||||
|
save_path = os.path.join(AppLocation.get_section_data_path(
|
||||||
|
self.plugin.name), 'audio', str(item_id))
|
||||||
|
if os.path.exists(save_path):
|
||||||
|
os.rmdir(save_path)
|
||||||
|
except OSError:
|
||||||
|
log.exception(u'Could not remove directory: %s', save_path)
|
||||||
self.plugin.manager.delete_object(Song, item_id)
|
self.plugin.manager.delete_object(Song, item_id)
|
||||||
self.onSearchTextButtonClick()
|
self.onSearchTextButtonClick()
|
||||||
|
|
||||||
@ -392,15 +426,16 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
self.plugin.manager.save_object(new_song)
|
self.plugin.manager.save_object(new_song)
|
||||||
self.onSongListLoad()
|
self.onSongListLoad()
|
||||||
|
|
||||||
def generateSlideData(self, service_item, item=None, xmlVersion=False):
|
def generateSlideData(self, service_item, item=None, xmlVersion=False,
|
||||||
|
remote=False):
|
||||||
log.debug(u'generateSlideData (%s:%s)' % (service_item, item))
|
log.debug(u'generateSlideData (%s:%s)' % (service_item, item))
|
||||||
item_id = self._getIdOfItemToGenerate(item, self.remoteSong)
|
item_id = self._getIdOfItemToGenerate(item, self.remoteSong)
|
||||||
service_item.add_capability(ItemCapabilities.AllowsEdit)
|
service_item.add_capability(ItemCapabilities.CanEdit)
|
||||||
service_item.add_capability(ItemCapabilities.AllowsPreview)
|
service_item.add_capability(ItemCapabilities.CanPreview)
|
||||||
service_item.add_capability(ItemCapabilities.AllowsLoop)
|
service_item.add_capability(ItemCapabilities.CanLoop)
|
||||||
service_item.add_capability(ItemCapabilities.OnLoadUpdate)
|
service_item.add_capability(ItemCapabilities.OnLoadUpdate)
|
||||||
service_item.add_capability(ItemCapabilities.AddIfNewItem)
|
service_item.add_capability(ItemCapabilities.AddIfNewItem)
|
||||||
service_item.add_capability(ItemCapabilities.AllowsVirtualSplit)
|
service_item.add_capability(ItemCapabilities.CanSoftBreak)
|
||||||
song = self.plugin.manager.get_object(Song, item_id)
|
song = self.plugin.manager.get_object(Song, item_id)
|
||||||
service_item.theme = song.theme_name
|
service_item.theme = song.theme_name
|
||||||
service_item.edit_id = item_id
|
service_item.edit_id = item_id
|
||||||
@ -471,6 +506,10 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
service_item.data_string = {u'title': song.search_title,
|
service_item.data_string = {u'title': song.search_title,
|
||||||
u'authors': u', '.join(author_list)}
|
u'authors': u', '.join(author_list)}
|
||||||
service_item.xml_version = self.openLyrics.song_to_xml(song)
|
service_item.xml_version = self.openLyrics.song_to_xml(song)
|
||||||
|
# Add the audio file to the service item.
|
||||||
|
if len(song.media_files) > 0:
|
||||||
|
service_item.add_capability(ItemCapabilities.HasBackgroundAudio)
|
||||||
|
service_item.background_audio = [m.file_name for m in song.media_files]
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def serviceLoad(self, item):
|
def serviceLoad(self, item):
|
||||||
@ -510,8 +549,15 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
add_song = False
|
add_song = False
|
||||||
editId = song.id
|
editId = song.id
|
||||||
break
|
break
|
||||||
|
# If there's any backing tracks, copy them over.
|
||||||
|
if len(item.background_audio) > 0:
|
||||||
|
self._updateBackgroundAudio(song, item)
|
||||||
if add_song and self.addSongFromService:
|
if add_song and self.addSongFromService:
|
||||||
editId = self.openLyrics.xml_to_song(item.xml_version)
|
song = self.openLyrics.xml_to_song(item.xml_version)
|
||||||
|
# If there's any backing tracks, copy them over.
|
||||||
|
if len(item.background_audio) > 0:
|
||||||
|
self._updateBackgroundAudio(song, item)
|
||||||
|
editId = song.id
|
||||||
self.onSearchTextButtonClick()
|
self.onSearchTextButtonClick()
|
||||||
# Update service with correct song id.
|
# Update service with correct song id.
|
||||||
if editId:
|
if editId:
|
||||||
|
@ -32,6 +32,8 @@ openlp.org 1.x song databases into the current installation database.
|
|||||||
import logging
|
import logging
|
||||||
from chardet.universaldetector import UniversalDetector
|
from chardet.universaldetector import UniversalDetector
|
||||||
import sqlite
|
import sqlite
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
from openlp.core.lib import translate
|
from openlp.core.lib import translate
|
||||||
from openlp.plugins.songs.lib import retrieve_windows_encoding
|
from openlp.plugins.songs.lib import retrieve_windows_encoding
|
||||||
@ -44,7 +46,7 @@ class OpenLP1SongImport(SongImport):
|
|||||||
The :class:`OpenLP1SongImport` class provides OpenLP with the ability to
|
The :class:`OpenLP1SongImport` class provides OpenLP with the ability to
|
||||||
import song databases from installations of openlp.org 1.x.
|
import song databases from installations of openlp.org 1.x.
|
||||||
"""
|
"""
|
||||||
last_encoding = u'windows-1252'
|
lastEncoding = u'windows-1252'
|
||||||
|
|
||||||
def __init__(self, manager, **kwargs):
|
def __init__(self, manager, **kwargs):
|
||||||
"""
|
"""
|
||||||
@ -57,23 +59,23 @@ class OpenLP1SongImport(SongImport):
|
|||||||
The database providing the data to import.
|
The database providing the data to import.
|
||||||
"""
|
"""
|
||||||
SongImport.__init__(self, manager, **kwargs)
|
SongImport.__init__(self, manager, **kwargs)
|
||||||
self.available_themes = \
|
self.availableThemes = \
|
||||||
kwargs[u'plugin'].formparent.themeManagerContents.getThemes()
|
kwargs[u'plugin'].formparent.themeManagerContents.getThemes()
|
||||||
|
|
||||||
def do_import(self):
|
def doImport(self):
|
||||||
"""
|
"""
|
||||||
Run the import for an openlp.org 1.x song database.
|
Run the import for an openlp.org 1.x song database.
|
||||||
"""
|
"""
|
||||||
if not self.import_source.endswith(u'.olp'):
|
if not self.importSource.endswith(u'.olp'):
|
||||||
self.log_error(self.import_source,
|
self.logError(self.importSource,
|
||||||
translate('SongsPlugin.OpenLP1SongImport',
|
translate('SongsPlugin.OpenLP1SongImport',
|
||||||
'Not a valid openlp.org 1.x song database.'))
|
'Not a valid openlp.org 1.x song database.'))
|
||||||
return
|
return
|
||||||
encoding = self.get_encoding()
|
encoding = self.getEncoding()
|
||||||
if not encoding:
|
if not encoding:
|
||||||
return
|
return
|
||||||
# Connect to the database.
|
# Connect to the database.
|
||||||
connection = sqlite.connect(self.import_source, mode=0444,
|
connection = sqlite.connect(self.importSource, mode=0444,
|
||||||
encoding=(encoding, 'replace'))
|
encoding=(encoding, 'replace'))
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
# Determine if we're using a new or an old DB.
|
# Determine if we're using a new or an old DB.
|
||||||
@ -94,64 +96,66 @@ class OpenLP1SongImport(SongImport):
|
|||||||
cursor.execute(u'SELECT settingsid, settingsname FROM settings')
|
cursor.execute(u'SELECT settingsid, settingsname FROM settings')
|
||||||
themes = {}
|
themes = {}
|
||||||
for theme_id, theme_name in cursor.fetchall():
|
for theme_id, theme_name in cursor.fetchall():
|
||||||
if theme_name in self.available_themes:
|
if theme_name in self.availableThemes:
|
||||||
themes[theme_id] = theme_name
|
themes[theme_id] = theme_name
|
||||||
# Import the songs.
|
# Import the songs.
|
||||||
cursor.execute(u'-- types int, unicode, unicode, unicode, int')
|
cursor.execute(u'-- types int, unicode, unicode, unicode, int')
|
||||||
cursor.execute(u'SELECT songid, songtitle, lyrics || \'\' AS lyrics, '
|
cursor.execute(u'SELECT songid, songtitle, lyrics || \'\' AS lyrics, '
|
||||||
u'copyrightinfo, settingsid FROM songs')
|
u'copyrightinfo, settingsid FROM songs')
|
||||||
songs = cursor.fetchall()
|
songs = cursor.fetchall()
|
||||||
self.import_wizard.progressBar.setMaximum(len(songs))
|
self.importWizard.progressBar.setMaximum(len(songs))
|
||||||
for song in songs:
|
for song in songs:
|
||||||
self.set_defaults()
|
self.setDefaults()
|
||||||
if self.stop_import_flag:
|
if self.stopImportFlag:
|
||||||
break
|
break
|
||||||
song_id = song[0]
|
song_id = song[0]
|
||||||
self.title = song[1]
|
self.title = song[1]
|
||||||
lyrics = song[2].replace(u'\r\n', u'\n')
|
lyrics = song[2].replace(u'\r\n', u'\n')
|
||||||
self.add_copyright(song[3])
|
self.addCopyright(song[3])
|
||||||
if themes.has_key(song[4]):
|
if themes.has_key(song[4]):
|
||||||
self.theme_name = themes[song[4]]
|
self.themeName = themes[song[4]]
|
||||||
verses = lyrics.split(u'\n\n')
|
verses = lyrics.split(u'\n\n')
|
||||||
for verse in verses:
|
for verse in verses:
|
||||||
if verse.strip():
|
if verse.strip():
|
||||||
self.add_verse(verse.strip())
|
self.addVerse(verse.strip())
|
||||||
cursor.execute(u'-- types int')
|
cursor.execute(u'-- types int')
|
||||||
cursor.execute(u'SELECT authorid FROM songauthors '
|
cursor.execute(u'SELECT authorid FROM songauthors '
|
||||||
u'WHERE songid = %s' % song_id)
|
u'WHERE songid = %s' % song_id)
|
||||||
author_ids = cursor.fetchall()
|
author_ids = cursor.fetchall()
|
||||||
for author_id in author_ids:
|
for author_id in author_ids:
|
||||||
if self.stop_import_flag:
|
if self.stopImportFlag:
|
||||||
break
|
break
|
||||||
for author in authors:
|
for author in authors:
|
||||||
if author[0] == author_id[0]:
|
if author[0] == author_id[0]:
|
||||||
self.parse_author(author[1])
|
self.parseAuthor(author[1])
|
||||||
break
|
break
|
||||||
if self.stop_import_flag:
|
if self.stopImportFlag:
|
||||||
break
|
break
|
||||||
if new_db:
|
if new_db:
|
||||||
cursor.execute(u'-- types int')
|
cursor.execute(u'-- types int, int')
|
||||||
cursor.execute(u'SELECT trackid FROM songtracks '
|
cursor.execute(u'SELECT trackid, listindex '
|
||||||
|
u'FROM songtracks '
|
||||||
u'WHERE songid = %s ORDER BY listindex' % song_id)
|
u'WHERE songid = %s ORDER BY listindex' % song_id)
|
||||||
track_ids = cursor.fetchall()
|
track_ids = cursor.fetchall()
|
||||||
for track_id in track_ids:
|
for track_id, listindex in track_ids:
|
||||||
if self.stop_import_flag:
|
if self.stopImportFlag:
|
||||||
break
|
break
|
||||||
for track in tracks:
|
for track in tracks:
|
||||||
if track[0] == track_id[0]:
|
if track[0] == track_id:
|
||||||
self.add_media_file(track[1])
|
media_file = self.expandMediaFile(track[1])
|
||||||
|
self.addMediaFile(media_file, listindex)
|
||||||
break
|
break
|
||||||
if self.stop_import_flag:
|
if self.stopImportFlag:
|
||||||
break
|
break
|
||||||
if not self.finish():
|
if not self.finish():
|
||||||
self.log_error(self.import_source)
|
self.logError(self.importSource)
|
||||||
|
|
||||||
def get_encoding(self):
|
def getEncoding(self):
|
||||||
"""
|
"""
|
||||||
Detect character encoding of an openlp.org 1.x song database.
|
Detect character encoding of an openlp.org 1.x song database.
|
||||||
"""
|
"""
|
||||||
# Connect to the database.
|
# Connect to the database.
|
||||||
connection = sqlite.connect(self.import_source, mode=0444)
|
connection = sqlite.connect(self.importSource, mode=0444)
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
|
||||||
detector = UniversalDetector()
|
detector = UniversalDetector()
|
||||||
@ -186,3 +190,22 @@ class OpenLP1SongImport(SongImport):
|
|||||||
return detector.result[u'encoding']
|
return detector.result[u'encoding']
|
||||||
detector.close()
|
detector.close()
|
||||||
return retrieve_windows_encoding(detector.result[u'encoding'])
|
return retrieve_windows_encoding(detector.result[u'encoding'])
|
||||||
|
|
||||||
|
def expandMediaFile(self, filename):
|
||||||
|
"""
|
||||||
|
When you're on Windows, this function expands the file name to include
|
||||||
|
the path to OpenLP's application data directory. If you are not on
|
||||||
|
Windows, it returns the original file name.
|
||||||
|
|
||||||
|
``filename``
|
||||||
|
The filename to expand.
|
||||||
|
"""
|
||||||
|
if sys.platform != u'win32' and \
|
||||||
|
not os.environ.get(u'ALLUSERSPROFILE') and \
|
||||||
|
not os.environ.get(u'APPDATA'):
|
||||||
|
return filename
|
||||||
|
common_app_data = os.path.join(os.environ[u'ALLUSERSPROFILE'],
|
||||||
|
os.path.split(os.environ[u'APPDATA'])[1])
|
||||||
|
if not common_app_data:
|
||||||
|
return filename
|
||||||
|
return os.path.join(common_app_data, u'openlp.org', 'Audio', filename)
|
||||||
|
@ -95,22 +95,22 @@ class OpenLPSongImport(SongImport):
|
|||||||
The database providing the data to import.
|
The database providing the data to import.
|
||||||
"""
|
"""
|
||||||
SongImport.__init__(self, manager, **kwargs)
|
SongImport.__init__(self, manager, **kwargs)
|
||||||
self.source_session = None
|
self.sourceSession = None
|
||||||
|
|
||||||
def do_import(self):
|
def doImport(self):
|
||||||
"""
|
"""
|
||||||
Run the import for an OpenLP version 2 song database.
|
Run the import for an OpenLP version 2 song database.
|
||||||
"""
|
"""
|
||||||
if not self.import_source.endswith(u'.sqlite'):
|
if not self.importSource.endswith(u'.sqlite'):
|
||||||
self.log_error(self.import_source,
|
self.logError(self.importSource,
|
||||||
translate('SongsPlugin.OpenLPSongImport',
|
translate('SongsPlugin.OpenLPSongImport',
|
||||||
'Not a valid OpenLP 2.0 song database.'))
|
'Not a valid OpenLP 2.0 song database.'))
|
||||||
return
|
return
|
||||||
self.import_source = u'sqlite:///%s' % self.import_source
|
self.importSource = u'sqlite:///%s' % self.importSource
|
||||||
engine = create_engine(self.import_source)
|
engine = create_engine(self.importSource)
|
||||||
source_meta = MetaData()
|
source_meta = MetaData()
|
||||||
source_meta.reflect(engine)
|
source_meta.reflect(engine)
|
||||||
self.source_session = scoped_session(sessionmaker(bind=engine))
|
self.sourceSession = scoped_session(sessionmaker(bind=engine))
|
||||||
if u'media_files' in source_meta.tables.keys():
|
if u'media_files' in source_meta.tables.keys():
|
||||||
has_media_files = True
|
has_media_files = True
|
||||||
else:
|
else:
|
||||||
@ -156,9 +156,9 @@ class OpenLPSongImport(SongImport):
|
|||||||
except UnmappedClassError:
|
except UnmappedClassError:
|
||||||
mapper(OldTopic, source_topics_table)
|
mapper(OldTopic, source_topics_table)
|
||||||
|
|
||||||
source_songs = self.source_session.query(OldSong).all()
|
source_songs = self.sourceSession.query(OldSong).all()
|
||||||
if self.import_wizard:
|
if self.importWizard:
|
||||||
self.import_wizard.progressBar.setMaximum(len(source_songs))
|
self.importWizard.progressBar.setMaximum(len(source_songs))
|
||||||
for song in source_songs:
|
for song in source_songs:
|
||||||
new_song = Song()
|
new_song = Song()
|
||||||
new_song.title = song.title
|
new_song.title = song.title
|
||||||
@ -201,22 +201,22 @@ class OpenLPSongImport(SongImport):
|
|||||||
if existing_topic is None:
|
if existing_topic is None:
|
||||||
existing_topic = Topic.populate(name=topic.name)
|
existing_topic = Topic.populate(name=topic.name)
|
||||||
new_song.topics.append(existing_topic)
|
new_song.topics.append(existing_topic)
|
||||||
# if has_media_files:
|
if has_media_files:
|
||||||
# if song.media_files:
|
if song.media_files:
|
||||||
# for media_file in song.media_files:
|
for media_file in song.media_files:
|
||||||
# existing_media_file = \
|
existing_media_file = \
|
||||||
# self.manager.get_object_filtered(MediaFile,
|
self.manager.get_object_filtered(MediaFile,
|
||||||
# MediaFile.file_name == media_file.file_name)
|
MediaFile.file_name == media_file.file_name)
|
||||||
# if existing_media_file:
|
if existing_media_file:
|
||||||
# new_song.media_files.append(existing_media_file)
|
new_song.media_files.append(existing_media_file)
|
||||||
# else:
|
else:
|
||||||
# new_song.media_files.append(MediaFile.populate(
|
new_song.media_files.append(MediaFile.populate(
|
||||||
# file_name=media_file.file_name))
|
file_name=media_file.file_name))
|
||||||
clean_song(self.manager, new_song)
|
clean_song(self.manager, new_song)
|
||||||
self.manager.save_object(new_song)
|
self.manager.save_object(new_song)
|
||||||
if self.import_wizard:
|
if self.importWizard:
|
||||||
self.import_wizard.incrementProgressBar(
|
self.importWizard.incrementProgressBar(
|
||||||
WizardStrings.ImportingType % new_song.title)
|
WizardStrings.ImportingType % new_song.title)
|
||||||
if self.stop_import_flag:
|
if self.stopImportFlag:
|
||||||
break
|
break
|
||||||
engine.dispose()
|
engine.dispose()
|
||||||
|
@ -59,58 +59,58 @@ class OooImport(SongImport):
|
|||||||
"""
|
"""
|
||||||
SongImport.__init__(self, manager, **kwargs)
|
SongImport.__init__(self, manager, **kwargs)
|
||||||
self.document = None
|
self.document = None
|
||||||
self.process_started = False
|
self.processStarted = False
|
||||||
|
|
||||||
def do_import(self):
|
def doImport(self):
|
||||||
if not isinstance(self.import_source, list):
|
if not isinstance(self.importSource, list):
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
self.start_ooo()
|
self.start_ooo()
|
||||||
except NoConnectException as exc:
|
except NoConnectException as exc:
|
||||||
self.log_error(
|
self.logError(
|
||||||
self.import_source[0],
|
self.importSource[0],
|
||||||
translate('SongsPlugin.SongImport',
|
translate('SongsPlugin.SongImport',
|
||||||
'Cannot access OpenOffice or LibreOffice'))
|
'Cannot access OpenOffice or LibreOffice'))
|
||||||
log.error(exc)
|
log.error(exc)
|
||||||
return
|
return
|
||||||
self.import_wizard.progressBar.setMaximum(len(self.import_source))
|
self.importWizard.progressBar.setMaximum(len(self.importSource))
|
||||||
for filename in self.import_source:
|
for filename in self.importSource:
|
||||||
if self.stop_import_flag:
|
if self.stopImportFlag:
|
||||||
break
|
break
|
||||||
filename = unicode(filename)
|
filename = unicode(filename)
|
||||||
if os.path.isfile(filename):
|
if os.path.isfile(filename):
|
||||||
self.open_ooo_file(filename)
|
self.openOooFile(filename)
|
||||||
if self.document:
|
if self.document:
|
||||||
self.process_ooo_document()
|
self.processOooDocument()
|
||||||
self.close_ooo_file()
|
self.closeOooFile()
|
||||||
else:
|
else:
|
||||||
self.log_error(self.filepath,
|
self.logError(self.filepath,
|
||||||
translate('SongsPlugin.SongImport',
|
translate('SongsPlugin.SongImport',
|
||||||
'Unable to open file'))
|
'Unable to open file'))
|
||||||
else:
|
else:
|
||||||
self.log_error(self.filepath,
|
self.logError(self.filepath,
|
||||||
translate('SongsPlugin.SongImport', 'File not found'))
|
translate('SongsPlugin.SongImport', 'File not found'))
|
||||||
self.close_ooo()
|
self.closeOoo()
|
||||||
|
|
||||||
def process_ooo_document(self):
|
def processOooDocument(self):
|
||||||
"""
|
"""
|
||||||
Handle the import process for OpenOffice files. This method facilitates
|
Handle the import process for OpenOffice files. This method facilitates
|
||||||
allowing subclasses to handle specific types of OpenOffice files.
|
allowing subclasses to handle specific types of OpenOffice files.
|
||||||
"""
|
"""
|
||||||
if self.document.supportsService(
|
if self.document.supportsService(
|
||||||
"com.sun.star.presentation.PresentationDocument"):
|
"com.sun.star.presentation.PresentationDocument"):
|
||||||
self.process_pres()
|
self.processPres()
|
||||||
if self.document.supportsService("com.sun.star.text.TextDocument"):
|
if self.document.supportsService("com.sun.star.text.TextDocument"):
|
||||||
self.process_doc()
|
self.processDoc()
|
||||||
|
|
||||||
def start_ooo(self):
|
def startOoo(self):
|
||||||
"""
|
"""
|
||||||
Start OpenOffice.org process
|
Start OpenOffice.org process
|
||||||
TODO: The presentation/Impress plugin may already have it running
|
TODO: The presentation/Impress plugin may already have it running
|
||||||
"""
|
"""
|
||||||
if os.name == u'nt':
|
if os.name == u'nt':
|
||||||
self.start_ooo_process()
|
self.startOooProcess()
|
||||||
self.desktop = self.ooo_manager.createInstance(
|
self.desktop = self.oooManager.createInstance(
|
||||||
u'com.sun.star.frame.Desktop')
|
u'com.sun.star.frame.Desktop')
|
||||||
else:
|
else:
|
||||||
context = uno.getComponentContext()
|
context = uno.getComponentContext()
|
||||||
@ -123,7 +123,7 @@ class OooImport(SongImport):
|
|||||||
uno_instance = get_uno_instance(resolver)
|
uno_instance = get_uno_instance(resolver)
|
||||||
except NoConnectException:
|
except NoConnectException:
|
||||||
log.exception("Failed to resolve uno connection")
|
log.exception("Failed to resolve uno connection")
|
||||||
self.start_ooo_process()
|
self.startOooProcess()
|
||||||
loop += 1
|
loop += 1
|
||||||
else:
|
else:
|
||||||
manager = uno_instance.ServiceManager
|
manager = uno_instance.ServiceManager
|
||||||
@ -132,22 +132,22 @@ class OooImport(SongImport):
|
|||||||
return
|
return
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def start_ooo_process(self):
|
def startOooProcess(self):
|
||||||
try:
|
try:
|
||||||
if os.name == u'nt':
|
if os.name == u'nt':
|
||||||
self.ooo_manager = Dispatch(u'com.sun.star.ServiceManager')
|
self.oooManager = Dispatch(u'com.sun.star.ServiceManager')
|
||||||
self.ooo_manager._FlagAsMethod(u'Bridge_GetStruct')
|
self.oooManager._FlagAsMethod(u'Bridge_GetStruct')
|
||||||
self.ooo_manager._FlagAsMethod(u'Bridge_GetValueObject')
|
self.oooManager._FlagAsMethod(u'Bridge_GetValueObject')
|
||||||
else:
|
else:
|
||||||
cmd = get_uno_command()
|
cmd = get_uno_command()
|
||||||
process = QtCore.QProcess()
|
process = QtCore.QProcess()
|
||||||
process.startDetached(cmd)
|
process.startDetached(cmd)
|
||||||
process.waitForStarted()
|
process.waitForStarted()
|
||||||
self.process_started = True
|
self.processStarted = True
|
||||||
except:
|
except:
|
||||||
log.exception("start_ooo_process failed")
|
log.exception("start_ooo_process failed")
|
||||||
|
|
||||||
def open_ooo_file(self, filepath):
|
def openOooFile(self, filepath):
|
||||||
"""
|
"""
|
||||||
Open the passed file in OpenOffice.org Impress
|
Open the passed file in OpenOffice.org Impress
|
||||||
"""
|
"""
|
||||||
@ -166,29 +166,29 @@ class OooImport(SongImport):
|
|||||||
if not self.document.supportsService(
|
if not self.document.supportsService(
|
||||||
"com.sun.star.presentation.PresentationDocument") and not \
|
"com.sun.star.presentation.PresentationDocument") and not \
|
||||||
self.document.supportsService("com.sun.star.text.TextDocument"):
|
self.document.supportsService("com.sun.star.text.TextDocument"):
|
||||||
self.close_ooo_file()
|
self.closeOooFile()
|
||||||
else:
|
else:
|
||||||
self.import_wizard.incrementProgressBar(
|
self.importWizard.incrementProgressBar(
|
||||||
u'Processing file ' + filepath, 0)
|
u'Processing file ' + filepath, 0)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
log.exception("open_ooo_file failed: %s", url)
|
log.exception("open_ooo_file failed: %s", url)
|
||||||
return
|
return
|
||||||
|
|
||||||
def close_ooo_file(self):
|
def closeOooFile(self):
|
||||||
"""
|
"""
|
||||||
Close file.
|
Close file.
|
||||||
"""
|
"""
|
||||||
self.document.close(True)
|
self.document.close(True)
|
||||||
self.document = None
|
self.document = None
|
||||||
|
|
||||||
def close_ooo(self):
|
def closeOoo(self):
|
||||||
"""
|
"""
|
||||||
Close OOo. But only if we started it and not on windows
|
Close OOo. But only if we started it and not on windows
|
||||||
"""
|
"""
|
||||||
if self.process_started:
|
if self.processStarted:
|
||||||
self.desktop.terminate()
|
self.desktop.terminate()
|
||||||
|
|
||||||
def process_pres(self):
|
def processPres(self):
|
||||||
"""
|
"""
|
||||||
Process the file
|
Process the file
|
||||||
"""
|
"""
|
||||||
@ -196,8 +196,8 @@ class OooImport(SongImport):
|
|||||||
slides = doc.getDrawPages()
|
slides = doc.getDrawPages()
|
||||||
text = u''
|
text = u''
|
||||||
for slide_no in range(slides.getCount()):
|
for slide_no in range(slides.getCount()):
|
||||||
if self.stop_import_flag:
|
if self.stopImportFlag:
|
||||||
self.import_wizard.incrementProgressBar(u'Import cancelled', 0)
|
self.importWizard.incrementProgressBar(u'Import cancelled', 0)
|
||||||
return
|
return
|
||||||
slide = slides.getByIndex(slide_no)
|
slide = slides.getByIndex(slide_no)
|
||||||
slidetext = u''
|
slidetext = u''
|
||||||
@ -209,10 +209,10 @@ class OooImport(SongImport):
|
|||||||
if slidetext.strip() == u'':
|
if slidetext.strip() == u'':
|
||||||
slidetext = u'\f'
|
slidetext = u'\f'
|
||||||
text += slidetext
|
text += slidetext
|
||||||
self.process_songs_text(text)
|
self.processSongsText(text)
|
||||||
return
|
return
|
||||||
|
|
||||||
def process_doc(self):
|
def processDoc(self):
|
||||||
"""
|
"""
|
||||||
Process the doc file, a paragraph at a time
|
Process the doc file, a paragraph at a time
|
||||||
"""
|
"""
|
||||||
@ -231,16 +231,16 @@ class OooImport(SongImport):
|
|||||||
if textportion.BreakType in (PAGE_AFTER, PAGE_BOTH):
|
if textportion.BreakType in (PAGE_AFTER, PAGE_BOTH):
|
||||||
paratext += u'\f'
|
paratext += u'\f'
|
||||||
text += paratext + u'\n'
|
text += paratext + u'\n'
|
||||||
self.process_songs_text(text)
|
self.processSongsText(text)
|
||||||
|
|
||||||
def process_songs_text(self, text):
|
def processSongsText(self, text):
|
||||||
songtexts = self.tidy_text(text).split(u'\f')
|
songtexts = self.tidyText(text).split(u'\f')
|
||||||
self.set_defaults()
|
self.setDefaults()
|
||||||
for songtext in songtexts:
|
for songtext in songtexts:
|
||||||
if songtext.strip():
|
if songtext.strip():
|
||||||
self.process_song_text(songtext.strip())
|
self.processSongText(songtext.strip())
|
||||||
if self.check_complete():
|
if self.checkComplete():
|
||||||
self.finish()
|
self.finish()
|
||||||
self.set_defaults()
|
self.setDefaults()
|
||||||
if self.check_complete():
|
if self.checkComplete():
|
||||||
self.finish()
|
self.finish()
|
||||||
|
@ -53,16 +53,16 @@ class OpenLyricsImport(SongImport):
|
|||||||
SongImport.__init__(self, manager, **kwargs)
|
SongImport.__init__(self, manager, **kwargs)
|
||||||
self.openLyrics = OpenLyrics(self.manager)
|
self.openLyrics = OpenLyrics(self.manager)
|
||||||
|
|
||||||
def do_import(self):
|
def doImport(self):
|
||||||
"""
|
"""
|
||||||
Imports the songs.
|
Imports the songs.
|
||||||
"""
|
"""
|
||||||
self.import_wizard.progressBar.setMaximum(len(self.import_source))
|
self.importWizard.progressBar.setMaximum(len(self.importSource))
|
||||||
parser = etree.XMLParser(remove_blank_text=True)
|
parser = etree.XMLParser(remove_blank_text=True)
|
||||||
for file_path in self.import_source:
|
for file_path in self.importSource:
|
||||||
if self.stop_import_flag:
|
if self.stopImportFlag:
|
||||||
return
|
return
|
||||||
self.import_wizard.incrementProgressBar(
|
self.importWizard.incrementProgressBar(
|
||||||
WizardStrings.ImportingType % os.path.basename(file_path))
|
WizardStrings.ImportingType % os.path.basename(file_path))
|
||||||
try:
|
try:
|
||||||
# Pass a file object, because lxml does not cope with some
|
# Pass a file object, because lxml does not cope with some
|
||||||
@ -72,4 +72,4 @@ class OpenLyricsImport(SongImport):
|
|||||||
self.openLyrics.xml_to_song(xml)
|
self.openLyrics.xml_to_song(xml)
|
||||||
except etree.XMLSyntaxError:
|
except etree.XMLSyntaxError:
|
||||||
log.exception(u'XML syntax error in file %s' % file_path)
|
log.exception(u'XML syntax error in file %s' % file_path)
|
||||||
self.log_error(file_path, SongStrings.XMLSyntaxError)
|
self.logError(file_path, SongStrings.XMLSyntaxError)
|
||||||
|
@ -107,32 +107,32 @@ class OpenSongImport(SongImport):
|
|||||||
"""
|
"""
|
||||||
SongImport.__init__(self, manager, **kwargs)
|
SongImport.__init__(self, manager, **kwargs)
|
||||||
|
|
||||||
def do_import(self):
|
def doImport(self):
|
||||||
self.import_wizard.progressBar.setMaximum(len(self.import_source))
|
self.importWizard.progressBar.setMaximum(len(self.importSource))
|
||||||
for filename in self.import_source:
|
for filename in self.importSource:
|
||||||
if self.stop_import_flag:
|
if self.stopImportFlag:
|
||||||
return
|
return
|
||||||
song_file = open(filename)
|
song_file = open(filename)
|
||||||
self.do_import_file(song_file)
|
self.doImportFile(song_file)
|
||||||
song_file.close()
|
song_file.close()
|
||||||
|
|
||||||
def do_import_file(self, file):
|
def doImportFile(self, file):
|
||||||
"""
|
"""
|
||||||
Process the OpenSong file - pass in a file-like object, not a file path.
|
Process the OpenSong file - pass in a file-like object, not a file path.
|
||||||
"""
|
"""
|
||||||
self.set_defaults()
|
self.setDefaults()
|
||||||
try:
|
try:
|
||||||
tree = objectify.parse(file)
|
tree = objectify.parse(file)
|
||||||
except (Error, LxmlError):
|
except (Error, LxmlError):
|
||||||
self.log_error(file.name, SongStrings.XMLSyntaxError)
|
self.logError(file.name, SongStrings.XMLSyntaxError)
|
||||||
log.exception(u'Error parsing XML')
|
log.exception(u'Error parsing XML')
|
||||||
return
|
return
|
||||||
root = tree.getroot()
|
root = tree.getroot()
|
||||||
fields = dir(root)
|
fields = dir(root)
|
||||||
decode = {
|
decode = {
|
||||||
u'copyright': self.add_copyright,
|
u'copyright': self.addCopyright,
|
||||||
u'ccli': u'ccli_number',
|
u'ccli': u'ccli_number',
|
||||||
u'author': self.parse_author,
|
u'author': self.parseAuthor,
|
||||||
u'title': u'title',
|
u'title': u'title',
|
||||||
u'aka': u'alternate_title',
|
u'aka': u'alternate_title',
|
||||||
u'hymn_number': u'song_number'
|
u'hymn_number': u'song_number'
|
||||||
@ -190,7 +190,10 @@ class OpenSongImport(SongImport):
|
|||||||
# the verse tag
|
# the verse tag
|
||||||
verse_tag = content
|
verse_tag = content
|
||||||
verse_num = u'1'
|
verse_num = u'1'
|
||||||
verse_index = VerseType.from_loose_input(verse_tag)
|
if len(verse_tag) == 0:
|
||||||
|
verse_index = 0
|
||||||
|
else:
|
||||||
|
verse_index = VerseType.from_loose_input(verse_tag)
|
||||||
verse_tag = VerseType.Tags[verse_index]
|
verse_tag = VerseType.Tags[verse_index]
|
||||||
inst = 1
|
inst = 1
|
||||||
if [verse_tag, verse_num, inst] in our_verse_order \
|
if [verse_tag, verse_num, inst] in our_verse_order \
|
||||||
@ -211,7 +214,7 @@ class OpenSongImport(SongImport):
|
|||||||
verses[verse_tag][verse_num][inst] = []
|
verses[verse_tag][verse_num][inst] = []
|
||||||
our_verse_order.append([verse_tag, verse_num, inst])
|
our_verse_order.append([verse_tag, verse_num, inst])
|
||||||
# Tidy text and remove the ____s from extended words
|
# Tidy text and remove the ____s from extended words
|
||||||
this_line = self.tidy_text(this_line)
|
this_line = self.tidyText(this_line)
|
||||||
this_line = this_line.replace(u'_', u'')
|
this_line = this_line.replace(u'_', u'')
|
||||||
this_line = this_line.replace(u'|', u'\n')
|
this_line = this_line.replace(u'|', u'\n')
|
||||||
verses[verse_tag][verse_num][inst].append(this_line)
|
verses[verse_tag][verse_num][inst].append(this_line)
|
||||||
@ -220,9 +223,9 @@ class OpenSongImport(SongImport):
|
|||||||
for (verse_tag, verse_num, inst) in our_verse_order:
|
for (verse_tag, verse_num, inst) in our_verse_order:
|
||||||
verse_def = u'%s%s' % (verse_tag, verse_num)
|
verse_def = u'%s%s' % (verse_tag, verse_num)
|
||||||
lines = u'\n'.join(verses[verse_tag][verse_num][inst])
|
lines = u'\n'.join(verses[verse_tag][verse_num][inst])
|
||||||
self.add_verse(lines, verse_def)
|
self.addVerse(lines, verse_def)
|
||||||
if not self.verses:
|
if not self.verses:
|
||||||
self.add_verse('')
|
self.addVerse('')
|
||||||
# figure out the presentation order, if present
|
# figure out the presentation order, if present
|
||||||
if u'presentation' in fields and root.presentation:
|
if u'presentation' in fields and root.presentation:
|
||||||
order = unicode(root.presentation)
|
order = unicode(root.presentation)
|
||||||
@ -243,9 +246,9 @@ class OpenSongImport(SongImport):
|
|||||||
verse_def = u'%s%s' % (verse_tag, verse_num)
|
verse_def = u'%s%s' % (verse_tag, verse_num)
|
||||||
if verses.has_key(verse_tag) and \
|
if verses.has_key(verse_tag) and \
|
||||||
verses[verse_tag].has_key(verse_num):
|
verses[verse_tag].has_key(verse_num):
|
||||||
self.verse_order_list.append(verse_def)
|
self.verseOrderList.append(verse_def)
|
||||||
else:
|
else:
|
||||||
log.info(u'Got order %s but not in verse tags, dropping'
|
log.info(u'Got order %s but not in verse tags, dropping'
|
||||||
u'this item from presentation order', verse_def)
|
u'this item from presentation order', verse_def)
|
||||||
if not self.finish():
|
if not self.finish():
|
||||||
self.log_error(file.name)
|
self.logError(file.name)
|
||||||
|
@ -83,32 +83,32 @@ class SofImport(OooImport):
|
|||||||
OooImport.__init__(self, manager, **kwargs)
|
OooImport.__init__(self, manager, **kwargs)
|
||||||
self.song = False
|
self.song = False
|
||||||
|
|
||||||
def process_ooo_document(self):
|
def processOooDocument(self):
|
||||||
"""
|
"""
|
||||||
Handle the import process for SoF files.
|
Handle the import process for SoF files.
|
||||||
"""
|
"""
|
||||||
self.process_sof_file()
|
self.processSofFile()
|
||||||
|
|
||||||
def process_sof_file(self):
|
def processSofFile(self):
|
||||||
"""
|
"""
|
||||||
Process the RTF file, a paragraph at a time
|
Process the RTF file, a paragraph at a time
|
||||||
"""
|
"""
|
||||||
self.blanklines = 0
|
self.blankLines = 0
|
||||||
self.new_song()
|
self.newSong()
|
||||||
try:
|
try:
|
||||||
paragraphs = self.document.getText().createEnumeration()
|
paragraphs = self.document.getText().createEnumeration()
|
||||||
while paragraphs.hasMoreElements():
|
while paragraphs.hasMoreElements():
|
||||||
if self.stop_import_flag:
|
if self.stopImportFlag:
|
||||||
return
|
return
|
||||||
paragraph = paragraphs.nextElement()
|
paragraph = paragraphs.nextElement()
|
||||||
if paragraph.supportsService("com.sun.star.text.Paragraph"):
|
if paragraph.supportsService("com.sun.star.text.Paragraph"):
|
||||||
self.process_paragraph(paragraph)
|
self.processParagraph(paragraph)
|
||||||
except RuntimeException as exc:
|
except RuntimeException as exc:
|
||||||
log.exception(u'Error processing file: %s', exc)
|
log.exception(u'Error processing file: %s', exc)
|
||||||
if not self.finish():
|
if not self.finish():
|
||||||
self.log_error(self.filepath)
|
self.logError(self.filepath)
|
||||||
|
|
||||||
def process_paragraph(self, paragraph):
|
def processParagraph(self, paragraph):
|
||||||
"""
|
"""
|
||||||
Process a paragraph.
|
Process a paragraph.
|
||||||
In the first book, a paragraph is a single line. In the latter ones
|
In the first book, a paragraph is a single line. In the latter ones
|
||||||
@ -124,26 +124,26 @@ class SofImport(OooImport):
|
|||||||
while textportions.hasMoreElements():
|
while textportions.hasMoreElements():
|
||||||
textportion = textportions.nextElement()
|
textportion = textportions.nextElement()
|
||||||
if textportion.BreakType in (PAGE_BEFORE, PAGE_BOTH):
|
if textportion.BreakType in (PAGE_BEFORE, PAGE_BOTH):
|
||||||
self.process_paragraph_text(text)
|
self.processParagraphText(text)
|
||||||
self.new_song()
|
self.newSong()
|
||||||
text = u''
|
text = u''
|
||||||
text += self.process_textportion(textportion)
|
text += self.process_textportion(textportion)
|
||||||
if textportion.BreakType in (PAGE_AFTER, PAGE_BOTH):
|
if textportion.BreakType in (PAGE_AFTER, PAGE_BOTH):
|
||||||
self.process_paragraph_text(text)
|
self.processParagraphText(text)
|
||||||
self.new_song()
|
self.newSong()
|
||||||
text = u''
|
text = u''
|
||||||
self.process_paragraph_text(text)
|
self.processParagraphText(text)
|
||||||
|
|
||||||
def process_paragraph_text(self, text):
|
def processParagraphText(self, text):
|
||||||
"""
|
"""
|
||||||
Split the paragraph text into multiple lines and process
|
Split the paragraph text into multiple lines and process
|
||||||
"""
|
"""
|
||||||
for line in text.split(u'\n'):
|
for line in text.split(u'\n'):
|
||||||
self.process_paragraph_line(line)
|
self.processParagraphLine(line)
|
||||||
if self.blanklines > 2:
|
if self.blankLines > 2:
|
||||||
self.new_song()
|
self.newSong()
|
||||||
|
|
||||||
def process_paragraph_line(self, text):
|
def processParagraphLine(self, text):
|
||||||
"""
|
"""
|
||||||
Process a single line. Throw away that text which isn't relevant, i.e.
|
Process a single line. Throw away that text which isn't relevant, i.e.
|
||||||
stuff that appears at the end of the song.
|
stuff that appears at the end of the song.
|
||||||
@ -151,16 +151,16 @@ class SofImport(OooImport):
|
|||||||
"""
|
"""
|
||||||
text = text.strip()
|
text = text.strip()
|
||||||
if text == u'':
|
if text == u'':
|
||||||
self.blanklines += 1
|
self.blankLines += 1
|
||||||
if self.blanklines > 1:
|
if self.blankLines > 1:
|
||||||
return
|
return
|
||||||
if self.title != u'':
|
if self.title != u'':
|
||||||
self.finish_verse()
|
self.finishVerse()
|
||||||
return
|
return
|
||||||
self.blanklines = 0
|
self.blankLines = 0
|
||||||
if self.skip_to_close_bracket:
|
if self.skipToCloseBracket:
|
||||||
if text.endswith(u')'):
|
if text.endswith(u')'):
|
||||||
self.skip_to_close_bracket = False
|
self.skipToCloseBracket = False
|
||||||
return
|
return
|
||||||
if text.startswith(u'CCL Licence'):
|
if text.startswith(u'CCL Licence'):
|
||||||
self.italics = False
|
self.italics = False
|
||||||
@ -169,24 +169,24 @@ class SofImport(OooImport):
|
|||||||
return
|
return
|
||||||
if text.startswith(u'(NB.') or text.startswith(u'(Regrettably') \
|
if text.startswith(u'(NB.') or text.startswith(u'(Regrettably') \
|
||||||
or text.startswith(u'(From'):
|
or text.startswith(u'(From'):
|
||||||
self.skip_to_close_bracket = True
|
self.skipToCloseBracket = True
|
||||||
return
|
return
|
||||||
if text.startswith(u'Copyright'):
|
if text.startswith(u'Copyright'):
|
||||||
self.add_copyright(text)
|
self.addCopyright(text)
|
||||||
return
|
return
|
||||||
if text == u'(Repeat)':
|
if text == u'(Repeat)':
|
||||||
self.finish_verse()
|
self.finishVerse()
|
||||||
self.repeat_verse()
|
self.repeatVerse()
|
||||||
return
|
return
|
||||||
if self.title == u'':
|
if self.title == u'':
|
||||||
if self.copyright == u'':
|
if self.copyright == u'':
|
||||||
self.add_sof_author(text)
|
self.addSofAuthor(text)
|
||||||
else:
|
else:
|
||||||
self.add_copyright(text)
|
self.addCopyright(text)
|
||||||
return
|
return
|
||||||
self.add_verse_line(text)
|
self.addVerseLine(text)
|
||||||
|
|
||||||
def process_textportion(self, textportion):
|
def processTextPortion(self, textportion):
|
||||||
"""
|
"""
|
||||||
Process a text portion. Here we just get the text and detect if
|
Process a text portion. Here we just get the text and detect if
|
||||||
it's bold or italics. If it's bold then its a song number or song title.
|
it's bold or italics. If it's bold then its a song number or song title.
|
||||||
@ -199,53 +199,53 @@ class SofImport(OooImport):
|
|||||||
return text
|
return text
|
||||||
if textportion.CharWeight == BOLD:
|
if textportion.CharWeight == BOLD:
|
||||||
boldtext = text.strip()
|
boldtext = text.strip()
|
||||||
if boldtext.isdigit() and self.song_number == '':
|
if boldtext.isdigit() and self.songNumber == '':
|
||||||
self.add_songnumber(boldtext)
|
self.addSongNumber(boldtext)
|
||||||
return u''
|
return u''
|
||||||
if self.title == u'':
|
if self.title == u'':
|
||||||
text = self.uncap_text(text)
|
text = self.uncap_text(text)
|
||||||
self.add_title(text)
|
self.addTitle(text)
|
||||||
return text
|
return text
|
||||||
if text.strip().startswith(u'('):
|
if text.strip().startswith(u'('):
|
||||||
return text
|
return text
|
||||||
self.italics = (textportion.CharPosture == ITALIC)
|
self.italics = (textportion.CharPosture == ITALIC)
|
||||||
return text
|
return text
|
||||||
|
|
||||||
def new_song(self):
|
def newSong(self):
|
||||||
"""
|
"""
|
||||||
A change of song. Store the old, create a new
|
A change of song. Store the old, create a new
|
||||||
... but only if the last song was complete. If not, stick with it
|
... but only if the last song was complete. If not, stick with it
|
||||||
"""
|
"""
|
||||||
if self.song:
|
if self.song:
|
||||||
self.finish_verse()
|
self.finishVerse()
|
||||||
if not self.check_complete():
|
if not self.checkComplete():
|
||||||
return
|
return
|
||||||
self.finish()
|
self.finish()
|
||||||
self.song = True
|
self.song = True
|
||||||
self.set_defaults()
|
self.setDefaults()
|
||||||
self.skip_to_close_bracket = False
|
self.skipToCloseBracket = False
|
||||||
self.is_chorus = False
|
self.isChorus = False
|
||||||
self.italics = False
|
self.italics = False
|
||||||
self.currentverse = u''
|
self.currentVerse = u''
|
||||||
|
|
||||||
def add_songnumber(self, song_no):
|
def addSongNumber(self, song_no):
|
||||||
"""
|
"""
|
||||||
Add a song number, store as alternate title. Also use the song
|
Add a song number, store as alternate title. Also use the song
|
||||||
number to work out which songbook we're in
|
number to work out which songbook we're in
|
||||||
"""
|
"""
|
||||||
self.song_number = song_no
|
self.songNumber = song_no
|
||||||
self.alternate_title = song_no + u'.'
|
self.alternateTitle = song_no + u'.'
|
||||||
self.song_book_pub = u'Kingsway Publications'
|
self.songBook_pub = u'Kingsway Publications'
|
||||||
if int(song_no) <= 640:
|
if int(song_no) <= 640:
|
||||||
self.song_book = u'Songs of Fellowship 1'
|
self.songBook = u'Songs of Fellowship 1'
|
||||||
elif int(song_no) <= 1150:
|
elif int(song_no) <= 1150:
|
||||||
self.song_book = u'Songs of Fellowship 2'
|
self.songBook = u'Songs of Fellowship 2'
|
||||||
elif int(song_no) <= 1690:
|
elif int(song_no) <= 1690:
|
||||||
self.song_book = u'Songs of Fellowship 3'
|
self.songBook = u'Songs of Fellowship 3'
|
||||||
else:
|
else:
|
||||||
self.song_book = u'Songs of Fellowship 4'
|
self.songBook = u'Songs of Fellowship 4'
|
||||||
|
|
||||||
def add_title(self, text):
|
def addTitle(self, text):
|
||||||
"""
|
"""
|
||||||
Add the title to the song. Strip some leading/trailing punctuation that
|
Add the title to the song. Strip some leading/trailing punctuation that
|
||||||
we don't want in a title
|
we don't want in a title
|
||||||
@ -256,9 +256,9 @@ class SofImport(OooImport):
|
|||||||
if title.endswith(u','):
|
if title.endswith(u','):
|
||||||
title = title[:-1]
|
title = title[:-1]
|
||||||
self.title = title
|
self.title = title
|
||||||
self.import_wizard.incrementProgressBar(u'Processing song ' + title, 0)
|
self.importWizard.incrementProgressBar(u'Processing song ' + title, 0)
|
||||||
|
|
||||||
def add_sof_author(self, text):
|
def addSofAuthor(self, text):
|
||||||
"""
|
"""
|
||||||
Add the author. OpenLP stores them individually so split by 'and', '&'
|
Add the author. OpenLP stores them individually so split by 'and', '&'
|
||||||
and comma.
|
and comma.
|
||||||
@ -266,42 +266,42 @@ class SofImport(OooImport):
|
|||||||
"Mr Smith" and "Mrs Smith".
|
"Mr Smith" and "Mrs Smith".
|
||||||
"""
|
"""
|
||||||
text = text.replace(u' and ', u' & ')
|
text = text.replace(u' and ', u' & ')
|
||||||
self.parse_author(text)
|
self.parseAuthor(text)
|
||||||
|
|
||||||
def add_verse_line(self, text):
|
def addVerseLine(self, text):
|
||||||
"""
|
"""
|
||||||
Add a line to the current verse. If the formatting has changed and
|
Add a line to the current verse. If the formatting has changed and
|
||||||
we're beyond the second line of first verse, then this indicates
|
we're beyond the second line of first verse, then this indicates
|
||||||
a change of verse. Italics are a chorus
|
a change of verse. Italics are a chorus
|
||||||
"""
|
"""
|
||||||
if self.italics != self.is_chorus and ((len(self.verses) > 0) or
|
if self.italics != self.isChorus and ((len(self.verses) > 0) or
|
||||||
(self.currentverse.count(u'\n') > 1)):
|
(self.currentVerse.count(u'\n') > 1)):
|
||||||
self.finish_verse()
|
self.finishVerse()
|
||||||
if self.italics:
|
if self.italics:
|
||||||
self.is_chorus = True
|
self.isChorus = True
|
||||||
self.currentverse += text + u'\n'
|
self.currentVerse += text + u'\n'
|
||||||
|
|
||||||
def finish_verse(self):
|
def finishVerse(self):
|
||||||
"""
|
"""
|
||||||
Verse is finished, store it. Note in book 1+2, some songs are formatted
|
Verse is finished, store it. Note in book 1+2, some songs are formatted
|
||||||
incorrectly. Here we try and split songs with missing line breaks into
|
incorrectly. Here we try and split songs with missing line breaks into
|
||||||
the correct number of verses.
|
the correct number of verses.
|
||||||
"""
|
"""
|
||||||
if self.currentverse.strip() == u'':
|
if self.currentVerse.strip() == u'':
|
||||||
return
|
return
|
||||||
if self.is_chorus:
|
if self.isChorus:
|
||||||
versetag = u'C'
|
versetag = u'C'
|
||||||
splitat = None
|
splitat = None
|
||||||
else:
|
else:
|
||||||
versetag = u'V'
|
versetag = u'V'
|
||||||
splitat = self.verse_splits(self.song_number)
|
splitat = self.verseSplits(self.songNumber)
|
||||||
if splitat:
|
if splitat:
|
||||||
ln = 0
|
ln = 0
|
||||||
verse = u''
|
verse = u''
|
||||||
for line in self.currentverse.split(u'\n'):
|
for line in self.currentVerse.split(u'\n'):
|
||||||
ln += 1
|
ln += 1
|
||||||
if line == u'' or ln > splitat:
|
if line == u'' or ln > splitat:
|
||||||
self.add_sof_verse(verse, versetag)
|
self.addSofVerse(verse, versetag)
|
||||||
ln = 0
|
ln = 0
|
||||||
if line:
|
if line:
|
||||||
verse = line + u'\n'
|
verse = line + u'\n'
|
||||||
@ -310,19 +310,19 @@ class SofImport(OooImport):
|
|||||||
else:
|
else:
|
||||||
verse += line + u'\n'
|
verse += line + u'\n'
|
||||||
if verse:
|
if verse:
|
||||||
self.add_sof_verse(verse, versetag)
|
self.addSofVerse(verse, versetag)
|
||||||
else:
|
else:
|
||||||
self.add_sof_verse(self.currentverse, versetag)
|
self.addSofVerse(self.currentVerse, versetag)
|
||||||
self.currentverse = u''
|
self.currentVerse = u''
|
||||||
self.is_chorus = False
|
self.isChorus = False
|
||||||
|
|
||||||
def add_sof_verse(self, lyrics, tag):
|
def addSofVerse(self, lyrics, tag):
|
||||||
self.add_verse(lyrics, tag)
|
self.addVerse(lyrics, tag)
|
||||||
if not self.is_chorus and u'C1' in self.verse_order_list_generated:
|
if not self.isChorus and u'C1' in self.verseOrderListGenerated:
|
||||||
self.verse_order_list_generated.append(u'C1')
|
self.verseOrderListGenerated.append(u'C1')
|
||||||
self.verse_order_list_generated_useful = True
|
self.verseOrderListGenerated_useful = True
|
||||||
|
|
||||||
def uncap_text(self, text):
|
def uncapText(self, text):
|
||||||
"""
|
"""
|
||||||
Words in the title are in all capitals, so we lowercase them.
|
Words in the title are in all capitals, so we lowercase them.
|
||||||
However some of these words, e.g. referring to God need a leading
|
However some of these words, e.g. referring to God need a leading
|
||||||
@ -348,7 +348,7 @@ class SofImport(OooImport):
|
|||||||
text = u''.join(textarr)
|
text = u''.join(textarr)
|
||||||
return text
|
return text
|
||||||
|
|
||||||
def verse_splits(self, song_number):
|
def verseSplits(self, song_number):
|
||||||
"""
|
"""
|
||||||
Because someone at Kingsway forgot to check the 1+2 RTF file,
|
Because someone at Kingsway forgot to check the 1+2 RTF file,
|
||||||
some verses were not formatted correctly.
|
some verses were not formatted correctly.
|
||||||
|
@ -98,20 +98,20 @@ class SongBeamerImport(SongImport):
|
|||||||
"""
|
"""
|
||||||
SongImport.__init__(self, manager, **kwargs)
|
SongImport.__init__(self, manager, **kwargs)
|
||||||
|
|
||||||
def do_import(self):
|
def doImport(self):
|
||||||
"""
|
"""
|
||||||
Receive a single file or a list of files to import.
|
Receive a single file or a list of files to import.
|
||||||
"""
|
"""
|
||||||
self.import_wizard.progressBar.setMaximum(len(self.import_source))
|
self.importWizard.progressBar.setMaximum(len(self.importSource))
|
||||||
if not isinstance(self.import_source, list):
|
if not isinstance(self.importSource, list):
|
||||||
return
|
return
|
||||||
for file in self.import_source:
|
for file in self.importSource:
|
||||||
# TODO: check that it is a valid SongBeamer file
|
# TODO: check that it is a valid SongBeamer file
|
||||||
if self.stop_import_flag:
|
if self.stopImportFlag:
|
||||||
return
|
return
|
||||||
self.set_defaults()
|
self.setDefaults()
|
||||||
self.current_verse = u''
|
self.currentVerse = u''
|
||||||
self.current_verse_type = VerseType.Tags[VerseType.Verse]
|
self.currentVerseType = VerseType.Tags[VerseType.Verse]
|
||||||
read_verses = False
|
read_verses = False
|
||||||
file_name = os.path.split(file)[1]
|
file_name = os.path.split(file)[1]
|
||||||
if os.path.isfile(file):
|
if os.path.isfile(file):
|
||||||
@ -119,48 +119,48 @@ class SongBeamerImport(SongImport):
|
|||||||
details = chardet.detect(detect_file.read())
|
details = chardet.detect(detect_file.read())
|
||||||
detect_file.close()
|
detect_file.close()
|
||||||
infile = codecs.open(file, u'r', details['encoding'])
|
infile = codecs.open(file, u'r', details['encoding'])
|
||||||
songData = infile.readlines()
|
song_data = infile.readlines()
|
||||||
infile.close()
|
infile.close()
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
self.title = file_name.split('.sng')[0]
|
self.title = file_name.split('.sng')[0]
|
||||||
read_verses = False
|
read_verses = False
|
||||||
for line in songData:
|
for line in song_data:
|
||||||
# Just make sure that the line is of the type 'Unicode'.
|
# Just make sure that the line is of the type 'Unicode'.
|
||||||
line = unicode(line).strip()
|
line = unicode(line).strip()
|
||||||
if line.startswith(u'#') and not read_verses:
|
if line.startswith(u'#') and not read_verses:
|
||||||
self.parse_tags(line)
|
self.parseTags(line)
|
||||||
elif line.startswith(u'---'):
|
elif line.startswith(u'---'):
|
||||||
if self.current_verse:
|
if self.currentVerse:
|
||||||
self.replace_html_tags()
|
self.replaceHtmlTags()
|
||||||
self.add_verse(self.current_verse,
|
self.addVerse(self.currentVerse,
|
||||||
self.current_verse_type)
|
self.currentVerseType)
|
||||||
self.current_verse = u''
|
self.currentVerse = u''
|
||||||
self.current_verse_type = VerseType.Tags[VerseType.Verse]
|
self.currentVerseType = VerseType.Tags[VerseType.Verse]
|
||||||
read_verses = True
|
read_verses = True
|
||||||
verse_start = True
|
verse_start = True
|
||||||
elif read_verses:
|
elif read_verses:
|
||||||
if verse_start:
|
if verse_start:
|
||||||
verse_start = False
|
verse_start = False
|
||||||
if not self.check_verse_marks(line):
|
if not self.checkVerseMarks(line):
|
||||||
self.current_verse = line + u'\n'
|
self.currentVerse = line + u'\n'
|
||||||
else:
|
else:
|
||||||
self.current_verse += line + u'\n'
|
self.currentVerse += line + u'\n'
|
||||||
if self.current_verse:
|
if self.currentVerse:
|
||||||
self.replace_html_tags()
|
self.replaceHtmlTags()
|
||||||
self.add_verse(self.current_verse, self.current_verse_type)
|
self.addVerse(self.currentVerse, self.currentVerseType)
|
||||||
if not self.finish():
|
if not self.finish():
|
||||||
self.log_error(file)
|
self.logError(file)
|
||||||
|
|
||||||
def replace_html_tags(self):
|
def replaceHtmlTags(self):
|
||||||
"""
|
"""
|
||||||
This can be called to replace SongBeamer's specific (html) tags with
|
This can be called to replace SongBeamer's specific (html) tags with
|
||||||
OpenLP's specific (html) tags.
|
OpenLP's specific (html) tags.
|
||||||
"""
|
"""
|
||||||
for pair in SongBeamerImport.HTML_TAG_PAIRS:
|
for pair in SongBeamerImport.HTML_TAG_PAIRS:
|
||||||
self.current_verse = pair[0].sub(pair[1], self.current_verse)
|
self.currentVerse = pair[0].sub(pair[1], self.currentVerse)
|
||||||
|
|
||||||
def parse_tags(self, line):
|
def parseTags(self, line):
|
||||||
"""
|
"""
|
||||||
Parses a meta data line.
|
Parses a meta data line.
|
||||||
|
|
||||||
@ -176,11 +176,11 @@ class SongBeamerImport(SongImport):
|
|||||||
if not tag_val[0] or not tag_val[1]:
|
if not tag_val[0] or not tag_val[1]:
|
||||||
return
|
return
|
||||||
if tag_val[0] == u'#(c)':
|
if tag_val[0] == u'#(c)':
|
||||||
self.add_copyright(tag_val[1])
|
self.addCopyright(tag_val[1])
|
||||||
elif tag_val[0] == u'#AddCopyrightInfo':
|
elif tag_val[0] == u'#AddCopyrightInfo':
|
||||||
pass
|
pass
|
||||||
elif tag_val[0] == u'#Author':
|
elif tag_val[0] == u'#Author':
|
||||||
self.parse_author(tag_val[1])
|
self.parseAuthor(tag_val[1])
|
||||||
elif tag_val[0] == u'#BackgroundImage':
|
elif tag_val[0] == u'#BackgroundImage':
|
||||||
pass
|
pass
|
||||||
elif tag_val[0] == u'#Bible':
|
elif tag_val[0] == u'#Bible':
|
||||||
@ -188,7 +188,7 @@ class SongBeamerImport(SongImport):
|
|||||||
elif tag_val[0] == u'#Categories':
|
elif tag_val[0] == u'#Categories':
|
||||||
self.topics = tag_val[1].split(',')
|
self.topics = tag_val[1].split(',')
|
||||||
elif tag_val[0] == u'#CCLI':
|
elif tag_val[0] == u'#CCLI':
|
||||||
self.ccli_number = tag_val[1]
|
self.ccliNumber = tag_val[1]
|
||||||
elif tag_val[0] == u'#Chords':
|
elif tag_val[0] == u'#Chords':
|
||||||
pass
|
pass
|
||||||
elif tag_val[0] == u'#ChurchSongID':
|
elif tag_val[0] == u'#ChurchSongID':
|
||||||
@ -220,7 +220,7 @@ class SongBeamerImport(SongImport):
|
|||||||
elif tag_val[0] == u'#LangCount':
|
elif tag_val[0] == u'#LangCount':
|
||||||
pass
|
pass
|
||||||
elif tag_val[0] == u'#Melody':
|
elif tag_val[0] == u'#Melody':
|
||||||
self.parse_author(tag_val[1])
|
self.parseAuthor(tag_val[1])
|
||||||
elif tag_val[0] == u'#NatCopyright':
|
elif tag_val[0] == u'#NatCopyright':
|
||||||
pass
|
pass
|
||||||
elif tag_val[0] == u'#OTitle':
|
elif tag_val[0] == u'#OTitle':
|
||||||
@ -235,10 +235,10 @@ class SongBeamerImport(SongImport):
|
|||||||
song_book_pub = tag_val[1]
|
song_book_pub = tag_val[1]
|
||||||
elif tag_val[0] == u'#Songbook' or tag_val[0] == u'#SongBook':
|
elif tag_val[0] == u'#Songbook' or tag_val[0] == u'#SongBook':
|
||||||
book_data = tag_val[1].split(u'/')
|
book_data = tag_val[1].split(u'/')
|
||||||
self.song_book_name = book_data[0].strip()
|
self.songBookName = book_data[0].strip()
|
||||||
if len(book_data) == 2:
|
if len(book_data) == 2:
|
||||||
number = book_data[1].strip()
|
number = book_data[1].strip()
|
||||||
self.song_number = number if number.isdigit() else u''
|
self.songNumber = number if number.isdigit() else u''
|
||||||
elif tag_val[0] == u'#Speed':
|
elif tag_val[0] == u'#Speed':
|
||||||
pass
|
pass
|
||||||
elif tag_val[0] == u'Tempo':
|
elif tag_val[0] == u'Tempo':
|
||||||
@ -269,7 +269,7 @@ class SongBeamerImport(SongImport):
|
|||||||
# TODO: add the verse order.
|
# TODO: add the verse order.
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def check_verse_marks(self, line):
|
def checkVerseMarks(self, line):
|
||||||
"""
|
"""
|
||||||
Check and add the verse's MarkType. Returns ``True`` if the given line
|
Check and add the verse's MarkType. Returns ``True`` if the given line
|
||||||
contains a correct verse mark otherwise ``False``.
|
contains a correct verse mark otherwise ``False``.
|
||||||
@ -279,10 +279,10 @@ class SongBeamerImport(SongImport):
|
|||||||
"""
|
"""
|
||||||
marks = line.split(u' ')
|
marks = line.split(u' ')
|
||||||
if len(marks) <= 2 and marks[0] in SongBeamerTypes.MarkTypes:
|
if len(marks) <= 2 and marks[0] in SongBeamerTypes.MarkTypes:
|
||||||
self.current_verse_type = SongBeamerTypes.MarkTypes[marks[0]]
|
self.currentVerseType = SongBeamerTypes.MarkTypes[marks[0]]
|
||||||
if len(marks) == 2:
|
if len(marks) == 2:
|
||||||
# If we have a digit, we append it to current_verse_type.
|
# If we have a digit, we append it to current_verse_type.
|
||||||
if marks[1].isdigit():
|
if marks[1].isdigit():
|
||||||
self.current_verse_type += marks[1]
|
self.currentVerseType += marks[1]
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
@ -26,11 +26,14 @@
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
import shutil
|
||||||
|
import os
|
||||||
|
|
||||||
from PyQt4 import QtCore
|
from PyQt4 import QtCore
|
||||||
|
|
||||||
from openlp.core.lib import Receiver, translate
|
from openlp.core.lib import Receiver, translate
|
||||||
from openlp.core.ui.wizard import WizardStrings
|
from openlp.core.ui.wizard import WizardStrings
|
||||||
|
from openlp.core.utils import AppLocation
|
||||||
from openlp.plugins.songs.lib import clean_song, VerseType
|
from openlp.plugins.songs.lib import clean_song, VerseType
|
||||||
from openlp.plugins.songs.lib.db import Song, Author, Topic, Book, MediaFile
|
from openlp.plugins.songs.lib.db import Song, Author, Topic, Book, MediaFile
|
||||||
from openlp.plugins.songs.lib.ui import SongStrings
|
from openlp.plugins.songs.lib.ui import SongStrings
|
||||||
@ -58,51 +61,51 @@ class SongImport(QtCore.QObject):
|
|||||||
self.manager = manager
|
self.manager = manager
|
||||||
QtCore.QObject.__init__(self)
|
QtCore.QObject.__init__(self)
|
||||||
if kwargs.has_key(u'filename'):
|
if kwargs.has_key(u'filename'):
|
||||||
self.import_source = kwargs[u'filename']
|
self.importSource = kwargs[u'filename']
|
||||||
elif kwargs.has_key(u'filenames'):
|
elif kwargs.has_key(u'filenames'):
|
||||||
self.import_source = kwargs[u'filenames']
|
self.importSource = kwargs[u'filenames']
|
||||||
else:
|
else:
|
||||||
raise KeyError(u'Keyword arguments "filename[s]" not supplied.')
|
raise KeyError(u'Keyword arguments "filename[s]" not supplied.')
|
||||||
log.debug(self.import_source)
|
log.debug(self.importSource)
|
||||||
self.import_wizard = None
|
self.importWizard = None
|
||||||
self.song = None
|
self.song = None
|
||||||
self.stop_import_flag = False
|
self.stopImportFlag = False
|
||||||
self.set_defaults()
|
self.setDefaults()
|
||||||
self.error_log = []
|
self.errorLog = []
|
||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
QtCore.SIGNAL(u'openlp_stop_wizard'), self.stop_import)
|
QtCore.SIGNAL(u'openlp_stop_wizard'), self.stopImport)
|
||||||
|
|
||||||
def set_defaults(self):
|
def setDefaults(self):
|
||||||
"""
|
"""
|
||||||
Create defaults for properties - call this before each song
|
Create defaults for properties - call this before each song
|
||||||
if importing many songs at once to ensure a clean beginning
|
if importing many songs at once to ensure a clean beginning
|
||||||
"""
|
"""
|
||||||
self.title = u''
|
self.title = u''
|
||||||
self.song_number = u''
|
self.songNumber = u''
|
||||||
self.alternate_title = u''
|
self.alternateTitle = u''
|
||||||
self.copyright = u''
|
self.copyright = u''
|
||||||
self.comments = u''
|
self.comments = u''
|
||||||
self.theme_name = u''
|
self.themeName = u''
|
||||||
self.ccli_number = u''
|
self.ccliNumber = u''
|
||||||
self.authors = []
|
self.authors = []
|
||||||
self.topics = []
|
self.topics = []
|
||||||
self.media_files = []
|
self.mediaFiles = []
|
||||||
self.song_book_name = u''
|
self.songBookName = u''
|
||||||
self.song_book_pub = u''
|
self.songBookPub = u''
|
||||||
self.verse_order_list_generated_useful = False
|
self.verseOrderListGeneratedUseful = False
|
||||||
self.verse_order_list_generated = []
|
self.verseOrderListGenerated = []
|
||||||
self.verse_order_list = []
|
self.verseOrderList = []
|
||||||
self.verses = []
|
self.verses = []
|
||||||
self.verse_counts = {}
|
self.verseCounts = {}
|
||||||
self.copyright_string = unicode(translate(
|
self.copyrightString = unicode(translate(
|
||||||
'SongsPlugin.SongImport', 'copyright'))
|
'SongsPlugin.SongImport', 'copyright'))
|
||||||
|
|
||||||
def log_error(self, filepath, reason=SongStrings.SongIncomplete):
|
def logError(self, filepath, reason=SongStrings.SongIncomplete):
|
||||||
"""
|
"""
|
||||||
This should be called, when a song could not be imported.
|
This should be called, when a song could not be imported.
|
||||||
|
|
||||||
``filepath``
|
``filepath``
|
||||||
This should be the file path if ``self.import_source`` is a list
|
This should be the file path if ``self.importSource`` is a list
|
||||||
with different files. If it is not a list, but a single file (for
|
with different files. If it is not a list, but a single file (for
|
||||||
instance a database), then this should be the song's title.
|
instance a database), then this should be the song's title.
|
||||||
|
|
||||||
@ -110,30 +113,30 @@ class SongImport(QtCore.QObject):
|
|||||||
The reason, why the import failed. The string should be as
|
The reason, why the import failed. The string should be as
|
||||||
informative as possible.
|
informative as possible.
|
||||||
"""
|
"""
|
||||||
self.set_defaults()
|
self.setDefaults()
|
||||||
if self.import_wizard is None:
|
if self.importWizard is None:
|
||||||
return
|
return
|
||||||
if self.import_wizard.errorReportTextEdit.isHidden():
|
if self.importWizard.errorReportTextEdit.isHidden():
|
||||||
self.import_wizard.errorReportTextEdit.setText(
|
self.importWizard.errorReportTextEdit.setText(
|
||||||
translate('SongsPlugin.SongImport',
|
translate('SongsPlugin.SongImport',
|
||||||
'The following songs could not be imported:'))
|
'The following songs could not be imported:'))
|
||||||
self.import_wizard.errorReportTextEdit.setVisible(True)
|
self.importWizard.errorReportTextEdit.setVisible(True)
|
||||||
self.import_wizard.errorCopyToButton.setVisible(True)
|
self.importWizard.errorCopyToButton.setVisible(True)
|
||||||
self.import_wizard.errorSaveToButton.setVisible(True)
|
self.importWizard.errorSaveToButton.setVisible(True)
|
||||||
self.import_wizard.errorReportTextEdit.append(
|
self.importWizard.errorReportTextEdit.append(
|
||||||
u'- %s (%s)' % (filepath, reason))
|
u'- %s (%s)' % (filepath, reason))
|
||||||
|
|
||||||
def stop_import(self):
|
def stopImport(self):
|
||||||
"""
|
"""
|
||||||
Sets the flag for importers to stop their import
|
Sets the flag for importers to stop their import
|
||||||
"""
|
"""
|
||||||
log.debug(u'Stopping songs import')
|
log.debug(u'Stopping songs import')
|
||||||
self.stop_import_flag = True
|
self.stopImportFlag = True
|
||||||
|
|
||||||
def register(self, import_wizard):
|
def register(self, import_wizard):
|
||||||
self.import_wizard = import_wizard
|
self.importWizard = import_wizard
|
||||||
|
|
||||||
def tidy_text(self, text):
|
def tidyText(self, text):
|
||||||
"""
|
"""
|
||||||
Get rid of some dodgy unicode and formatting characters we're not
|
Get rid of some dodgy unicode and formatting characters we're not
|
||||||
interested in. Some can be converted to ascii.
|
interested in. Some can be converted to ascii.
|
||||||
@ -151,34 +154,34 @@ class SongImport(QtCore.QObject):
|
|||||||
text = re.sub(r' ?(\n{5}|\f)+ ?', u'\f', text)
|
text = re.sub(r' ?(\n{5}|\f)+ ?', u'\f', text)
|
||||||
return text
|
return text
|
||||||
|
|
||||||
def process_song_text(self, text):
|
def processSongText(self, text):
|
||||||
verse_texts = text.split(u'\n\n')
|
verse_texts = text.split(u'\n\n')
|
||||||
for verse_text in verse_texts:
|
for verse_text in verse_texts:
|
||||||
if verse_text.strip() != u'':
|
if verse_text.strip() != u'':
|
||||||
self.process_verse_text(verse_text.strip())
|
self.processVerseText(verse_text.strip())
|
||||||
|
|
||||||
def process_verse_text(self, text):
|
def processVerseText(self, text):
|
||||||
lines = text.split(u'\n')
|
lines = text.split(u'\n')
|
||||||
if text.lower().find(self.copyright_string) >= 0 \
|
if text.lower().find(self.copyrightString) >= 0 \
|
||||||
or text.find(unicode(SongStrings.CopyrightSymbol)) >= 0:
|
or text.find(unicode(SongStrings.CopyrightSymbol)) >= 0:
|
||||||
copyright_found = False
|
copyright_found = False
|
||||||
for line in lines:
|
for line in lines:
|
||||||
if (copyright_found or
|
if (copyright_found or
|
||||||
line.lower().find(self.copyright_string) >= 0 or
|
line.lower().find(self.copyrightString) >= 0 or
|
||||||
line.find(unicode(SongStrings.CopyrightSymbol)) >= 0):
|
line.find(unicode(SongStrings.CopyrightSymbol)) >= 0):
|
||||||
copyright_found = True
|
copyright_found = True
|
||||||
self.add_copyright(line)
|
self.addCopyright(line)
|
||||||
else:
|
else:
|
||||||
self.parse_author(line)
|
self.parseAuthor(line)
|
||||||
return
|
return
|
||||||
if len(lines) == 1:
|
if len(lines) == 1:
|
||||||
self.parse_author(lines[0])
|
self.parseAuthor(lines[0])
|
||||||
return
|
return
|
||||||
if not self.title:
|
if not self.title:
|
||||||
self.title = lines[0]
|
self.title = lines[0]
|
||||||
self.add_verse(text)
|
self.addVerse(text)
|
||||||
|
|
||||||
def add_copyright(self, copyright):
|
def addCopyright(self, copyright):
|
||||||
"""
|
"""
|
||||||
Build the copyright field
|
Build the copyright field
|
||||||
"""
|
"""
|
||||||
@ -188,7 +191,7 @@ class SongImport(QtCore.QObject):
|
|||||||
self.copyright += ' '
|
self.copyright += ' '
|
||||||
self.copyright += copyright
|
self.copyright += copyright
|
||||||
|
|
||||||
def parse_author(self, text):
|
def parseAuthor(self, text):
|
||||||
"""
|
"""
|
||||||
Add the author. OpenLP stores them individually so split by 'and', '&'
|
Add the author. OpenLP stores them individually so split by 'and', '&'
|
||||||
and comma. However need to check for 'Mr and Mrs Smith' and turn it to
|
and comma. However need to check for 'Mr and Mrs Smith' and turn it to
|
||||||
@ -204,9 +207,9 @@ class SongImport(QtCore.QObject):
|
|||||||
if author2.endswith(u'.'):
|
if author2.endswith(u'.'):
|
||||||
author2 = author2[:-1]
|
author2 = author2[:-1]
|
||||||
if author2:
|
if author2:
|
||||||
self.add_author(author2)
|
self.addAuthor(author2)
|
||||||
|
|
||||||
def add_author(self, author):
|
def addAuthor(self, author):
|
||||||
"""
|
"""
|
||||||
Add an author to the list
|
Add an author to the list
|
||||||
"""
|
"""
|
||||||
@ -214,15 +217,15 @@ class SongImport(QtCore.QObject):
|
|||||||
return
|
return
|
||||||
self.authors.append(author)
|
self.authors.append(author)
|
||||||
|
|
||||||
def add_media_file(self, filename):
|
def addMediaFile(self, filename, weight=0):
|
||||||
"""
|
"""
|
||||||
Add a media file to the list
|
Add a media file to the list
|
||||||
"""
|
"""
|
||||||
if filename in self.media_files:
|
if filename in map(lambda x: x[0], self.mediaFiles):
|
||||||
return
|
return
|
||||||
self.media_files.append(filename)
|
self.mediaFiles.append((filename, weight))
|
||||||
|
|
||||||
def add_verse(self, verse_text, verse_def=u'v', lang=None):
|
def addVerse(self, verse_text, verse_def=u'v', lang=None):
|
||||||
"""
|
"""
|
||||||
Add a verse. This is the whole verse, lines split by \\n. It will also
|
Add a verse. This is the whole verse, lines split by \\n. It will also
|
||||||
attempt to detect duplicates. In this case it will just add to the verse
|
attempt to detect duplicates. In this case it will just add to the verse
|
||||||
@ -241,29 +244,29 @@ class SongImport(QtCore.QObject):
|
|||||||
"""
|
"""
|
||||||
for (old_verse_def, old_verse, old_lang) in self.verses:
|
for (old_verse_def, old_verse, old_lang) in self.verses:
|
||||||
if old_verse.strip() == verse_text.strip():
|
if old_verse.strip() == verse_text.strip():
|
||||||
self.verse_order_list_generated.append(old_verse_def)
|
self.verseOrderListGenerated.append(old_verse_def)
|
||||||
self.verse_order_list_generated_useful = True
|
self.verseOrderListGeneratedUseful = True
|
||||||
return
|
return
|
||||||
if verse_def[0] in self.verse_counts:
|
if verse_def[0] in self.verseCounts:
|
||||||
self.verse_counts[verse_def[0]] += 1
|
self.verseCounts[verse_def[0]] += 1
|
||||||
else:
|
else:
|
||||||
self.verse_counts[verse_def[0]] = 1
|
self.verseCounts[verse_def[0]] = 1
|
||||||
if len(verse_def) == 1:
|
if len(verse_def) == 1:
|
||||||
verse_def += unicode(self.verse_counts[verse_def[0]])
|
verse_def += unicode(self.verseCounts[verse_def[0]])
|
||||||
elif int(verse_def[1:]) > self.verse_counts[verse_def[0]]:
|
elif int(verse_def[1:]) > self.verseCounts[verse_def[0]]:
|
||||||
self.verse_counts[verse_def[0]] = int(verse_def[1:])
|
self.verseCounts[verse_def[0]] = int(verse_def[1:])
|
||||||
self.verses.append([verse_def, verse_text.rstrip(), lang])
|
self.verses.append([verse_def, verse_text.rstrip(), lang])
|
||||||
self.verse_order_list_generated.append(verse_def)
|
self.verseOrderListGenerated.append(verse_def)
|
||||||
|
|
||||||
def repeat_verse(self):
|
def repeatVerse(self):
|
||||||
"""
|
"""
|
||||||
Repeat the previous verse in the verse order
|
Repeat the previous verse in the verse order
|
||||||
"""
|
"""
|
||||||
self.verse_order_list_generated.append(
|
self.verseOrderListGenerated.append(
|
||||||
self.verse_order_list_generated[-1])
|
self.verseOrderListGenerated[-1])
|
||||||
self.verse_order_list_generated_useful = True
|
self.verseOrderListGeneratedUseful = True
|
||||||
|
|
||||||
def check_complete(self):
|
def checkComplete(self):
|
||||||
"""
|
"""
|
||||||
Check the mandatory fields are entered (i.e. title and a verse)
|
Check the mandatory fields are entered (i.e. title and a verse)
|
||||||
Author not checked here, if no author then "Author unknown" is
|
Author not checked here, if no author then "Author unknown" is
|
||||||
@ -278,21 +281,21 @@ class SongImport(QtCore.QObject):
|
|||||||
"""
|
"""
|
||||||
All fields have been set to this song. Write the song to disk.
|
All fields have been set to this song. Write the song to disk.
|
||||||
"""
|
"""
|
||||||
if not self.check_complete():
|
if not self.checkComplete():
|
||||||
self.set_defaults()
|
self.setDefaults()
|
||||||
return False
|
return False
|
||||||
log.info(u'committing song %s to database', self.title)
|
log.info(u'committing song %s to database', self.title)
|
||||||
song = Song()
|
song = Song()
|
||||||
song.title = self.title
|
song.title = self.title
|
||||||
if self.import_wizard is not None:
|
if self.importWizard is not None:
|
||||||
self.import_wizard.incrementProgressBar(
|
self.importWizard.incrementProgressBar(
|
||||||
WizardStrings.ImportingType % song.title)
|
WizardStrings.ImportingType % song.title)
|
||||||
song.alternate_title = self.alternate_title
|
song.alternate_title = self.alternateTitle
|
||||||
# Values will be set when cleaning the song.
|
# Values will be set when cleaning the song.
|
||||||
song.search_title = u''
|
song.search_title = u''
|
||||||
song.search_lyrics = u''
|
song.search_lyrics = u''
|
||||||
song.verse_order = u''
|
song.verse_order = u''
|
||||||
song.song_number = self.song_number
|
song.song_number = self.songNumber
|
||||||
verses_changed_to_other = {}
|
verses_changed_to_other = {}
|
||||||
sxml = SongXML()
|
sxml = SongXML()
|
||||||
other_count = 1
|
other_count = 1
|
||||||
@ -310,18 +313,18 @@ class SongImport(QtCore.QObject):
|
|||||||
verse_def = new_verse_def
|
verse_def = new_verse_def
|
||||||
sxml.add_verse_to_lyrics(verse_tag, verse_def[1:], verse_text, lang)
|
sxml.add_verse_to_lyrics(verse_tag, verse_def[1:], verse_text, lang)
|
||||||
song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
|
song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
|
||||||
if not len(self.verse_order_list) and \
|
if not len(self.verseOrderList) and \
|
||||||
self.verse_order_list_generated_useful:
|
self.verseOrderListGeneratedUseful:
|
||||||
self.verse_order_list = self.verse_order_list_generated
|
self.verseOrderList = self.verseOrderListGenerated
|
||||||
for i, current_verse_def in enumerate(self.verse_order_list):
|
for i, current_verse_def in enumerate(self.verseOrderList):
|
||||||
if verses_changed_to_other.has_key(current_verse_def):
|
if verses_changed_to_other.has_key(current_verse_def):
|
||||||
self.verse_order_list[i] = \
|
self.verseOrderList[i] = \
|
||||||
verses_changed_to_other[current_verse_def]
|
verses_changed_to_other[current_verse_def]
|
||||||
song.verse_order = u' '.join(self.verse_order_list)
|
song.verse_order = u' '.join(self.verseOrderList)
|
||||||
song.copyright = self.copyright
|
song.copyright = self.copyright
|
||||||
song.comments = self.comments
|
song.comments = self.comments
|
||||||
song.theme_name = self.theme_name
|
song.theme_name = self.themeName
|
||||||
song.ccli_number = self.ccli_number
|
song.ccli_number = self.ccliNumber
|
||||||
for authortext in self.authors:
|
for authortext in self.authors:
|
||||||
author = self.manager.get_object_filtered(Author,
|
author = self.manager.get_object_filtered(Author,
|
||||||
Author.display_name == authortext)
|
Author.display_name == authortext)
|
||||||
@ -330,17 +333,12 @@ class SongImport(QtCore.QObject):
|
|||||||
last_name=authortext.split(u' ')[-1],
|
last_name=authortext.split(u' ')[-1],
|
||||||
first_name=u' '.join(authortext.split(u' ')[:-1]))
|
first_name=u' '.join(authortext.split(u' ')[:-1]))
|
||||||
song.authors.append(author)
|
song.authors.append(author)
|
||||||
for filename in self.media_files:
|
if self.songBookName:
|
||||||
media_file = self.manager.get_object_filtered(MediaFile,
|
|
||||||
MediaFile.file_name == filename)
|
|
||||||
if not media_file:
|
|
||||||
song.media_files.append(MediaFile.populate(file_name=filename))
|
|
||||||
if self.song_book_name:
|
|
||||||
song_book = self.manager.get_object_filtered(Book,
|
song_book = self.manager.get_object_filtered(Book,
|
||||||
Book.name == self.song_book_name)
|
Book.name == self.songBookName)
|
||||||
if song_book is None:
|
if song_book is None:
|
||||||
song_book = Book.populate(name=self.song_book_name,
|
song_book = Book.populate(name=self.songBookName,
|
||||||
publisher=self.song_book_pub)
|
publisher=self.songBookPub)
|
||||||
song.book = song_book
|
song.book = song_book
|
||||||
for topictext in self.topics:
|
for topictext in self.topics:
|
||||||
if not topictext:
|
if not topictext:
|
||||||
@ -350,38 +348,42 @@ class SongImport(QtCore.QObject):
|
|||||||
if topic is None:
|
if topic is None:
|
||||||
topic = Topic.populate(name=topictext)
|
topic = Topic.populate(name=topictext)
|
||||||
song.topics.append(topic)
|
song.topics.append(topic)
|
||||||
|
# We need to save the song now, before adding the media files, so that
|
||||||
|
# we know where to save the media files to.
|
||||||
clean_song(self.manager, song)
|
clean_song(self.manager, song)
|
||||||
self.manager.save_object(song)
|
self.manager.save_object(song)
|
||||||
self.set_defaults()
|
# Now loop through the media files, copy them to the correct location,
|
||||||
|
# and save the song again.
|
||||||
|
for filename, weight in self.mediaFiles:
|
||||||
|
media_file = self.manager.get_object_filtered(MediaFile,
|
||||||
|
MediaFile.file_name == filename)
|
||||||
|
if not media_file:
|
||||||
|
if os.path.dirname(filename):
|
||||||
|
filename = self.copyMediaFile(song.id, filename)
|
||||||
|
song.media_files.append(
|
||||||
|
MediaFile.populate(file_name=filename, weight=weight)
|
||||||
|
)
|
||||||
|
self.manager.save_object(song)
|
||||||
|
self.setDefaults()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def print_song(self):
|
def copyMediaFile(self, song_id, filename):
|
||||||
"""
|
"""
|
||||||
For debugging
|
This method copies the media file to the correct location and returns
|
||||||
|
the new file location.
|
||||||
|
|
||||||
|
``filename``
|
||||||
|
The file to copy.
|
||||||
"""
|
"""
|
||||||
print u'========================================' \
|
if not hasattr(self, u'save_path'):
|
||||||
+ u'========================================'
|
self.save_path = os.path.join(
|
||||||
print u'TITLE: ' + self.title
|
AppLocation.get_section_data_path(
|
||||||
print u'ALT TITLE: ' + self.alternate_title
|
self.importWizard.plugin.name),
|
||||||
for (verse_def, verse_text, lang) in self.verses:
|
'audio', str(song_id))
|
||||||
print u'VERSE ' + verse_def + u': ' + verse_text
|
if not os.path.exists(self.save_path):
|
||||||
print u'ORDER: ' + u' '.join(self.verse_order_list)
|
os.makedirs(self.save_path)
|
||||||
print u'GENERATED ORDER: ' + u' '.join(self.verse_order_list_generated)
|
if not filename.startswith(self.save_path):
|
||||||
for author in self.authors:
|
oldfile, filename = filename, os.path.join(self.save_path,
|
||||||
print u'AUTHOR: ' + author
|
os.path.split(filename)[1])
|
||||||
if self.copyright:
|
shutil.copyfile(oldfile, filename)
|
||||||
print u'COPYRIGHT: ' + self.copyright
|
return filename
|
||||||
if self.song_book_name:
|
|
||||||
print u'BOOK: ' + self.song_book_name
|
|
||||||
if self.song_book_pub:
|
|
||||||
print u'BOOK PUBLISHER: ' + self.song_book_pub
|
|
||||||
if self.song_number:
|
|
||||||
print u'NUMBER: ' + self.song_number
|
|
||||||
for topictext in self.topics:
|
|
||||||
print u'TOPIC: ' + topictext
|
|
||||||
if self.comments:
|
|
||||||
print u'COMMENTS: ' + self.comments
|
|
||||||
if self.theme_name:
|
|
||||||
print u'THEME: ' + self.theme_name
|
|
||||||
if self.ccli_number:
|
|
||||||
print u'CCLI: ' + self.ccli_number
|
|
||||||
|
@ -95,118 +95,120 @@ class SongShowPlusImport(SongImport):
|
|||||||
"""
|
"""
|
||||||
SongImport.__init__(self, manager, **kwargs)
|
SongImport.__init__(self, manager, **kwargs)
|
||||||
|
|
||||||
def do_import(self):
|
def doImport(self):
|
||||||
"""
|
"""
|
||||||
Receive a single file or a list of files to import.
|
Receive a single file or a list of files to import.
|
||||||
"""
|
"""
|
||||||
if not isinstance(self.import_source, list):
|
if not isinstance(self.importSource, list):
|
||||||
return
|
return
|
||||||
self.import_wizard.progressBar.setMaximum(len(self.import_source))
|
self.importWizard.progressBar.setMaximum(len(self.importSource))
|
||||||
for file in self.import_source:
|
for file in self.importSource:
|
||||||
|
if self.stopImportFlag:
|
||||||
|
return
|
||||||
self.sspVerseOrderList = []
|
self.sspVerseOrderList = []
|
||||||
otherCount = 0
|
other_count = 0
|
||||||
otherList = {}
|
other_list = {}
|
||||||
file_name = os.path.split(file)[1]
|
file_name = os.path.split(file)[1]
|
||||||
self.import_wizard.incrementProgressBar(
|
self.importWizard.incrementProgressBar(
|
||||||
WizardStrings.ImportingType % file_name, 0)
|
WizardStrings.ImportingType % file_name, 0)
|
||||||
songData = open(file, 'rb')
|
song_data = open(file, 'rb')
|
||||||
while True:
|
while True:
|
||||||
blockKey, = struct.unpack("I", songData.read(4))
|
block_key, = struct.unpack("I", song_data.read(4))
|
||||||
# The file ends with 4 NUL's
|
# The file ends with 4 NUL's
|
||||||
if blockKey == 0:
|
if block_key == 0:
|
||||||
break
|
break
|
||||||
nextBlockStarts, = struct.unpack("I", songData.read(4))
|
next_block_starts, = struct.unpack("I", song_data.read(4))
|
||||||
nextBlockStarts += songData.tell()
|
next_block_starts += song_data.tell()
|
||||||
if blockKey in (VERSE, CHORUS, BRIDGE):
|
if block_key in (VERSE, CHORUS, BRIDGE):
|
||||||
null, verseNo, = struct.unpack("BB", songData.read(2))
|
null, verse_no, = struct.unpack("BB", song_data.read(2))
|
||||||
elif blockKey == CUSTOM_VERSE:
|
elif block_key == CUSTOM_VERSE:
|
||||||
null, verseNameLength, = struct.unpack("BB",
|
null, verse_name_length, = struct.unpack("BB",
|
||||||
songData.read(2))
|
song_data.read(2))
|
||||||
verseName = songData.read(verseNameLength)
|
verse_name = song_data.read(verse_name_length)
|
||||||
lengthDescriptorSize, = struct.unpack("B", songData.read(1))
|
length_descriptor_size, = struct.unpack("B", song_data.read(1))
|
||||||
log.debug(lengthDescriptorSize)
|
log.debug(length_descriptor_size)
|
||||||
# Detect if/how long the length descriptor is
|
# Detect if/how long the length descriptor is
|
||||||
if lengthDescriptorSize == 12 or lengthDescriptorSize == 20:
|
if length_descriptor_size == 12 or length_descriptor_size == 20:
|
||||||
lengthDescriptor, = struct.unpack("I", songData.read(4))
|
length_descriptor, = struct.unpack("I", song_data.read(4))
|
||||||
elif lengthDescriptorSize == 2:
|
elif length_descriptor_size == 2:
|
||||||
lengthDescriptor = 1
|
length_descriptor = 1
|
||||||
elif lengthDescriptorSize == 9:
|
elif length_descriptor_size == 9:
|
||||||
lengthDescriptor = 0
|
length_descriptor = 0
|
||||||
else:
|
else:
|
||||||
lengthDescriptor, = struct.unpack("B", songData.read(1))
|
length_descriptor, = struct.unpack("B", song_data.read(1))
|
||||||
log.debug(lengthDescriptorSize)
|
log.debug(length_descriptor_size)
|
||||||
data = songData.read(lengthDescriptor)
|
data = song_data.read(length_descriptor)
|
||||||
if blockKey == TITLE:
|
if block_key == TITLE:
|
||||||
self.title = unicode(data, u'cp1252')
|
self.title = unicode(data, u'cp1252')
|
||||||
elif blockKey == AUTHOR:
|
elif block_key == AUTHOR:
|
||||||
authors = data.split(" / ")
|
authors = data.split(" / ")
|
||||||
for author in authors:
|
for author in authors:
|
||||||
if author.find(",") !=-1:
|
if author.find(",") !=-1:
|
||||||
authorParts = author.split(", ")
|
authorParts = author.split(", ")
|
||||||
author = authorParts[1] + " " + authorParts[0]
|
author = authorParts[1] + " " + authorParts[0]
|
||||||
self.parse_author(unicode(author, u'cp1252'))
|
self.parseAuthor(unicode(author, u'cp1252'))
|
||||||
elif blockKey == COPYRIGHT:
|
elif block_key == COPYRIGHT:
|
||||||
self.add_copyright(unicode(data, u'cp1252'))
|
self.addCopyright(unicode(data, u'cp1252'))
|
||||||
elif blockKey == CCLI_NO:
|
elif block_key == CCLI_NO:
|
||||||
self.ccli_number = int(data)
|
self.ccliNumber = int(data)
|
||||||
elif blockKey == VERSE:
|
elif block_key == VERSE:
|
||||||
self.add_verse(unicode(data, u'cp1252'),
|
self.addVerse(unicode(data, u'cp1252'),
|
||||||
"%s%s" % (VerseType.Tags[VerseType.Verse], verseNo))
|
"%s%s" % (VerseType.Tags[VerseType.Verse], verse_no))
|
||||||
elif blockKey == CHORUS:
|
elif block_key == CHORUS:
|
||||||
self.add_verse(unicode(data, u'cp1252'),
|
self.addVerse(unicode(data, u'cp1252'),
|
||||||
"%s%s" % (VerseType.Tags[VerseType.Chorus], verseNo))
|
"%s%s" % (VerseType.Tags[VerseType.Chorus], verse_no))
|
||||||
elif blockKey == BRIDGE:
|
elif block_key == BRIDGE:
|
||||||
self.add_verse(unicode(data, u'cp1252'),
|
self.addVerse(unicode(data, u'cp1252'),
|
||||||
"%s%s" % (VerseType.Tags[VerseType.Bridge], verseNo))
|
"%s%s" % (VerseType.Tags[VerseType.Bridge], verse_no))
|
||||||
elif blockKey == TOPIC:
|
elif block_key == TOPIC:
|
||||||
self.topics.append(unicode(data, u'cp1252'))
|
self.topics.append(unicode(data, u'cp1252'))
|
||||||
elif blockKey == COMMENTS:
|
elif block_key == COMMENTS:
|
||||||
self.comments = unicode(data, u'cp1252')
|
self.comments = unicode(data, u'cp1252')
|
||||||
elif blockKey == VERSE_ORDER:
|
elif block_key == VERSE_ORDER:
|
||||||
verseTag = self.toOpenLPVerseTag(data, True)
|
verse_tag = self.toOpenLPVerseTag(data, True)
|
||||||
if verseTag:
|
if verse_tag:
|
||||||
if not isinstance(verseTag, unicode):
|
if not isinstance(verse_tag, unicode):
|
||||||
verseTag = unicode(verseTag, u'cp1252')
|
verse_tag = unicode(verse_tag, u'cp1252')
|
||||||
self.sspVerseOrderList.append(verseTag)
|
self.sspVerseOrderList.append(verse_tag)
|
||||||
elif blockKey == SONG_BOOK:
|
elif block_key == SONG_BOOK:
|
||||||
self.song_book_name = unicode(data, u'cp1252')
|
self.songBookName = unicode(data, u'cp1252')
|
||||||
elif blockKey == SONG_NUMBER:
|
elif block_key == SONG_NUMBER:
|
||||||
self.song_number = ord(data)
|
self.songNumber = ord(data)
|
||||||
elif blockKey == CUSTOM_VERSE:
|
elif block_key == CUSTOM_VERSE:
|
||||||
verseTag = self.toOpenLPVerseTag(verseName)
|
verse_tag = self.toOpenLPVerseTag(verse_name)
|
||||||
self.add_verse(unicode(data, u'cp1252'), verseTag)
|
self.addVerse(unicode(data, u'cp1252'), verse_tag)
|
||||||
else:
|
else:
|
||||||
log.debug("Unrecognised blockKey: %s, data: %s"
|
log.debug("Unrecognised blockKey: %s, data: %s"
|
||||||
% (blockKey, data))
|
% (block_key, data))
|
||||||
songData.seek(nextBlockStarts)
|
song_data.seek(next_block_starts)
|
||||||
self.verse_order_list = self.sspVerseOrderList
|
self.verseOrderList = self.sspVerseOrderList
|
||||||
songData.close()
|
song_data.close()
|
||||||
if not self.finish():
|
if not self.finish():
|
||||||
self.log_error(file)
|
self.logError(file)
|
||||||
|
|
||||||
def toOpenLPVerseTag(self, verseName, ignoreUnique=False):
|
def toOpenLPVerseTag(self, verse_name, ignore_unique=False):
|
||||||
if verseName.find(" ") != -1:
|
if verse_name.find(" ") != -1:
|
||||||
verseParts = verseName.split(" ")
|
verse_parts = verse_name.split(" ")
|
||||||
verseType = verseParts[0]
|
verse_type = verse_parts[0]
|
||||||
verseNumber = verseParts[1]
|
verse_number = verse_parts[1]
|
||||||
else:
|
else:
|
||||||
verseType = verseName
|
verse_type = verse_name
|
||||||
verseNumber = "1"
|
verse_number = "1"
|
||||||
verseType = verseType.lower()
|
verse_type = verse_type.lower()
|
||||||
if verseType == "verse":
|
if verse_type == "verse":
|
||||||
verseTag = VerseType.Tags[VerseType.Verse]
|
verse_tag = VerseType.Tags[VerseType.Verse]
|
||||||
elif verseType == "chorus":
|
elif verse_type == "chorus":
|
||||||
verseTag = VerseType.Tags[VerseType.Chorus]
|
verse_tag = VerseType.Tags[VerseType.Chorus]
|
||||||
elif verseType == "bridge":
|
elif verse_type == "bridge":
|
||||||
verseTag = VerseType.Tags[VerseType.Bridge]
|
verse_tag = VerseType.Tags[VerseType.Bridge]
|
||||||
elif verseType == "pre-chorus":
|
elif verse_type == "pre-chorus":
|
||||||
verseTag = VerseType.Tags[VerseType.PreChorus]
|
verse_tag = VerseType.Tags[VerseType.PreChorus]
|
||||||
else:
|
else:
|
||||||
if not self.otherList.has_key(verseName):
|
if not self.otherList.has_key(verse_name):
|
||||||
if ignoreUnique:
|
if ignore_unique:
|
||||||
return None
|
return None
|
||||||
self.otherCount = self.otherCount + 1
|
self.otherCount = self.otherCount + 1
|
||||||
self.otherList[verseName] = str(self.otherCount)
|
self.otherList[verse_name] = str(self.otherCount)
|
||||||
verseTag = VerseType.Tags[VerseType.Other]
|
verse_tag = VerseType.Tags[VerseType.Other]
|
||||||
verseNumber = self.otherList[verseName]
|
verse_number = self.otherList[verse_name]
|
||||||
return verseTag + verseNumber
|
return verse_tag + verse_number
|
||||||
|
@ -98,56 +98,58 @@ class WowImport(SongImport):
|
|||||||
"""
|
"""
|
||||||
SongImport.__init__(self, manager, **kwargs)
|
SongImport.__init__(self, manager, **kwargs)
|
||||||
|
|
||||||
def do_import(self):
|
def doImport(self):
|
||||||
"""
|
"""
|
||||||
Receive a single file or a list of files to import.
|
Receive a single file or a list of files to import.
|
||||||
"""
|
"""
|
||||||
if isinstance(self.import_source, list):
|
if isinstance(self.importSource, list):
|
||||||
self.import_wizard.progressBar.setMaximum(len(self.import_source))
|
self.importWizard.progressBar.setMaximum(len(self.importSource))
|
||||||
for file in self.import_source:
|
for file in self.importSource:
|
||||||
|
if self.stopImportFlag:
|
||||||
|
return
|
||||||
file_name = os.path.split(file)[1]
|
file_name = os.path.split(file)[1]
|
||||||
# Get the song title
|
# Get the song title
|
||||||
self.title = file_name.rpartition(u'.')[0]
|
self.title = file_name.rpartition(u'.')[0]
|
||||||
songData = open(file, 'rb')
|
song_data = open(file, 'rb')
|
||||||
if songData.read(19) != u'WoW File\nSong Words':
|
if song_data.read(19) != u'WoW File\nSong Words':
|
||||||
self.log_error(file)
|
self.logError(file)
|
||||||
continue
|
continue
|
||||||
# Seek to byte which stores number of blocks in the song
|
# Seek to byte which stores number of blocks in the song
|
||||||
songData.seek(56)
|
song_data.seek(56)
|
||||||
no_of_blocks = ord(songData.read(1))
|
no_of_blocks = ord(song_data.read(1))
|
||||||
# Seek to the beging of the first block
|
# Seek to the beging of the first block
|
||||||
songData.seek(82)
|
song_data.seek(82)
|
||||||
for block in range(no_of_blocks):
|
for block in range(no_of_blocks):
|
||||||
self.lines_to_read = ord(songData.read(1))
|
self.linesToRead = ord(song_data.read(1))
|
||||||
# Skip 3 nulls to the beginnig of the 1st line
|
# Skip 3 nulls to the beginnig of the 1st line
|
||||||
songData.seek(3, os.SEEK_CUR)
|
song_data.seek(3, os.SEEK_CUR)
|
||||||
block_text = u''
|
block_text = u''
|
||||||
while self.lines_to_read:
|
while self.linesToRead:
|
||||||
self.line_text = unicode(
|
self.lineText = unicode(
|
||||||
songData.read(ord(songData.read(1))), u'cp1252')
|
song_data.read(ord(song_data.read(1))), u'cp1252')
|
||||||
songData.seek(1, os.SEEK_CUR)
|
song_data.seek(1, os.SEEK_CUR)
|
||||||
if block_text:
|
if block_text:
|
||||||
block_text += u'\n'
|
block_text += u'\n'
|
||||||
block_text += self.line_text
|
block_text += self.lineText
|
||||||
self.lines_to_read -= 1
|
self.linesToRead -= 1
|
||||||
block_type = BLOCK_TYPES[ord(songData.read(1))]
|
block_type = BLOCK_TYPES[ord(song_data.read(1))]
|
||||||
# Skip 3 nulls at the end of the block
|
# Skip 3 nulls at the end of the block
|
||||||
songData.seek(3, os.SEEK_CUR)
|
song_data.seek(3, os.SEEK_CUR)
|
||||||
# Blocks are seperated by 2 bytes, skip them, but not if
|
# Blocks are seperated by 2 bytes, skip them, but not if
|
||||||
# this is the last block!
|
# this is the last block!
|
||||||
if block + 1 < no_of_blocks:
|
if block + 1 < no_of_blocks:
|
||||||
songData.seek(2, os.SEEK_CUR)
|
song_data.seek(2, os.SEEK_CUR)
|
||||||
self.add_verse(block_text, block_type)
|
self.addVerse(block_text, block_type)
|
||||||
# Now to extract the author
|
# Now to extract the author
|
||||||
author_length = ord(songData.read(1))
|
author_length = ord(song_data.read(1))
|
||||||
if author_length:
|
if author_length:
|
||||||
self.parse_author(
|
self.parseAuthor(
|
||||||
unicode(songData.read(author_length), u'cp1252'))
|
unicode(song_data.read(author_length), u'cp1252'))
|
||||||
# Finally the copyright
|
# Finally the copyright
|
||||||
copyright_length = ord(songData.read(1))
|
copyright_length = ord(song_data.read(1))
|
||||||
if copyright_length:
|
if copyright_length:
|
||||||
self.add_copyright(unicode(
|
self.addCopyright(unicode(
|
||||||
songData.read(copyright_length), u'cp1252'))
|
song_data.read(copyright_length), u'cp1252'))
|
||||||
songData.close()
|
song_data.close()
|
||||||
if not self.finish():
|
if not self.finish():
|
||||||
self.log_error(file)
|
self.logError(file)
|
||||||
|
@ -343,7 +343,7 @@ class OpenLyrics(object):
|
|||||||
self._process_topics(properties, song)
|
self._process_topics(properties, song)
|
||||||
clean_song(self.manager, song)
|
clean_song(self.manager, song)
|
||||||
self.manager.save_object(song)
|
self.manager.save_object(song)
|
||||||
return song.id
|
return song
|
||||||
|
|
||||||
def _add_text_to_element(self, tag, parent, text=None, label=None):
|
def _add_text_to_element(self, tag, parent, text=None, label=None):
|
||||||
if label:
|
if label:
|
||||||
|
@ -196,7 +196,7 @@ class SongsPlugin(Plugin):
|
|||||||
def importSongs(self, format, **kwargs):
|
def importSongs(self, format, **kwargs):
|
||||||
class_ = SongFormat.get_class(format)
|
class_ = SongFormat.get_class(format)
|
||||||
importer = class_(self.manager, **kwargs)
|
importer = class_(self.manager, **kwargs)
|
||||||
importer.register(self.mediaItem.import_wizard)
|
importer.register(self.mediaItem.importWizard)
|
||||||
return importer
|
return importer
|
||||||
|
|
||||||
def setPluginTextStrings(self):
|
def setPluginTextStrings(self):
|
||||||
@ -252,7 +252,7 @@ class SongsPlugin(Plugin):
|
|||||||
progress.setValue(idx)
|
progress.setValue(idx)
|
||||||
Receiver.send_message(u'openlp_process_events')
|
Receiver.send_message(u'openlp_process_events')
|
||||||
importer = OpenLPSongImport(self.manager, filename=db)
|
importer = OpenLPSongImport(self.manager, filename=db)
|
||||||
importer.do_import()
|
importer.doImport()
|
||||||
progress.setValue(len(song_dbs))
|
progress.setValue(len(song_dbs))
|
||||||
self.mediaItem.onSearchTextButtonClick()
|
self.mediaItem.onSearchTextButtonClick()
|
||||||
|
|
||||||
|
95
resources/forms/mediafilesdialog.ui
Normal file
95
resources/forms/mediafilesdialog.ui
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MediaFilesDialog</class>
|
||||||
|
<widget class="QDialog" name="MediaFilesDialog">
|
||||||
|
<property name="windowModality">
|
||||||
|
<enum>Qt::ApplicationModal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>300</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Select Media File(s)</string>
|
||||||
|
</property>
|
||||||
|
<property name="modal">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="filesVerticalLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>8</number>
|
||||||
|
</property>
|
||||||
|
<property name="margin">
|
||||||
|
<number>8</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="selectLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Select one or more audio files from the list below, and click OK to import them into this song.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QListView" name="fileListView">
|
||||||
|
<property name="alternatingRowColors">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="../images/openlp-2.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>MediaFilesDialog</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>MediaFilesDialog</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
1179
resources/i18n/af.ts
1179
resources/i18n/af.ts
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1212
resources/i18n/de.ts
1212
resources/i18n/de.ts
File diff suppressed because it is too large
Load Diff
1039
resources/i18n/en.ts
1039
resources/i18n/en.ts
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1014
resources/i18n/et.ts
1014
resources/i18n/et.ts
File diff suppressed because it is too large
Load Diff
1108
resources/i18n/fr.ts
1108
resources/i18n/fr.ts
File diff suppressed because it is too large
Load Diff
2680
resources/i18n/hu.ts
2680
resources/i18n/hu.ts
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1004
resources/i18n/ko.ts
1004
resources/i18n/ko.ts
File diff suppressed because it is too large
Load Diff
1004
resources/i18n/nb.ts
1004
resources/i18n/nb.ts
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1695
resources/i18n/ru.ts
1695
resources/i18n/ru.ts
File diff suppressed because it is too large
Load Diff
1481
resources/i18n/sv.ts
1481
resources/i18n/sv.ts
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -80,6 +80,7 @@ OPTIONAL_MODULES = [
|
|||||||
('sqlite', ' (SQLite 2 support)'),
|
('sqlite', ' (SQLite 2 support)'),
|
||||||
('MySQLdb', ' (MySQL support)'),
|
('MySQLdb', ' (MySQL support)'),
|
||||||
('psycopg2', ' (PostgreSQL support)'),
|
('psycopg2', ' (PostgreSQL support)'),
|
||||||
|
('pytest', ' (testing framework)'),
|
||||||
]
|
]
|
||||||
|
|
||||||
w = sys.stdout.write
|
w = sys.stdout.write
|
||||||
|
45
testing/conftest.py
Normal file
45
testing/conftest.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||||
|
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
|
# Armin Köhler, Joshua Millar, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
|
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
Configuration file for pytest framework.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from openlp.core import main as openlp_main
|
||||||
|
|
||||||
|
|
||||||
|
# Test function argument to make openlp gui instance persistent for all tests.
|
||||||
|
# All test cases have to access the same instance. To allow create multiple
|
||||||
|
# instances it would be necessary use diffrent configuraion and data files.
|
||||||
|
# Created instance will use your OpenLP settings.
|
||||||
|
def pytest_funcarg__openlpapp(request):
|
||||||
|
def setup():
|
||||||
|
return openlp_main(['--testing'])
|
||||||
|
def teardown(app):
|
||||||
|
pass
|
||||||
|
return request.cached_setup(setup=setup, teardown=teardown, scope='session')
|
59
testing/run.py
Executable file
59
testing/run.py
Executable file
@ -0,0 +1,59 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||||
|
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
|
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
This script is used to run set of automated tests of OpenLP. To start tests,
|
||||||
|
simply run this script::
|
||||||
|
|
||||||
|
@:~$ ./run.py
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os.path
|
||||||
|
import sys
|
||||||
|
|
||||||
|
TESTS_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
SRC_PATH = os.path.join(TESTS_PATH, '..')
|
||||||
|
|
||||||
|
PYTEST_OPTIONS = [TESTS_PATH]
|
||||||
|
|
||||||
|
# Extend python PATH with openlp source
|
||||||
|
sys.path.insert(0, SRC_PATH)
|
||||||
|
|
||||||
|
# Python testing framework
|
||||||
|
# http://pytest.org
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print 'pytest options:', PYTEST_OPTIONS
|
||||||
|
pytest.main(PYTEST_OPTIONS)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == u'__main__':
|
||||||
|
main()
|
36
testing/test_app.py
Normal file
36
testing/test_app.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||||
|
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
|
# Armin Köhler, Joshua Millar, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
|
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
from openlp.core import OpenLP
|
||||||
|
from openlp.core.ui.mainwindow import MainWindow
|
||||||
|
|
||||||
|
|
||||||
|
def test_start_app(openlpapp):
|
||||||
|
assert type(openlpapp) == OpenLP
|
||||||
|
assert type(openlpapp.mainWindow) == MainWindow
|
||||||
|
assert unicode(openlpapp.mainWindow.windowTitle()) == u'OpenLP 2.0'
|
Loading…
Reference in New Issue
Block a user