← Back to team overview

openlp-core team mailing list archive

[Merge] lp:~marmyshev/openlp/full_background_audio into lp:openlp

 

Dmitriy Marmyshev has proposed merging lp:~marmyshev/openlp/full_background_audio into lp:openlp.

Requested reviews:
  Raoul Snyman (raoul-snyman)
Related bugs:
  Bug #845693 in OpenLP: "Add background audio to custom slides"
  https://bugs.launchpad.net/openlp/+bug/845693

For more details, see:
https://code.launchpad.net/~marmyshev/openlp/full_background_audio/+merge/185998

This is first scratch of new using BG audio
Main idea - to show to a person that he has BG audio in schedule and allow to control it. 
Also BG audio can play continuosly without stopping and can stop if the person want it.
For better understanding we need time of playing for each item in schedule, then we can count is BG playlist long enougth to cover this items (i didn't coded this yet).

I used existed icons to show BG audio status for item - just to show that icons will be.
-- 
https://code.launchpad.net/~marmyshev/openlp/full_background_audio/+merge/185998
Your team OpenLP Core is subscribed to branch lp:openlp.
=== modified file 'openlp/core/lib/serviceitem.py'
--- openlp/core/lib/serviceitem.py	2013-08-31 18:17:38 +0000
+++ openlp/core/lib/serviceitem.py	2013-09-17 10:33:02 +0000
@@ -124,7 +124,7 @@
     CanWordSplit = 14
     HasBackgroundAudio = 15
     CanAutoStartForLive = 16
-
+    
 
 class ServiceItem(object):
     """
@@ -174,6 +174,10 @@
         self.from_service = False
         self.image_border = '#000000'
         self.background_audio = []
+        self.background_audio_stop = False
+        self.background_audio_volume = 100
+        self.background_audio_repeat = Settings().value('core/audio repeat list')
+        self.duration = 0
         self.theme_overwritten = False
         self.temporary_edit = False
         self.auto_play_slides_once = False
@@ -190,6 +194,36 @@
         """
         self.unique_identifier = str(uuid.uuid1())
         self.validate_item()
+    
+    def set_duration(self, duration = 0):
+        """
+        Split time up into hours minutes and seconds from secongs
+        """
+        if duration == 0:
+            if self.service_item_type == ServiceItemType.Text or self.service_item_type == ServiceItemType.Image:
+                delay = self.timed_slide_interval
+                if delay == 0:
+                    delay = Settings().value('core/loop delay')
+                duration = len(self._raw_frames) * delay
+            if self.service_item_type == ServiceItemType.Command:
+#                duration = self.media_length
+                if self.end_time > 0 or self.end_time > self.start_time:
+                    duration = self.end_time - self.start_time
+#                else:
+#                    duration = duration - self.start_time
+        self.duration = duration
+        if self.duration == 0:
+            self._duration_text = '00:00'
+        else:
+            seconds = self.duration
+            hours = int(seconds / 3600)
+            seconds -= 3600 * hours
+            minutes = int(seconds / 60)
+            seconds -= 60 * minutes
+            if hours == 0:
+                self._duration_text = '%s:%s' % (str(minutes), str(seconds))
+            else:
+                self._duration_text = '%s:%s:%s' % (str(hours),  str(minutes), str(seconds))
 
     def add_capability(self, capability):
         """
@@ -287,6 +321,7 @@
         self.service_item_type = ServiceItemType.Image
         self._raw_frames.append({'title': title, 'path': path})
         self.image_manager.add_image(path, ImageSource.ImagePlugin, self.image_border)
+#        self.set_duration()
         self._new_item()
 
     def add_from_text(self, raw_slide, verse_tag=None):
@@ -301,6 +336,7 @@
         self.service_item_type = ServiceItemType.Text
         title = raw_slide[:30].split('\n')[0]
         self._raw_frames.append({'title': title, 'raw_slide': raw_slide, 'verseTag': verse_tag})
+#        self.set_duration()
         self._new_item()
 
     def add_from_command(self, path, file_name, image):
@@ -318,6 +354,7 @@
         """
         self.service_item_type = ServiceItemType.Command
         self._raw_frames.append({'title': file_name, 'image': image, 'path': path})
+        self.set_duration()
         self._new_item()
 
     def get_service_repr(self, lite_save):
@@ -347,6 +384,10 @@
             'end_time': self.end_time,
             'media_length': self.media_length,
             'background_audio': self.background_audio,
+            'background_audio_stop': self.background_audio_stop, 
+            'background_audio_volume': self.background_audio_volume, 
+            'background_audio_repeat': self.background_audio_repeat, 
+            'duration': self.duration, 
             'theme_overwritten': self.theme_overwritten,
             'will_auto_start': self.will_auto_start,
             'processor': self.processor
@@ -403,7 +444,7 @@
         self.will_auto_start = header.get('will_auto_start', False)
         self.processor = header.get('processor', None)
         self.has_original_files = True
-        #TODO Remove me in 2,3 build phase
+        #TODO: Remove me in 2,3 build phase
         if self.is_capable(ItemCapabilities.HasDetailedTitleDisplay):
             self.capabilities.remove(ItemCapabilities.HasDetailedTitleDisplay)
             self.processor = self.title
@@ -413,6 +454,11 @@
             for filename in header['background_audio']:
                 # Give them real file paths
                 self.background_audio.append(os.path.join(path, filename))
+        self.background_audio_stop = header.get('background_audio_stop',  True)
+        self.background_audio_volume = header.get('background_audio_volume',  100)
+        repeat_track_list = Settings().value('core/audio repeat list')
+        self.background_audio_repeat = header.get('background_audio_repeat',  repeat_track_list)
+        self.set_duration(header.get('duration',  0))
         self.theme_overwritten = header.get('theme_overwritten', False)
         if self.service_item_type == ServiceItemType.Text:
             for slide in serviceitem['serviceitem']['data']:
@@ -450,6 +496,13 @@
                 return self.title
             else:
                 return self._raw_frames[0]['title']
+    
+    def get_duration(self):
+        """
+        Returns the duration text of the service item.
+        """
+        self.set_duration()
+        return self._duration_text
 
     def merge(self, other):
         """

=== modified file 'openlp/core/ui/__init__.py'
--- openlp/core/ui/__init__.py	2013-08-31 18:17:38 +0000
+++ openlp/core/ui/__init__.py	2013-09-17 10:33:02 +0000
@@ -83,6 +83,7 @@
 from .themeform import ThemeForm
 from .filerenameform import FileRenameForm
 from .starttimeform import StartTimeForm
+from .editbackgroundaudioform import EditBackgroundAudioForm
 from .maindisplay import MainDisplay, Display
 from .servicenoteform import ServiceNoteForm
 from .serviceitemeditform import ServiceItemEditForm
@@ -99,9 +100,11 @@
 from .mediadockmanager import MediaDockManager
 from .servicemanager import ServiceManager
 from .thememanager import ThemeManager
+#from .editbackgroundaudioform import EditBackgroundAudioForm
 
 __all__ = ['SplashScreen', 'AboutForm', 'SettingsForm', 'MainDisplay', 'SlideController', 'ServiceManager',
     'ThemeManager', 'MediaDockManager', 'ServiceItemEditForm', 'FirstTimeForm', 'FirstTimeLanguageForm', 'ThemeForm',
     'ThemeLayoutForm', 'FileRenameForm', 'StartTimeForm', 'MainDisplay', 'Display', 'ServiceNoteForm',
     'SlideController', 'DisplayController', 'GeneralTab', 'ThemesTab', 'AdvancedTab', 'PluginForm',
-    'FormattingTagForm', 'ShortcutListForm']
+#    'FormattingTagForm', 'ShortcutListForm']
+    'FormattingTagForm', 'ShortcutListForm',  'EditBackgroundAudioForm']

=== added file 'openlp/core/ui/editbackgroundaudiodialog.py'
--- openlp/core/ui/editbackgroundaudiodialog.py	1970-01-01 00:00:00 +0000
+++ openlp/core/ui/editbackgroundaudiodialog.py	2013-09-17 10:33:02 +0000
@@ -0,0 +1,123 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2013 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan      #
+# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub,      #
+# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer.   #
+# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru,          #
+# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith,             #
+# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock,              #
+# Frode Woldsund, Martin Zibricky, Patrick Zimmermann                         #
+# --------------------------------------------------------------------------- #
+# 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                          #
+###############################################################################
+"""
+The UI widgets for the time dialog
+"""
+from PyQt4 import QtCore, QtGui
+
+from openlp.core.lib import build_icon, translate
+from openlp.core.lib.ui import create_button_box, create_button
+
+
+class Ui_EditBackgroundAudioDialog(object):
+    """
+    The UI widgets for the time dialog
+    """
+    def setupUi(self, edit_background_audio_dialog):
+        """
+        Set up the UI
+        """
+        edit_background_audio_dialog.setObjectName('edit_background_audio_dialog')
+        edit_background_audio_dialog.resize(650, 400)
+        edit_background_audio_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png'))
+        edit_background_audio_dialog.setModal(True)
+        self.dialog_layout = QtGui.QVBoxLayout(edit_background_audio_dialog)
+        self.dialog_layout.setSpacing(8)
+        self.dialog_layout.setContentsMargins(8, 8, 8, 8)
+        self.dialog_layout.setObjectName('dialog_layout')
+        
+        self.audio_layout = QtGui.QHBoxLayout(edit_background_audio_dialog)
+        self.audio_layout.setObjectName('audio_layout')
+        self.audio_list_widget = QtGui.QListWidget(edit_background_audio_dialog)
+        self.audio_list_widget.setObjectName('audio_list_widget')
+        self.audio_layout.addWidget(self.audio_list_widget)
+        self.audio_buttons_layout = QtGui.QVBoxLayout()
+        self.audio_buttons_layout.setObjectName('audio_buttons_layout')
+        self.from_file_button = QtGui.QPushButton(edit_background_audio_dialog)
+        self.from_file_button.setObjectName('from_file_button')
+        self.audio_buttons_layout.addWidget(self.from_file_button)
+        self.from_media_button = QtGui.QPushButton(edit_background_audio_dialog)
+        self.from_media_button.setObjectName('from_media_button')
+        self.audio_buttons_layout.addWidget(self.from_media_button)
+        self.audio_remove_button = QtGui.QPushButton(edit_background_audio_dialog)
+        self.audio_remove_button.setObjectName('audio_remove_button')
+        self.audio_buttons_layout.addWidget(self.audio_remove_button)
+        self.audio_remove_all_button = QtGui.QPushButton(edit_background_audio_dialog)
+        self.audio_remove_all_button.setObjectName('audio_remove_all_button')
+        self.audio_buttons_layout.addWidget(self.audio_remove_all_button)
+        self.audio_buttons_layout.addStretch(1)
+        self.up_button = create_button(self, 'up_button', role='up', click=self.on_up_button_clicked)
+        self.down_button = create_button(self, 'down_button', role='down', click=self.on_down_button_clicked)
+        self.audio_buttons_layout.addWidget(self.up_button)
+        self.audio_buttons_layout.addWidget(self.down_button)
+        self.audio_layout.addLayout(self.audio_buttons_layout)
+        
+        self.dialog_layout.addLayout(self.audio_layout)
+        
+        self.settings_layout = QtGui.QHBoxLayout(edit_background_audio_dialog)
+        self.settings_layout.setObjectName('settings_layout')
+        
+        self.volume_slider_label = QtGui.QLabel(edit_background_audio_dialog)
+        self.volume_slider_label.setObjectName('volume_slider_label')
+        self.settings_layout.addWidget(self.volume_slider_label)
+        
+        self.volume_slider = QtGui.QSlider(QtCore.Qt.Horizontal)
+        self.volume_slider.setTickInterval(10)
+        self.volume_slider.setTickPosition(QtGui.QSlider.TicksAbove)
+        self.volume_slider.setMinimum(0)
+        self.volume_slider.setMaximum(100)
+#        self.volume_slider.setTracking(True)
+        self.volume_slider.setToolTip(translate('OpenLP.SlideController', 'Audio Volume.'))
+#        self.volume_slider.setValue(self.media_info.volume)
+        self.volume_slider.setGeometry(QtCore.QRect(90, 160, 221, 24))
+        self.volume_slider.setObjectName('volume_slider')
+        self.settings_layout.addWidget(self.volume_slider)
+        
+        self.repeat_list_check_box = QtGui.QCheckBox(edit_background_audio_dialog)
+        self.repeat_list_check_box.setObjectName('repeat_list_check_box')
+        self.settings_layout.addWidget(self.repeat_list_check_box)
+        
+        self.dialog_layout.addLayout(self.settings_layout)
+        
+        self.button_box = create_button_box(edit_background_audio_dialog, 'button_box', ['cancel', 'save'])
+        self.dialog_layout.addWidget(self.button_box)
+        self.retranslateUi(edit_background_audio_dialog)
+#        self.setMaximumHeight(self.sizeHint().height())
+
+    def retranslateUi(self, EditBackgroundAudioDialog):
+        """
+        Update the translations on the fly
+        """
+        self.setWindowTitle(translate('OpenLP.EditBackgroundAudioForm', 'Item background audio playlist'))
+        self.from_file_button.setText(translate('OpenLP.EditBackgroundAudioForm', 'Add &File(s)'))
+        self.from_media_button.setText(translate('OpenLP.EditBackgroundAudioForm', 'Add &Media'))
+        self.audio_remove_button.setText(translate('OpenLP.EditBackgroundAudioForm', '&Remove'))
+        self.audio_remove_all_button.setText(translate('OpenLP.EditBackgroundAudioForm', 'Remove &All'))
+        self.volume_slider_label.setText(translate('OpenLP.EditBackgroundAudioForm', 'Volume:'))
+        self.repeat_list_check_box.setText(translate('OpenLP.GeneralTab', 'Repeat track list'))

=== added file 'openlp/core/ui/editbackgroundaudioform.py'
--- openlp/core/ui/editbackgroundaudioform.py	1970-01-01 00:00:00 +0000
+++ openlp/core/ui/editbackgroundaudioform.py	2013-09-17 10:33:02 +0000
@@ -0,0 +1,194 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2013 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan      #
+# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub,      #
+# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer.   #
+# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru,          #
+# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith,             #
+# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock,              #
+# Frode Woldsund, Martin Zibricky, Patrick Zimmermann                         #
+# --------------------------------------------------------------------------- #
+# 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                          #
+###############################################################################
+"""
+The actual start time form.
+"""
+import logging
+import os
+
+from PyQt4 import QtCore,  QtGui
+
+from .editbackgroundaudiodialog import Ui_EditBackgroundAudioDialog
+
+from openlp.core.lib import UiStrings, Registry, PluginStatus, MediaType, translate
+from openlp.plugins.songs.forms.mediafilesform import MediaFilesForm
+
+log = logging.getLogger(__name__)
+
+
+class EditBackgroundAudioForm(QtGui.QDialog, Ui_EditBackgroundAudioDialog):
+    """
+    The start time dialog
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        super(EditBackgroundAudioForm, self).__init__(Registry().get('main_window'))
+        self.setupUi(self)
+        # Connecting signals and slots
+        self.from_file_button.clicked.connect(self.on_audio_add_from_file_button_clicked)
+        self.from_media_button.clicked.connect(self.on_audio_add_from_media_button_clicked)
+        self.audio_remove_button.clicked.connect(self.on_audio_remove_button_clicked)
+        self.audio_remove_all_button.clicked.connect(self.on_audio_remove_all_button_clicked)
+        
+        self.media_form = MediaFilesForm(self)
+
+    def load_media_files(self):
+        """
+        Load the media files into a combobox.
+        """
+        self.from_media_button.setVisible(False)
+        for plugin in self.plugin_manager.plugins:
+            if plugin.name == 'media' and plugin.status == PluginStatus.Active:
+                self.from_media_button.setVisible(True)
+                self.media_form.populateFiles(plugin.media_item.get_list(MediaType.Audio))
+                break
+
+    def exec_(self):
+        """
+        Run the Dialog with correct heading.
+        """
+        self.volume_slider.setValue(self.item['service_item'].background_audio_volume)
+        self.repeat_list_check_box.setChecked(self.item['service_item'].background_audio_repeat)
+        self.load_media_files()
+        self.audio_list_widget.clear()
+        for file_name in self.item['service_item'].background_audio:
+            media_file = QtGui.QListWidgetItem(os.path.split(file_name)[1])
+            media_file.setData(QtCore.Qt.UserRole, file_name)
+            self.audio_list_widget.addItem(media_file)
+        return QtGui.QDialog.exec_(self)
+
+    def on_audio_add_from_file_button_clicked(self):
+        """
+        Loads file(s) from the filesystem.
+        """
+        filters = '%s (*)' % UiStrings().AllFiles
+        filenames = QtGui.QFileDialog.getOpenFileNames(self,
+            translate('SongsPlugin.EditSongForm', 'Open File(s)'), '', filters)
+        for filename in filenames:
+            item = QtGui.QListWidgetItem(os.path.split(str(filename))[1])
+            item.setData(QtCore.Qt.UserRole, filename)
+            self.audio_list_widget.addItem(item)
+
+    def on_audio_add_from_media_button_clicked(self):
+        """
+        Loads file(s) from the media plugin.
+        """
+        if self.media_form.exec_():
+            for filename in self.media_form.getSelectedFiles():
+                item = QtGui.QListWidgetItem(os.path.split(str(filename))[1])
+                item.setData(QtCore.Qt.UserRole, filename)
+                self.audio_list_widget.addItem(item)
+
+    def on_audio_remove_button_clicked(self):
+        """
+        Removes a file from the list.
+        """
+        row = self.audio_list_widget.currentRow()
+        if row == -1:
+            return
+        self.audio_list_widget.takeItem(row)
+
+    def on_audio_remove_all_button_clicked(self):
+        """
+        Removes all files from the list.
+        """
+        self.audio_list_widget.clear()
+
+    def on_up_button_clicked(self):
+        """
+        Moves a file up when the user clicks the up button on the audio tab.
+        """
+        row = self.audio_list_widget.currentRow()
+        if row <= 0:
+            return
+        item = self.audio_list_widget.takeItem(row)
+        self.audio_list_widget.insertItem(row - 1, item)
+        self.audio_list_widget.setCurrentRow(row - 1)
+
+    def on_down_button_clicked(self):
+        """
+        Moves a file down when the user clicks the up button on the audio tab.
+        """
+        row = self.audio_list_widget.currentRow()
+        if row == -1 or row > self.audio_list_widget.count() - 1:
+            return
+        item = self.audio_list_widget.takeItem(row)
+        self.audio_list_widget.insertItem(row + 1, item)
+        self.audio_list_widget.setCurrentRow(row + 1)
+        
+    def accept(self):
+        """
+        When the dialog succeeds, this is run
+        """
+        self.item['service_item'].background_audio_volume = self.volume_slider.value()
+        self.item['service_item'].background_audio_repeat = self.repeat_list_check_box.isChecked()
+        audio_files = self.item['service_item'].background_audio
+        log.debug(audio_files)
+        self.item['service_item'].background_audio = []
+        for row in range(self.audio_list_widget.count()):
+            item = self.audio_list_widget.item(row)
+            filename = item.data(QtCore.Qt.UserRole)
+            self.item['service_item'].background_audio.append(filename)
+        if len(self.item['service_item'].background_audio) > 0:
+            self.item['service_item'].background_audio_stop = True
+        elif len(audio_files) > 0 and len(self.item['service_item'].background_audio) == 0:
+            self.item['service_item'].background_audio_stop = False
+        return QtGui.QDialog.accept(self)
+
+    def _time_split(self, seconds):
+        """
+        Split time up into hours minutes and seconds from secongs
+        """
+        hours = seconds / 3600
+        seconds -= 3600 * hours
+        minutes = seconds / 60
+        seconds -= 60 * minutes
+        return hours, minutes, seconds
+    
+    def _get_plugin_manager(self):
+        """
+        Adds the plugin manager to the class dynamically
+        """
+        if not hasattr(self, '_plugin_manager'):
+            self._plugin_manager = Registry().get('plugin_manager')
+        return self._plugin_manager
+
+    plugin_manager = property(_get_plugin_manager)
+
+    def _get_main_window(self):
+        """
+        Adds the main window to the class dynamically
+        """
+        if not hasattr(self, '_main_window'):
+            self._main_window = Registry().get('main_window')
+        return self._main_window
+
+    main_window = property(_get_main_window)

=== modified file 'openlp/core/ui/servicemanager.py'
--- openlp/core/ui/servicemanager.py	2013-09-07 07:52:52 +0000
+++ openlp/core/ui/servicemanager.py	2013-09-17 10:33:02 +0000
@@ -46,10 +46,11 @@
     UiStrings, build_icon, translate, str_to_bool, check_directory_exists
 from openlp.core.lib.theme import ThemeLevel
 from openlp.core.lib.ui import critical_error_message_box, create_widget_action, find_and_set_in_combo_box
-from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm
+from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm, EditBackgroundAudioForm
 from openlp.core.ui.printserviceform import PrintServiceForm
 from openlp.core.utils import AppLocation, delete_file, split_filename, format_time
 from openlp.core.utils.actions import ActionList, CategoryOrder
+#from openlp.core.ui import EditBackgroundAudioForm
 
 
 class ServiceManagerList(QtGui.QTreeWidget):
@@ -141,6 +142,13 @@
         self.service_manager_list.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
         self.service_manager_list.setAlternatingRowColors(True)
         self.service_manager_list.setHeaderHidden(True)
+        #TODO: set stylesheet for the list and new columns
+        self.service_manager_list.setColumnCount(3)
+        self.service_manager_list.header().setResizeMode(0, QtGui.QHeaderView.Stretch)
+        self.service_manager_list.header().setStretchLastSection(False)
+        #TODO: text align should be right in column 1
+        self.service_manager_list.setColumnWidth(1,  50)
+        self.service_manager_list.setColumnWidth(2,  30)
         self.service_manager_list.setExpandsOnDoubleClick(False)
         self.service_manager_list.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
         self.service_manager_list.customContextMenuRequested.connect(self.context_menu)
@@ -251,6 +259,24 @@
             text=translate('OpenLP.ServiceManager', '&Delay between slides'),
             triggers=self.on_timed_slide_interval)
         self.menu.addSeparator()
+        # Add background audio menu actions
+        self.background_audio_menu = QtGui.QMenu(translate('OpenLP.ServiceManager', '&Background audio'))
+        self.menu.addMenu(self.background_audio_menu)
+        self.background_audio_edit = create_widget_action(self.background_audio_menu,
+            text=translate('OpenLP.ServiceManager', '&Edit playlist...'),
+            triggers=self.on_background_audio_edit)
+        self.background_audio_menu.addSeparator()
+        self.background_audio_stop_group = QtGui.QActionGroup(self.background_audio_menu)
+        self.background_audio_stop_group.setExclusive(True)
+        self.background_audio_continue = create_widget_action(self.background_audio_menu,
+            text=translate('OpenLP.ServiceManager', 'Continue background audio'),
+            checked=False, triggers=self.on_background_audio_continue_checked)
+        self.background_audio_stop_group.addAction(self.background_audio_continue)
+        self.background_audio_stop = create_widget_action(self.background_audio_menu,
+            text=translate('OpenLP.ServiceManager', 'Stop background audio'),
+            checked=False, triggers=self.on_background_audio_stop_checked)
+        self.background_audio_stop_group.addAction(self.background_audio_stop)
+        self.menu.addSeparator()
         self.preview_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', 'Show &Preview'),
             icon=':/general/general_preview.png', triggers=self.make_preview)
         # Add already existing make live action to the menu.
@@ -309,6 +335,7 @@
         self.service_note_form = ServiceNoteForm()
         self.service_item_edit_form = ServiceItemEditForm()
         self.start_time_form = StartTimeForm()
+        self.background_audio_form = EditBackgroundAudioForm()
         # start with the layout
         self.layout = QtGui.QVBoxLayout(self)
         self.layout.setSpacing(0)
@@ -808,6 +835,19 @@
             self.auto_play_slides_menu.menuAction().setVisible(False)
         if service_item['service_item'].is_capable(ItemCapabilities.HasVariableStartTime):
             self.time_action.setVisible(True)
+        if service_item['service_item'].is_capable(ItemCapabilities.HasBackgroundAudio):
+            self.background_audio_menu.menuAction().setVisible(True)
+            if len(service_item['service_item'].background_audio) == 0:
+                self.background_audio_edit .setText(translate('OpenLP.ServiceManager', '&Edit playlist...'))
+                self.background_audio_stop_group.setVisible(True)
+            else:
+                self.background_audio_stop_group.setVisible(False)
+                traks = str(len(service_item['service_item'].background_audio))
+                self.background_audio_edit .setText(translate('OpenLP.ServiceManager', '&Edit playlist (%s)...' % traks))
+            self.background_audio_stop.setChecked(service_item['service_item'].background_audio_stop)
+            self.background_audio_continue.setChecked(not service_item['service_item'].background_audio_stop)
+        else:
+            self.background_audio_menu.menuAction().setVisible(False)
         if service_item['service_item'].is_capable(ItemCapabilities.CanAutoStartForLive):
             self.auto_start_action.setVisible(True)
             self.auto_start_action.setIcon(self.inactive)
@@ -906,6 +946,41 @@
             service_item.auto_play_slides_once = False
         self.set_modified()
 
+    def on_background_audio_edit(self):
+        """
+        Edit background audio playlist
+        """
+        item = self.find_service_item()[0]
+        self.background_audio_form.item = self.service_items[item]
+        if self.background_audio_form.exec_():
+            self.repaint_service_list(item, -1)
+
+    def on_background_audio_continue_checked(self):
+        """
+        Toggle background audio continue
+        """
+        item = self.find_service_item()[0]
+        service_item = self.service_items[item]['service_item']
+        if service_item.background_audio_stop:
+            service_item.background_audio_stop = False
+            self.background_audio_continue.setChecked(True)
+            self.background_audio_stop.setChecked(False)
+            self.set_modified()
+            self.repaint_service_list(item, -1)
+
+    def on_background_audio_stop_checked(self):
+        """
+        Toggle background audio stop
+        """
+        item = self.find_service_item()[0]
+        service_item = self.service_items[item]['service_item']
+        if not service_item.background_audio_stop:
+            service_item.background_audio_stop = True
+            self.background_audio_continue.setChecked(False)
+            self.background_audio_stop.setChecked(True)
+            self.set_modified()
+            self.repaint_service_list(item, -1)
+
     def on_auto_start(self):
         """
         Toggles to Auto Start Setting.
@@ -1166,6 +1241,15 @@
             else:
                 treewidgetitem.setIcon(0, build_icon(':/general/general_delete.png'))
             treewidgetitem.setText(0, serviceitem.get_display_title())
+            treewidgetitem.setText(1, serviceitem.get_duration())
+            treewidgetitem.setTextAlignment(1, QtCore.Qt.AlignRight)
+            #TODO: set icons depends of bg audio status
+            if serviceitem.background_audio_stop and len(serviceitem.background_audio) == 0:
+                treewidgetitem.setIcon(2, build_icon(':/general/general_delete.png'))
+            elif len(serviceitem.background_audio) > 0:
+                treewidgetitem.setIcon(2, build_icon(':/slides/slide_next.png'))
+            else:
+                treewidgetitem.setIcon(2, build_icon(':/services/service_bottom.png'))
             tips = []
             if serviceitem.temporary_edit:
                 tips.append('<strong>%s:</strong> <em>%s</em>' %

=== modified file 'openlp/core/ui/slidecontroller.py'
--- openlp/core/ui/slidecontroller.py	2013-08-31 18:17:38 +0000
+++ openlp/core/ui/slidecontroller.py	2013-09-17 10:33:02 +0000
@@ -735,12 +735,15 @@
             '%s_start' % service_item.name.lower(), [service_item, self.is_live, self.hide_mode(), slideno])
         self.slideList = {}
         if self.is_live:
-            self.song_menu.menu().clear()
-            self.display.audio_player.reset()
-            self.set_audio_items_visibility(False)
-            self.audio_pause_item.setChecked(False)
+            if self.service_item.background_audio_stop or\
+                not self.service_item.is_capable(ItemCapabilities.HasBackgroundAudio):
+                self.song_menu.menu().clear()
+                self.display.audio_player.reset()
+                self.set_audio_items_visibility(False)
+                self.audio_pause_item.setChecked(False)
             # If the current item has background audio
-            if self.service_item.is_capable(ItemCapabilities.HasBackgroundAudio):
+            if self.service_item.is_capable(ItemCapabilities.HasBackgroundAudio) and\
+                len(self.service_item.background_audio) > 0:
                 log.debug('Starting to play...')
                 self.display.audio_player.add_to_playlist(self.service_item.background_audio)
                 self.trackMenu.clear()
@@ -748,6 +751,8 @@
                     action = self.trackMenu.addAction(os.path.basename(self.service_item.background_audio[counter]))
                     action.setData(counter)
                     action.triggered.connect(self.on_track_triggered)
+                #TODO: Set volume level from item
+#                self.display.audio_player.volume = self.service_item.background_audio_volume
                 self.display.audio_player.repeat = Settings().value(
                     self.main_window.general_settings_section + '/audio repeat list')
                 if Settings().value(self.main_window.general_settings_section + '/audio start paused'):

=== modified file 'openlp/plugins/bibles/lib/mediaitem.py'
--- openlp/plugins/bibles/lib/mediaitem.py	2013-08-31 18:17:38 +0000
+++ openlp/plugins/bibles/lib/mediaitem.py	2013-09-17 10:33:02 +0000
@@ -841,6 +841,7 @@
             service_item.add_capability(ItemCapabilities.NoLineBreaks)
         service_item.add_capability(ItemCapabilities.CanPreview)
         service_item.add_capability(ItemCapabilities.CanLoop)
+        service_item.add_capability(ItemCapabilities.HasBackgroundAudio)
         service_item.add_capability(ItemCapabilities.CanWordSplit)
         # Service Item: Title
         service_item.title = create_separated_list(raw_title)

=== modified file 'openlp/plugins/custom/lib/mediaitem.py'
--- openlp/plugins/custom/lib/mediaitem.py	2013-08-31 18:17:38 +0000
+++ openlp/plugins/custom/lib/mediaitem.py	2013-09-17 10:33:02 +0000
@@ -189,6 +189,7 @@
         service_item.add_capability(ItemCapabilities.CanEdit)
         service_item.add_capability(ItemCapabilities.CanPreview)
         service_item.add_capability(ItemCapabilities.CanLoop)
+        service_item.add_capability(ItemCapabilities.HasBackgroundAudio)
         service_item.add_capability(ItemCapabilities.CanSoftBreak)
         service_item.add_capability(ItemCapabilities.OnLoadUpdate)
         custom_slide = self.plugin.manager.get_object(CustomSlide, item_id)

=== modified file 'openlp/plugins/images/lib/mediaitem.py'
--- openlp/plugins/images/lib/mediaitem.py	2013-08-31 18:17:38 +0000
+++ openlp/plugins/images/lib/mediaitem.py	2013-09-17 10:33:02 +0000
@@ -550,6 +550,7 @@
         service_item.add_capability(ItemCapabilities.CanMaintain)
         service_item.add_capability(ItemCapabilities.CanPreview)
         service_item.add_capability(ItemCapabilities.CanLoop)
+        service_item.add_capability(ItemCapabilities.HasBackgroundAudio)
         service_item.add_capability(ItemCapabilities.CanAppend)
         # force a nonexistent theme
         service_item.theme = -1

=== modified file 'openlp/plugins/media/lib/mediaitem.py'
--- openlp/plugins/media/lib/mediaitem.py	2013-08-31 18:17:38 +0000
+++ openlp/plugins/media/lib/mediaitem.py	2013-09-17 10:33:02 +0000
@@ -196,6 +196,11 @@
                 return False
         service_item.add_capability(ItemCapabilities.CanAutoStartForLive)
         service_item.add_capability(ItemCapabilities.RequiresMedia)
+        extension = self.media_controller.audio_extensions_list
+        extension = [x[1:] for x in extension]
+        if os.path.splitext(name)[1] not in extension:
+            service_item.add_capability(ItemCapabilities.HasBackgroundAudio)
+            service_item.background_audio_stop = True
         if Settings().value(self.settings_section + '/media auto start') == QtCore.Qt.Checked:
             service_item.will_auto_start = True
             # force a non-existent theme

=== modified file 'openlp/plugins/presentations/lib/mediaitem.py'
--- openlp/plugins/presentations/lib/mediaitem.py	2013-08-31 18:17:38 +0000
+++ openlp/plugins/presentations/lib/mediaitem.py	2013-09-17 10:33:02 +0000
@@ -246,6 +246,7 @@
                 return False
         service_item.processor = self.display_type_combo_box.currentText()
         service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay)
+        service_item.add_capability(ItemCapabilities.HasBackgroundAudio)
         if not self.display_type_combo_box.currentText():
             return False
         for bitem in items:

=== modified file 'openlp/plugins/songs/lib/mediaitem.py'
--- openlp/plugins/songs/lib/mediaitem.py	2013-08-31 18:17:38 +0000
+++ openlp/plugins/songs/lib/mediaitem.py	2013-09-17 10:33:02 +0000
@@ -400,6 +400,7 @@
         service_item.add_capability(ItemCapabilities.CanEdit)
         service_item.add_capability(ItemCapabilities.CanPreview)
         service_item.add_capability(ItemCapabilities.CanLoop)
+        service_item.add_capability(ItemCapabilities.HasBackgroundAudio)
         service_item.add_capability(ItemCapabilities.OnLoadUpdate)
         service_item.add_capability(ItemCapabilities.AddIfNewItem)
         service_item.add_capability(ItemCapabilities.CanSoftBreak)
@@ -452,8 +453,8 @@
         service_item.xml_version = self.openLyrics.song_to_xml(song)
         # Add the audio file to the service item.
         if song.media_files:
-            service_item.add_capability(ItemCapabilities.HasBackgroundAudio)
             service_item.background_audio = [m.file_name for m in song.media_files]
+            service_item.background_audio_stop = True
         return True
 
     def generate_footer(self, item, song):


Follow ups