← Back to team overview

openlp-core team mailing list archive

[Merge] lp:~trb143/openlp/media into lp:openlp

 

Tim Bentley has proposed merging lp:~trb143/openlp/media into lp:openlp.

Requested reviews:
  OpenLP Core (openlp-core)
Related bugs:
  Bug #885150 in OpenLP: "Need non self contained service files"
  https://bugs.launchpad.net/openlp/+bug/885150
  Bug #899714 in OpenLP: "Play/Pause button should be merged"
  https://bugs.launchpad.net/openlp/+bug/899714
  Bug #927829 in OpenLP: "media backends should provide some information about themselves in the settings"
  https://bugs.launchpad.net/openlp/+bug/927829
  Bug #952821 in OpenLP: "Unable to play videos from web"
  https://bugs.launchpad.net/openlp/+bug/952821
  Bug #958198 in OpenLP: "Replacing live background with a video shows theme behind"
  https://bugs.launchpad.net/openlp/+bug/958198
  Bug #999618 in OpenLP: "Video position slider jumps to part way through video"
  https://bugs.launchpad.net/openlp/+bug/999618
  Bug #1022053 in OpenLP: "Previewing media item interferes with live media item"
  https://bugs.launchpad.net/openlp/+bug/1022053
  Bug #1063211 in OpenLP: "Media and Presentation Plugins do not update the service suffix lists if players are added or removed without a restart"
  https://bugs.launchpad.net/openlp/+bug/1063211

For more details, see:
https://code.launchpad.net/~trb143/openlp/media/+merge/134800

Ok time to get this in the Open.  This is for 2.1

New Features.
- Added ability for an individual service item to bypass the "do not go live if blanked" check. 
- Used above feature to allow web to go live automatically.
- Added ability to save a "lite" service with no files (non transportable)
- Media buttons are state aware

Changes
- Split media plugin from media core as 2 players are in core this makes no sense.
- Cleaned up the arrays in Media Controller to use propped indexes not objects. 
- Cleaned up API's
- Simplified the understanding of which Controller and why media was being called.
- Added icon for audio.
- Video length is only called when the item is going to a service.
- Split Play and length to their own methods.
- Used ServiceItem to control playing media not passing individual fields.
- Move the players in to a consistent directory structure.
- refactored and renamed code to make it more understandable.
- fixed bug where if you remove a player (Presentation or Media) it's suffexes are not reset!

-- 
https://code.launchpad.net/~trb143/openlp/media/+merge/134800
Your team OpenLP Core is requested to review the proposed merge of lp:~trb143/openlp/media into lp:openlp.
=== modified file 'openlp/core/lib/__init__.py'
--- openlp/core/lib/__init__.py	2012-11-11 21:16:14 +0000
+++ openlp/core/lib/__init__.py	2012-11-18 08:18:21 +0000
@@ -37,6 +37,14 @@
 
 log = logging.getLogger(__name__)
 
+class ServiceItemContext(object):
+    """
+    The context in which a Service Item is being generated
+    """
+    Preview = 0
+    Live = 1
+    Service = 2
+
 
 class ImageSource(object):
     """
@@ -395,4 +403,4 @@
 from imagemanager import ImageManager
 from renderer import Renderer
 from mediamanageritem import MediaManagerItem
-from openlp.core.utils.actions import ActionList
+

=== modified file 'openlp/core/lib/mediamanageritem.py'
--- openlp/core/lib/mediamanageritem.py	2012-11-11 21:16:14 +0000
+++ openlp/core/lib/mediamanageritem.py	2012-11-18 08:18:21 +0000
@@ -36,7 +36,8 @@
 from PyQt4 import QtCore, QtGui
 
 from openlp.core.lib import SettingsManager, OpenLPToolbar, ServiceItem, \
-    StringContent, build_icon, translate, Receiver, ListWidgetWithDnD
+    StringContent, build_icon, translate, Receiver, ListWidgetWithDnD, \
+    ServiceItemContext
 from openlp.core.lib.searchedit import SearchEdit
 from openlp.core.lib.ui import UiStrings, create_widget_action, \
     critical_error_message_box
@@ -459,7 +460,7 @@
         pass
 
     def generateSlideData(self, serviceItem, item=None, xmlVersion=False,
-        remote=False):
+        remote=False, context=ServiceItemContext.Live):
         raise NotImplementedError(u'MediaManagerItem.generateSlideData needs '
             u'to be defined by the plugin')
 
@@ -521,6 +522,8 @@
         if serviceItem:
             if not item_id:
                 serviceItem.from_plugin = True
+            if remote:
+                serviceItem.will_auto_start = True
             self.plugin.liveController.addServiceItem(serviceItem)
 
     def createItemFromId(self, item_id):
@@ -548,7 +551,8 @@
                     self.addToService(item)
 
     def addToService(self, item=None, replace=None, remote=False):
-        serviceItem = self.buildServiceItem(item, True, remote=remote)
+        serviceItem = self.buildServiceItem(item, True, remote=remote,
+            context=ServiceItemContext.Service)
         if serviceItem:
             serviceItem.from_plugin = False
             self.plugin.serviceManager.addServiceItem(serviceItem,
@@ -581,13 +585,15 @@
                     unicode(translate('OpenLP.MediaManagerItem',
                         'You must select a %s service item.')) % self.title)
 
-    def buildServiceItem(self, item=None, xmlVersion=False, remote=False):
+    def buildServiceItem(self, item=None, xmlVersion=False, remote=False,
+            context=ServiceItemContext.Live):
         """
         Common method for generating a service item
         """
         serviceItem = ServiceItem(self.plugin)
         serviceItem.add_icon(self.plugin.iconPath)
-        if self.generateSlideData(serviceItem, item, xmlVersion, remote):
+        if self.generateSlideData(serviceItem, item, xmlVersion, remote,
+            context):
             return serviceItem
         else:
             return None

=== modified file 'openlp/core/lib/serviceitem.py'
--- openlp/core/lib/serviceitem.py	2012-11-11 21:16:14 +0000
+++ openlp/core/lib/serviceitem.py	2012-11-18 08:18:21 +0000
@@ -53,7 +53,7 @@
 
 class ItemCapabilities(object):
     """
-    Provides an enumeration of a serviceitem's capabilities
+    Provides an enumeration of a service item's capabilities
     """
     CanPreview = 1
     CanEdit = 2
@@ -70,6 +70,7 @@
     CanSoftBreak = 13
     CanWordSplit = 14
     HasBackgroundAudio = 15
+    CanAutoStartForLive = 16
 
 
 class ServiceItem(object):
@@ -123,6 +124,7 @@
         self.background_audio = []
         self.theme_overwritten = False
         self.temporary_edit = False
+        self.will_auto_start = False
         self._new_item()
 
     def _new_item(self):
@@ -256,7 +258,7 @@
             {u'title': file_name, u'image': image, u'path': path})
         self._new_item()
 
-    def get_service_repr(self):
+    def get_service_repr(self, lite_save):
         """
         This method returns some text which can be saved into the service
         file to represent this item.
@@ -280,17 +282,24 @@
             u'end_time': self.end_time,
             u'media_length': self.media_length,
             u'background_audio': self.background_audio,
-            u'theme_overwritten': self.theme_overwritten
+            u'theme_overwritten': self.theme_overwritten,
+            u'will_auto_start': self.will_auto_start
         }
         service_data = []
         if self.service_item_type == ServiceItemType.Text:
             service_data = [slide for slide in self._raw_frames]
         elif self.service_item_type == ServiceItemType.Image:
-            service_data = [slide[u'title'] for slide in self._raw_frames]
+            if lite_save:
+                for slide in self._raw_frames:
+                    service_data.append(
+                        {u'title': slide[u'title'], u'path': slide[u'path']})
+            else:
+                service_data = [slide[u'title'] for slide in self._raw_frames]
         elif self.service_item_type == ServiceItemType.Command:
             for slide in self._raw_frames:
                 service_data.append(
-                    {u'title': slide[u'title'], u'image': slide[u'image']})
+                    {u'title': slide[u'title'], u'image': slide[u'image'],
+                     u'path': slide[u'path']})
         return {u'header': service_header, u'data': service_data}
 
     def set_from_service(self, serviceitem, path=None):
@@ -302,7 +311,9 @@
             The item to extract data from.
 
         ``path``
-            Defaults to *None*. Any path data, usually for images.
+            Defaults to *None*. This is the service manager path for things
+            which have their files saved with them or None when the saved
+            service is lite and the original file paths need to be preserved..
         """
         log.debug(u'set_from_service called with path %s' % path)
         header = serviceitem[u'serviceitem'][u'header']
@@ -324,6 +335,7 @@
         self.start_time = header.get(u'start_time', 0)
         self.end_time = header.get(u'end_time', 0)
         self.media_length = header.get(u'media_length', 0)
+        self.will_auto_start = header.get(u'will_auto_start', False)
         if u'background_audio' in header:
             self.background_audio = []
             for filename in header[u'background_audio']:
@@ -334,14 +346,24 @@
             for slide in serviceitem[u'serviceitem'][u'data']:
                 self._raw_frames.append(slide)
         elif self.service_item_type == ServiceItemType.Image:
-            for text_image in serviceitem[u'serviceitem'][u'data']:
-                filename = os.path.join(path, text_image)
-                self.add_from_image(filename, text_image)
+            if path:
+                for text_image in serviceitem[u'serviceitem'][u'data']:
+                    filename = os.path.join(path, text_image)
+                    self.add_from_image(filename, text_image)
+            else:
+                for text_image in serviceitem[u'serviceitem'][u'data']:
+                    self.add_from_image(text_image[u'path'],
+                        text_image[u'title'])
         elif self.service_item_type == ServiceItemType.Command:
             for text_image in serviceitem[u'serviceitem'][u'data']:
-                filename = os.path.join(path, text_image[u'title'])
-                self.add_from_command(
-                    path, text_image[u'title'], text_image[u'image'])
+                if path:
+                    self.add_from_command(
+                        path, text_image[u'title'], text_image[u'image'])
+                else:
+                    self.add_from_command(
+                        text_image[u'path'], text_image[u'title'],
+                        text_image[u'image'])
+
         self._new_item()
 
     def get_display_title(self):
@@ -423,6 +445,17 @@
         """
         return self.service_item_type == ServiceItemType.Text
 
+    def set_media_length(self, length):
+        """
+        Stores the media length of the item
+
+        ``length``
+            The length of the media item
+        """
+        self.media_length = length
+        if length > 0:
+            self.add_capability(ItemCapabilities.HasVariableStartTime)
+
     def get_frames(self):
         """
         Returns the frames for the ServiceItem
@@ -435,6 +468,8 @@
     def get_rendered_frame(self, row):
         """
         Returns the correct frame for a given list and renders it if required.
+        ``row``
+            The service item slide to be returned
         """
         if self.service_item_type == ServiceItemType.Text:
             return self._display_frames[row][u'html'].split(u'\n')[0]

=== modified file 'openlp/core/ui/__init__.py'
--- openlp/core/ui/__init__.py	2012-11-11 21:16:14 +0000
+++ openlp/core/ui/__init__.py	2012-11-18 08:18:21 +0000
@@ -72,6 +72,16 @@
     Middle = 1
     Bottom = 2
 
+class DisplayControllerType(object):
+    """
+    This is an enumeration class which says where a display controller
+    originated from.
+    """
+    Live = 0
+    Preview = 1
+    Plugin = 2
+
+
 from firsttimeform import FirstTimeForm
 from firsttimelanguageform import FirstTimeLanguageForm
 from themelayoutform import ThemeLayoutForm
@@ -82,7 +92,7 @@
 from maindisplay import MainDisplay, Display
 from servicenoteform import ServiceNoteForm
 from serviceitemeditform import ServiceItemEditForm
-from slidecontroller import SlideController, Controller
+from slidecontroller import SlideController, DisplayController
 from splashscreen import SplashScreen
 from generaltab import GeneralTab
 from themestab import ThemesTab
@@ -98,4 +108,4 @@
 
 __all__ = ['SplashScreen', 'AboutForm', 'SettingsForm', 'MainDisplay',
     'SlideController', 'ServiceManager', 'ThemeManager', 'MediaDockManager',
-    'ServiceItemEditForm', u'FirstTimeForm']
+    'ServiceItemEditForm', 'FirstTimeForm']

=== modified file 'openlp/core/ui/maindisplay.py'
--- openlp/core/ui/maindisplay.py	2012-11-11 21:16:14 +0000
+++ openlp/core/ui/maindisplay.py	2012-11-18 08:18:21 +0000
@@ -53,8 +53,8 @@
 class Display(QtGui.QGraphicsView):
     """
     This is a general display screen class. Here the general display settings
-   will done. It will be used as specialized classes by Main Display and
-   Preview display.
+    will done. It will be used as specialized classes by Main Display and
+    Preview display.
     """
     def __init__(self, parent, live, controller):
         if live:
@@ -327,7 +327,7 @@
         """
         log.debug(u'image to display')
         image = self.imageManager.getImageBytes(path, ImageSource.ImagePlugin)
-        self.controller.mediaController.video_reset(self.controller)
+        self.controller.mediaController.media_reset(self.controller)
         self.displayImage(image)
 
     def displayImage(self, image):

=== modified file 'openlp/core/ui/mainwindow.py'
--- openlp/core/ui/mainwindow.py	2012-11-11 21:16:14 +0000
+++ openlp/core/ui/mainwindow.py	2012-11-18 08:18:21 +0000
@@ -576,6 +576,7 @@
         self.headerSection = u'SettingsImport'
         self.serviceNotSaved = False
         self.aboutForm = AboutForm(self)
+        self.mediaController = MediaController(self)
         self.settingsForm = SettingsForm(self, self)
         self.formattingTagForm = FormattingTagForm(self)
         self.shortcutForm = ShortcutListForm(self)
@@ -585,9 +586,10 @@
         self.pluginManager = PluginManager(plugin_path)
         self.pluginHelpers = {}
         self.imageManager = ImageManager()
-        self.mediaController = MediaController(self)
         # Set up the interface
         self.setupUi(self)
+        # Register the active media players and suffixes
+        self.mediaController.check_available_media_players()
         # Load settings after setupUi so default UI sizes are overwritten
         self.loadSettings()
         # Once settings are loaded update the menu with the recent files.
@@ -1169,7 +1171,7 @@
         if self.serviceManagerContents.isModified():
             ret = self.serviceManagerContents.saveModifiedService()
             if ret == QtGui.QMessageBox.Save:
-                if self.serviceManagerContents.saveFile():
+                if self.serviceManagerContents.decideSaveMethod():
                     self.cleanUp()
                     event.accept()
                 else:

=== modified file 'openlp/core/ui/media/__init__.py'
--- openlp/core/ui/media/__init__.py	2012-11-11 21:16:14 +0000
+++ openlp/core/ui/media/__init__.py	2012-11-18 08:18:21 +0000
@@ -47,7 +47,7 @@
 
 class MediaType(object):
     """
-    An enumeration of possibible Media Types
+    An enumeration of possible Media Types
     """
     Unused = 0
     Audio = 1
@@ -59,7 +59,7 @@
 
 class MediaInfo(object):
     """
-    This class hold the media related infos
+    This class hold the media related info
     """
     file_info = None
     volume = 100
@@ -72,30 +72,26 @@
 
 def get_media_players():
     """
-    This method extract the configured media players and overridden player from
-    the settings.
-
-    ``players_list``
-       A list with all active media players.
-
-    ``overridden_player``
-        Here an special media player is chosen for all media actions.
+    This method extracts the configured media players and overridden player
+    from the settings.
     """
     log.debug(u'get_media_players')
-    players = unicode(Settings().value(u'media/players').toString())
-    if not players:
-        players = u'webkit'
+    saved_players = unicode(Settings().value(u'media/players').toString())
+    if not saved_players:
+        # we must always have a player and Webkit is the core one.
+        saved_players = u'webkit'
     reg_ex = QtCore.QRegExp(".*\[(.*)\].*")
     if Settings().value(u'media/override player',
         QtCore.QVariant(QtCore.Qt.Unchecked)).toInt()[0] == QtCore.Qt.Checked:
-        if reg_ex.exactMatch(players):
+        if reg_ex.exactMatch(saved_players):
             overridden_player = u'%s' % reg_ex.cap(1)
         else:
             overridden_player = u'auto'
     else:
         overridden_player = u''
-    players_list = players.replace(u'[', u'').replace(u']', u'').split(u',')
-    return players_list, overridden_player
+    saved_players_list = saved_players.replace(u'[', u'').\
+        replace(u']',u'').split(u',')
+    return saved_players_list, overridden_player
 
 
 def set_media_players(players_list, overridden_player=u'auto'):
@@ -118,3 +114,4 @@
     Settings().setValue(u'media/players', QtCore.QVariant(players))
 
 from mediacontroller import MediaController
+from playertab import PlayerTab

=== modified file 'openlp/core/ui/media/mediacontroller.py'
--- openlp/core/ui/media/mediacontroller.py	2012-11-11 21:16:14 +0000
+++ openlp/core/ui/media/mediacontroller.py	2012-11-18 08:18:21 +0000
@@ -34,11 +34,12 @@
 
 from openlp.core.lib import OpenLPToolbar, Receiver, translate
 from openlp.core.lib.settings import Settings
-from openlp.core.lib.mediaplayer import MediaPlayer
 from openlp.core.lib.ui import critical_error_message_box
 from openlp.core.ui.media import MediaState, MediaInfo, MediaType, \
     get_media_players, set_media_players
+from openlp.core.ui.media.mediaplayer import MediaPlayer
 from openlp.core.utils import AppLocation
+from openlp.core.ui import DisplayControllerType
 
 log = logging.getLogger(__name__)
 
@@ -46,68 +47,104 @@
     """
     The implementation of the Media Controller. The Media Controller adds an own
     class for every Player. Currently these are QtWebkit, Phonon and Vlc.
+
+    displayControllers are an array of controllers keyed on the
+    slidecontroller or plugin which built them.  ControllerType is the class
+    containing the key values.
+
+    mediaPlayers are an array of media players keyed on player name.
+
+    currentMediaPlayer is an array of player instances keyed on ControllerType.
+
     """
-
     def __init__(self, parent):
-        self.parent = parent
+        self.mainWindow = parent
         self.mediaPlayers = {}
-        self.controller = []
-        self.curDisplayMediaPlayer = {}
+        self.displayControllers = {}
+        self.currentMediaPlayer = {}
         # Timer for video state
         self.timer = QtCore.QTimer()
         self.timer.setInterval(200)
-        self.withLivePreview = False
-        self.check_available_media_players()
         # Signals
         QtCore.QObject.connect(self.timer,
-            QtCore.SIGNAL("timeout()"), self.video_state)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'playbackPlay'), self.video_play)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'playbackPause'), self.video_pause)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'playbackStop'), self.video_stop)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'seekSlider'), self.video_seek)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'volumeSlider'), self.video_volume)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'media_hide'), self.video_hide)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'media_blank'), self.video_blank)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'media_unblank'), self.video_unblank)
+            QtCore.SIGNAL("timeout()"), self.media_state)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'playbackPlay'), self.media_play_msg)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'playbackPause'), self.media_pause_msg)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'playbackStop'), self.media_stop_msg)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'seekSlider'), self.media_seek)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'volumeSlider'), self.media_volume)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'media_hide'), self.media_hide)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'media_blank'), self.media_blank)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'media_unblank'), self.media_unblank)
         # Signals for background video
         QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'songs_hide'), self.video_hide)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'songs_unblank'), self.video_unblank)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'mediaitem_media_rebuild'), self.set_active_players)
+            QtCore.SIGNAL(u'songs_hide'), self.media_hide)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'songs_unblank'), self.media_unblank)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'mediaitem_media_rebuild'), self._set_active_players)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'mediaitem_suffexes'),
+            self._generate_extensions_lists)
 
-    def set_active_players(self):
+    def _set_active_players(self):
+        """
+        Set the active players and available media files
+        """
         savedPlayers = get_media_players()[0]
         for player in self.mediaPlayers.keys():
             self.mediaPlayers[player].isActive = player in savedPlayers
 
-    def register_controllers(self, controller):
-        """
-        Register each media Player controller (Webkit, Phonon, etc) and store
+    def _generate_extensions_lists(self):
+        """
+        Set the active players and available media files
+        """
+        self.audio_extensions_list = []
+        for player in self.mediaPlayers.values():
+            if player.isActive:
+                for item in player.audio_extensions_list:
+                    if not item in self.audio_extensions_list:
+                        self.audio_extensions_list.append(item)
+                        self.mainWindow.serviceManagerContents. \
+                            supportedSuffixes(item[2:])
+        self.video_extensions_list = []
+        for player in self.mediaPlayers.values():
+            if player.isActive:
+                for item in player.video_extensions_list:
+                    if item not in self.video_extensions_list:
+                        self.video_extensions_list.extend(item)
+                        self.mainWindow.serviceManagerContents. \
+                            supportedSuffixes(item[2:])
+
+    def register_players(self, player):
+        """
+        Register each media Player (Webkit, Phonon, etc) and store
         for later use
+
+        ``player``
+            Individual player class which has been enabled
         """
-        self.mediaPlayers[controller.name] = controller
+        self.mediaPlayers[player.name] = player
 
     def check_available_media_players(self):
         """
-        Check to see if we have any media Player's available. If Not do not
-        install the plugin.
+        Check to see if we have any media Player's available.
         """
-        log.debug(u'check_available_media_players')
+        log.debug(u'_check_available_media_players')
         controller_dir = os.path.join(
             AppLocation.get_directory(AppLocation.AppDir),
             u'core', u'ui', u'media')
         for filename in os.listdir(controller_dir):
-            if filename.endswith(u'player.py'):
+            if filename.endswith(u'player.py') and \
+                not filename == 'mediaplayer.py':
                 path = os.path.join(controller_dir, filename)
                 if os.path.isfile(path):
                     modulename = u'openlp.core.ui.media.' + \
@@ -120,10 +157,10 @@
                     except (ImportError, OSError):
                         log.warn(u'Failed to import %s on path %s',
                             modulename, path)
-        controller_classes = MediaPlayer.__subclasses__()
-        for controller_class in controller_classes:
-            controller = controller_class(self)
-            self.register_controllers(controller)
+        player_classes = MediaPlayer.__subclasses__()
+        for player_class in player_classes:
+            player = player_class(self)
+            self.register_players(player)
         if not self.mediaPlayers:
             return False
         savedPlayers, overriddenPlayer = get_media_players()
@@ -134,31 +171,33 @@
             for invalidPlayer in invalidMediaPlayers:
                 savedPlayers.remove(invalidPlayer)
             set_media_players(savedPlayers, overriddenPlayer)
-        self.set_active_players()
+        self._set_active_players()
+        self._generate_extensions_lists()
         return True
 
-    def video_state(self):
+    def media_state(self):
         """
         Check if there is a running media Player and do updating stuff (e.g.
         update the UI)
         """
-        if not self.curDisplayMediaPlayer.keys():
+        if not self.currentMediaPlayer.keys():
             self.timer.stop()
         else:
             any_active = False
-            for display in self.curDisplayMediaPlayer.keys():
-                self.curDisplayMediaPlayer[display].resize(display)
-                self.curDisplayMediaPlayer[display].update_ui(display)
-                if self.curDisplayMediaPlayer[display].state == \
-                        MediaState.Playing:
+            for source in self.currentMediaPlayer.keys():
+                display = self._define_display(self.displayControllers[source])
+                self.currentMediaPlayer[source].resize(display)
+                self.currentMediaPlayer[source].update_ui(display)
+                if self.currentMediaPlayer[source].state == \
+                    MediaState.Playing:
                     any_active = True
-            # There are still any active players - no need to stop timer.
+        # There are still any active players - no need to stop timer.
             if any_active:
                 return
-
-        # No players are active anymore.
-        for display in self.curDisplayMediaPlayer.keys():
-            if self.curDisplayMediaPlayer[display].state != MediaState.Paused:
+        # no players are active anymore
+        for source in self.currentMediaPlayer.keys():
+            if self.currentMediaPlayer[source].state != MediaState.Paused:
+                display = self._define_display(self.displayControllers[source])
                 display.controller.seekSlider.setSliderPosition(0)
         self.timer.stop()
 
@@ -192,14 +231,22 @@
                 html += player.get_media_display_html()
         return html
 
-    def add_controller_items(self, controller, control_panel):
-        self.controller.append(controller)
-        self.setup_generic_controls(controller, control_panel)
-        self.setup_special_controls(controller, control_panel)
-
-    def setup_generic_controls(self, controller, control_panel):
-        """
-        Add generic media control items (valid for all types of medias)
+    def register_controller(self, controller):
+        """
+        Registers media controls where the players will be placed to run.
+
+        ``controller``
+            The controller where a player will be placed
+        """
+        self.displayControllers[controller.controllerType] = controller
+        self.setup_generic_controls(controller)
+
+    def setup_generic_controls(self, controller):
+        """
+        Set up controls on the control_panel for a given controller
+
+        ``controller``
+            First element is the controller which should be used
         """
         controller.media_info = MediaInfo()
         # Build a Media ToolBar
@@ -212,7 +259,8 @@
         controller.mediabar.addToolbarAction(u'playbackPause',
             text=u'media_playback_pause',
             icon=u':/slides/media_playback_pause.png',
-            tooltip=translate('OpenLP.SlideController', 'Pause playing media.'),
+            tooltip=translate('OpenLP.SlideController',
+            'Pause playing media.'),
             triggers=controller.sendToPlugins)
         controller.mediabar.addToolbarAction(u'playbackStop',
             text=u'media_playback_stop',
@@ -241,7 +289,7 @@
         controller.volumeSlider.setGeometry(QtCore.QRect(90, 160, 221, 24))
         controller.volumeSlider.setObjectName(u'volumeSlider')
         controller.mediabar.addToolbarWidget(controller.volumeSlider)
-        control_panel.addWidget(controller.mediabar)
+        controller.controllerLayout.addWidget(controller.mediabar)
         controller.mediabar.setVisible(False)
         # Signals
         QtCore.QObject.connect(controller.seekSlider,
@@ -249,75 +297,94 @@
         QtCore.QObject.connect(controller.volumeSlider,
             QtCore.SIGNAL(u'valueChanged(int)'), controller.sendToPlugins)
 
-    def setup_special_controls(self, controller, control_panel):
-        """
-        Special media Toolbars will be created here (e.g. for DVD Playback)
-        """
-        controller.media_info = MediaInfo()
-        # TODO: add Toolbar for DVD, ...
 
-    def setup_display(self, display):
+    def setup_display(self, display, preview):
         """
         After a new display is configured, all media related widget will be
         created too
+
+        ``display``
+            Display on which the output is to be played
+
+        ``preview``
+            Whether the display is a main or preview display
         """
         # clean up possible running old media files
         self.finalise()
         # update player status
-        self.set_active_players()
+        self._set_active_players()
         display.hasAudio = True
-        if not self.withLivePreview and \
-            display == self.parent.liveController.previewDisplay:
-            return
-        if display == self.parent.previewController.previewDisplay or \
-            display == self.parent.liveController.previewDisplay:
+        if preview:
             display.hasAudio = False
         for player in self.mediaPlayers.values():
             if player.isActive:
                 player.setup(display)
 
     def set_controls_visible(self, controller, value):
+        """
+        After a new display is configured, all media related widget will be
+        created too
+
+        ``controller``
+            The controller on which controls act.
+
+        ``value``
+            control name to be changed.
+        """
         # Generic controls
         controller.mediabar.setVisible(value)
         if controller.isLive and controller.display:
-            if self.curDisplayMediaPlayer and value:
-                if self.curDisplayMediaPlayer[controller.display] != \
+            if self.currentMediaPlayer and value:
+                if self.currentMediaPlayer[controller.controllerType] != \
                     self.mediaPlayers[u'webkit']:
                     controller.display.setTransparency(False)
-        # Special controls: Here media type specific Controls will be enabled
-        # (e.g. for DVD control, ...)
-        # TODO
 
-    def resize(self, controller, display, player):
+    def resize(self, display, player):
         """
         After Mainwindow changes or Splitter moved all related media widgets
         have to be resized
+
+        ``display``
+            The display on which output is playing.
+
+        ``player``
+            The player which is doing the playing.
         """
         player.resize(display)
 
-    def video(self, controller, file, muted, isBackground, hidden=False,
-            isInfo=False, controlsVisible=True):
+    def video(self, source, serviceItem, hidden=False, videoBehindText=False):
         """
         Loads and starts a video to run with the option of sound
+
+        ``source``
+            Where the call originated form
+
+        ``serviceItem``
+            The player which is doing the playing
+
+        ``hidden``
+            The player which is doing the playing
+
+        ``videoBehindText``
+            Is the video to be played behind text.
         """
         log.debug(u'video')
         isValid = False
+        controller = self.displayControllers[source]
         # stop running videos
-        self.video_reset(controller)
+        self.media_reset(controller)
         controller.media_info = MediaInfo()
-        if muted:
+        if videoBehindText:
             controller.media_info.volume = 0
+            controller.media_info.is_background = True
         else:
             controller.media_info.volume = controller.volumeSlider.value()
-        controller.media_info.file_info = QtCore.QFileInfo(file)
-        controller.media_info.is_background = isBackground
-        display = None
+            controller.media_info.is_background = False
+        controller.media_info.file_info = \
+            QtCore.QFileInfo(serviceItem.get_frame_path())
+        display = self._define_display(controller)
         if controller.isLive:
-            if self.withLivePreview and controller.previewDisplay:
-                display = controller.previewDisplay
-                isValid = self.check_file_type(controller, display)
-            display = controller.display
-            isValid = self.check_file_type(controller, display)
+            isValid = self._check_file_type(controller, display, serviceItem)
             display.override[u'theme'] = u''
             display.override[u'video'] = True
             if controller.media_info.is_background:
@@ -327,10 +394,9 @@
             else:
                 controller.media_info.start_time = \
                     display.serviceItem.start_time
-                controller.media_info.end_time = display.serviceItem.end_time
+                controller.media_info.end_time = serviceItem.end_time
         elif controller.previewDisplay:
-            display = controller.previewDisplay
-            isValid = self.check_file_type(controller, display)
+            isValid = self._check_file_type(controller, display, serviceItem)
         if not isValid:
             # Media could not be loaded correctly
             critical_error_message_box(
@@ -347,33 +413,74 @@
         # Preview requested
         if not controller.isLive:
             autoplay = True
-        # Visible or background requested
-        elif not hidden or controller.media_info.is_background:
+        # Visible or background requested or Service Item wants to autostart
+        elif not hidden or controller.media_info.is_background or \
+             serviceItem.will_auto_start:
             autoplay = True
         # Unblank on load set
         elif Settings().value(u'general/auto unblank',
             QtCore.QVariant(False)).toBool():
             autoplay = True
-        # Start playback only for visible widgets. If we need just load a video
-        # and get video information, do not start playback.
-        if autoplay and not isInfo:
-            if not self.video_play([controller]):
+        if autoplay:
+            if not self.media_play(controller):
                 critical_error_message_box(
                     translate('MediaPlugin.MediaItem', 'Unsupported File'),
                     unicode(translate('MediaPlugin.MediaItem',
                     'Unsupported File')))
                 return False
-        self.set_controls_visible(controller, controlsVisible)
-        log.debug(u'use %s controller' % self.curDisplayMediaPlayer[display])
-        return True
-
-    def check_file_type(self, controller, display):
+        self.set_controls_visible(controller, True)
+        log.debug(u'use %s controller' %
+            self.currentMediaPlayer[controller.controllerType])
+        return True
+
+    def media_length(self, serviceItem):
+        """
+        Loads and starts a media item to obtain the media length
+
+        ``serviceItem``
+            The ServiceItem containing the details to be played.
+        """
+        controller =  self.displayControllers[DisplayControllerType.Plugin]
+        log.debug(u'media_length')
+        # stop running videos
+        self.media_reset(controller)
+        controller.media_info = MediaInfo()
+        controller.media_info.volume = 0
+        controller.media_info.file_info = QtCore.QFileInfo(serviceItem
+            .get_frame_path())
+        display = controller.previewDisplay
+        if not self._check_file_type(controller, display, serviceItem):
+            # Media could not be loaded correctly
+            critical_error_message_box(
+                translate('MediaPlugin.MediaItem', 'Unsupported File'),
+                unicode(translate('MediaPlugin.MediaItem',
+                    'Unsupported File')))
+            return False
+        if not self.media_play(controller):
+            critical_error_message_box(
+                translate('MediaPlugin.MediaItem', 'Unsupported File'),
+                unicode(translate('MediaPlugin.MediaItem',
+                    'Unsupported File')))
+            return False
+        serviceItem.set_media_length(controller.media_info.length)
+        self.media_stop(controller)
+        log.debug(u'use %s controller' %
+            self.currentMediaPlayer[controller.controllerType])
+        return True
+
+    def _check_file_type(self, controller, display, serviceItem):
         """
         Select the correct media Player type from the prioritized Player list
+
+        ``controller``
+            First element is the controller which should be used
+
+        ``serviceItem``
+            The ServiceItem containing the details to be played.
         """
-        usedPlayers, overriddenPlayer = get_media_players()
-        if overriddenPlayer and overriddenPlayer != u'auto':
-            usedPlayers = [overriddenPlayer]
+        usedPlayers = get_media_players()[0]
+        if serviceItem.title != u'Automatic':
+            usedPlayers = [serviceItem.title.lower()]
         if controller.media_info.file_info.isFile():
             suffix = u'*.%s' % \
                 controller.media_info.file_info.suffix().toLower()
@@ -383,83 +490,122 @@
                     if not controller.media_info.is_background or \
                         controller.media_info.is_background and \
                         player.canBackground:
-                        self.resize(controller, display, player)
+                        self.resize(display, player)
                         if player.load(display):
-                            self.curDisplayMediaPlayer[display] = player
+                            self.currentMediaPlayer[controller.controllerType] \
+                                = player
                             controller.media_info.media_type = MediaType.Video
                             return True
                 if suffix in player.audio_extensions_list:
                     if player.load(display):
-                        self.curDisplayMediaPlayer[display] = player
+                        self.currentMediaPlayer[controller.controllerType] \
+                            = player
                         controller.media_info.media_type = MediaType.Audio
                         return True
         else:
             for title in usedPlayers:
                 player = self.mediaPlayers[title]
                 if player.canFolder:
-                    self.resize(controller, display, player)
+                    self.resize(display, player)
                     if player.load(display):
-                        self.curDisplayMediaPlayer[display] = player
+                        self.currentMediaPlayer[controller.controllerType] \
+                            = player
                         controller.media_info.media_type = MediaType.Video
                         return True
         # no valid player found
         return False
 
-    def video_play(self, msg, status=True):
+    def media_play_msg(self, msg, status=True):
         """
         Responds to the request to play a loaded video
 
         ``msg``
             First element is the controller which should be used
         """
-        log.debug(u'video_play')
-        controller = msg[0]
-        for display in self.curDisplayMediaPlayer.keys():
-            if display.controller == controller:
-                if not self.curDisplayMediaPlayer[display].play(display):
-                    return False
-                if status:
-                    display.frame.evaluateJavaScript(u'show_blank("desktop");')
-                    self.curDisplayMediaPlayer[display].set_visible(display,
-                        True)
-                    if controller.isLive:
-                        if controller.hideMenu.defaultAction().isChecked():
-                            controller.hideMenu.defaultAction().trigger()
+        log.debug(u'media_play_msg')
+        self.media_play(msg[0],status)
+
+
+    def media_play(self, controller, status=True):
+        """
+        Responds to the request to play a loaded video
+
+        ``controller``
+            The controller to be played
+        """
+        log.debug(u'media_play')
+        display = self._define_display(controller)
+        if not self.currentMediaPlayer[controller.controllerType].play(display):
+            return False
+        if status:
+            display.frame.evaluateJavaScript(u'show_blank("desktop");')
+            self.currentMediaPlayer[controller.controllerType]\
+                .set_visible(display, True)
+            controller.mediabar.actions[u'playbackPlay'].setVisible(False)
+            controller.mediabar.actions[u'playbackStop'].setVisible(True)
+            controller.mediabar.actions[u'playbackPause'].setVisible(True)
+            if controller.isLive:
+                if controller.hideMenu.defaultAction().isChecked():
+                    controller.hideMenu.defaultAction().trigger()
         # Start Timer for ui updates
         if not self.timer.isActive():
             self.timer.start()
         return True
 
-    def video_pause(self, msg):
-        """
-        Responds to the request to pause a loaded video
-
-        ``msg``
-            First element is the controller which should be used
-        """
-        log.debug(u'video_pause')
-        controller = msg[0]
-        for display in self.curDisplayMediaPlayer.keys():
-            if display.controller == controller:
-                self.curDisplayMediaPlayer[display].pause(display)
-
-    def video_stop(self, msg):
-        """
-        Responds to the request to stop a loaded video
-
-        ``msg``
-            First element is the controller which should be used
-        """
-        log.debug(u'video_stop')
-        controller = msg[0]
-        for display in self.curDisplayMediaPlayer.keys():
-            if display.controller == controller:
-                display.frame.evaluateJavaScript(u'show_blank("black");')
-                self.curDisplayMediaPlayer[display].stop(display)
-                self.curDisplayMediaPlayer[display].set_visible(display, False)
-                controller.seekSlider.setSliderPosition(0)
-
-    def video_volume(self, msg):
+    def media_pause_msg(self, msg):
+        """
+        Responds to the request to pause a loaded video
+
+        ``msg``
+            First element is the controller which should be used
+        """
+        log.debug(u'media_pause_msg')
+        self.media_pause( msg[0])
+
+    def media_pause(self, controller):
+        """
+        Responds to the request to pause a loaded video
+
+        ``controller``
+            The Controller to be paused
+        """
+        log.debug(u'media_pause')
+        display = self._define_display(controller)
+        self.currentMediaPlayer[controller.controllerType].pause(display)
+        controller.mediabar.actions[u'playbackPlay'].setVisible(True)
+        controller.mediabar.actions[u'playbackStop'].setVisible(True)
+        controller.mediabar.actions[u'playbackPause'].setVisible(False)
+
+    def media_stop_msg(self, msg):
+        """
+        Responds to the request to stop a loaded video
+
+        ``msg``
+            First element is the controller which should be used
+        """
+        log.debug(u'media_stop_msg')
+        self.media_stop(msg[0])
+
+    def media_stop(self, controller):
+        """
+        Responds to the request to stop a loaded video
+
+        ``controller``
+            The controller that needs to be stopped
+        """
+        log.debug(u'media_stop')
+        display = self._define_display(controller)
+        if display in self.currentMediaPlayer:
+            display.frame.evaluateJavaScript(u'show_blank("black");')
+            self.currentMediaPlayer[controller.controllerType].stop(display)
+            self.currentMediaPlayer[controller.controllerType] \
+                .set_visible(display, False)
+            controller.seekSlider.setSliderPosition(0)
+            controller.mediabar.actions[u'playbackPlay'].setVisible(True)
+            controller.mediabar.actions[u'playbackStop'].setVisible(False)
+            controller.mediabar.actions[u'playbackPause'].setVisible(False)
+
+    def media_volume(self, msg):
         """
         Changes the volume of a running video
 
@@ -468,12 +614,11 @@
         """
         controller = msg[0]
         vol = msg[1][0]
-        log.debug(u'video_volume %d' % vol)
-        for display in self.curDisplayMediaPlayer.keys():
-            if display.controller == controller:
-                self.curDisplayMediaPlayer[display].volume(display, vol)
+        log.debug(u'media_volume %d' % vol)
+        display = self._define_display(controller)
+        self.currentMediaPlayer[controller.controllerType].volume(display, vol)
 
-    def video_seek(self, msg):
+    def media_seek(self, msg):
         """
         Responds to the request to change the seek Slider of a loaded video
 
@@ -481,29 +626,30 @@
             First element is the controller which should be used
             Second element is a list with the seek Value as first element
         """
-        log.debug(u'video_seek')
+        log.debug(u'media_seek')
         controller = msg[0]
         seekVal = msg[1][0]
-        for display in self.curDisplayMediaPlayer.keys():
-            if display.controller == controller:
-                self.curDisplayMediaPlayer[display].seek(display, seekVal)
+        display = self._define_display(controller)
+        self.currentMediaPlayer[controller.controllerType] \
+            .seek(display, seekVal)
 
-    def video_reset(self, controller):
+    def media_reset(self, controller):
         """
         Responds to the request to reset a loaded video
         """
-        log.debug(u'video_reset')
+        log.debug(u'media_reset')
         self.set_controls_visible(controller, False)
-        for display in self.curDisplayMediaPlayer.keys():
-            if display.controller == controller:
-                display.override = {}
-                self.curDisplayMediaPlayer[display].reset(display)
-                self.curDisplayMediaPlayer[display].set_visible(display, False)
-                display.frame.evaluateJavaScript(u'show_video( \
+        display = self._define_display(controller)
+        if display in self.currentMediaPlayer:
+            display.override = {}
+            self.currentMediaPlayer[controller.controllerType].reset(display)
+            self.currentMediaPlayer[controller.controllerType] \
+                .set_visible(display, False)
+            display.frame.evaluateJavaScript(u'show_video( \
                 "setBackBoard", null, null, null,"hidden");')
-                del self.curDisplayMediaPlayer[display]
+            del self.currentMediaPlayer[controller.controllerType]
 
-    def video_hide(self, msg):
+    def media_hide(self, msg):
         """
         Hide the related video Widget
 
@@ -513,15 +659,15 @@
         isLive = msg[1]
         if not isLive:
             return
-        controller = self.parent.liveController
-        for display in self.curDisplayMediaPlayer.keys():
-            if display.controller != controller or \
-                self.curDisplayMediaPlayer[display].state != MediaState.Playing:
-                continue
-            self.curDisplayMediaPlayer[display].pause(display)
-            self.curDisplayMediaPlayer[display].set_visible(display, False)
+        controller = self.mainWindow.liveController
+        display = self._define_display(controller)
+        if self.currentMediaPlayer[controller.controllerType].state \
+            == MediaState.Playing:
+            self.currentMediaPlayer[controller.controllerType].pause(display)
+            self.currentMediaPlayer[controller.controllerType] \
+                .set_visible(display, False)
 
-    def video_blank(self, msg):
+    def media_blank(self, msg):
         """
         Blank the related video Widget
 
@@ -534,15 +680,15 @@
         if not isLive:
             return
         Receiver.send_message(u'live_display_hide', hide_mode)
-        controller = self.parent.liveController
-        for display in self.curDisplayMediaPlayer.keys():
-            if display.controller != controller or \
-                self.curDisplayMediaPlayer[display].state != MediaState.Playing:
-                continue
-            self.curDisplayMediaPlayer[display].pause(display)
-            self.curDisplayMediaPlayer[display].set_visible(display, False)
+        controller = self.mainWindow.liveController
+        display = self._define_display(controller)
+        if self.currentMediaPlayer[controller.controllerType].state \
+            == MediaState.Playing:
+            self.currentMediaPlayer[controller.controllerType].pause(display)
+            self.currentMediaPlayer[controller.controllerType] \
+                .set_visible(display, False)
 
-    def video_unblank(self, msg):
+    def media_unblank(self, msg):
         """
         Unblank the related video Widget
 
@@ -554,35 +700,29 @@
         isLive = msg[1]
         if not isLive:
             return
-        controller = self.parent.liveController
-        for display in self.curDisplayMediaPlayer.keys():
-            if display.controller != controller or \
-                self.curDisplayMediaPlayer[display].state != MediaState.Paused:
-                continue
-            if self.curDisplayMediaPlayer[display].play(display):
-                self.curDisplayMediaPlayer[display].set_visible(display, True)
+        controller = self.mainWindow.liveController
+        display = self._define_display(controller)
+        if self.currentMediaPlayer[controller.controllerType].state \
+            != MediaState.Playing:
+            if self.currentMediaPlayer[controller.controllerType].play(display):
+                self.currentMediaPlayer[controller.controllerType] \
+                    .set_visible(display, True)
                 # Start Timer for ui updates
                 if not self.timer.isActive():
                     self.timer.start()
 
-    def get_audio_extensions_list(self):
-        audio_list = []
-        for player in self.mediaPlayers.values():
-            if player.isActive:
-                for item in player.audio_extensions_list:
-                    if not item in audio_list:
-                        audio_list.append(item)
-        return audio_list
-
-    def get_video_extensions_list(self):
-        video_list = []
-        for player in self.mediaPlayers.values():
-            if player.isActive:
-                video_list.extend([item for item in player.video_extensions_list
-                    if item not in video_list])
-        return video_list
-
     def finalise(self):
         self.timer.stop()
-        for controller in self.controller:
-            self.video_reset(controller)
+        for controller in self.displayControllers:
+            self.media_reset(self.displayControllers[controller])
+
+    def _define_display(self, controller):
+        """
+        Extract the correct display for a given controller
+
+        ``controller``
+            Controller to be used
+        """
+        if controller.isLive:
+            return controller.display
+        return controller.previewDisplay

=== renamed file 'openlp/core/lib/mediaplayer.py' => 'openlp/core/ui/media/mediaplayer.py'
--- openlp/core/lib/mediaplayer.py	2012-11-11 21:16:14 +0000
+++ openlp/core/ui/media/mediaplayer.py	2012-11-18 08:18:21 +0000
@@ -31,8 +31,8 @@
 
 class MediaPlayer(object):
     """
-    This is the base class media Player class to provide OpenLP with a pluggable media display
-    framework.
+    This is the base class media Player class to provide OpenLP with a
+    pluggable media display framework.
     """
 
     def __init__(self, parent, name=u'media_player'):
@@ -137,3 +137,9 @@
         Add html code to htmlbuilder
         """
         return u''
+
+    def get_info(self):
+        """
+        Returns Information about the player
+        """
+        return u''

=== modified file 'openlp/core/ui/media/phononplayer.py'
--- openlp/core/ui/media/phononplayer.py	2012-11-11 21:16:14 +0000
+++ openlp/core/ui/media/phononplayer.py	2012-11-18 08:18:21 +0000
@@ -31,11 +31,15 @@
 import mimetypes
 from datetime import datetime
 
+from PyQt4 import QtCore, QtGui
 from PyQt4.phonon import Phonon
 
-from openlp.core.lib import Receiver
-from openlp.core.lib.mediaplayer import MediaPlayer
+from openlp.core.lib import Receiver, translate
+from openlp.core.lib.settings import Settings
+
 from openlp.core.ui.media import MediaState
+from openlp.core.ui.media.mediaplayer import MediaPlayer
+
 
 log = logging.getLogger(__name__)
 
@@ -56,6 +60,20 @@
         u'video/mpeg' : [u'.mp4', u'.mts', u'.mov'],
         u'video/x-ms-wmv': [u'.wmv']}
 
+VIDEO_CSS = u"""
+#videobackboard {
+    z-index:3;
+    background-color: %s;
+}
+#video1 {
+    background-color: %s;
+    z-index:4;
+}
+#video2 {
+    background-color: %s;
+    z-index:4;
+}
+"""
 
 class PhononPlayer(MediaPlayer):
     """
@@ -205,3 +223,20 @@
         if not controller.seekSlider.isSliderDown():
             controller.seekSlider.setSliderPosition(
                 display.mediaObject.currentTime())
+
+    def get_media_display_css(self):
+        """
+        Add css style sheets to htmlbuilder
+        """
+        background = unicode(QtGui.QColor(Settings().value(
+            u'players/background color', QtCore.QVariant(u'#000000'))).name())
+        return VIDEO_CSS % (background,background,background)
+
+    def get_info(self):
+        return(translate('Media.player', 'Phonon is a media player which '
+            'interacts with the operating system to provide media capabilities'
+            '.') +
+            u'<br/> <strong>' + translate('Media.player', 'Audio') +
+            u'</strong><br/>' + unicode(self.audio_extensions_list) +
+            u'<br/><strong>' + translate('Media.player', 'Video') +
+            u'</strong><br/>' + unicode(self.video_extensions_list) + u'<br/>')
\ No newline at end of file

=== added file 'openlp/core/ui/media/playertab.py'
--- openlp/core/ui/media/playertab.py	1970-01-01 00:00:00 +0000
+++ openlp/core/ui/media/playertab.py	2012-11-18 08:18:21 +0000
@@ -0,0 +1,257 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2012 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan      #
+# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub,      #
+# Meinert Jordan, Armin Köhler, Eric Ludin, 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,              #
+# Erode Woldsund, Martin Zibricky                                             #
+# --------------------------------------------------------------------------- #
+# 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 SettingsTab, translate, Receiver
+from openlp.core.lib.ui import UiStrings, create_button
+from openlp.core.lib.settings import Settings
+from openlp.core.ui.media import get_media_players, set_media_players
+
+class MediaQCheckBox(QtGui.QCheckBox):
+    """
+    MediaQCheckBox adds an extra property, playerName to the QCheckBox class.
+    """
+    def setPlayerName(self, name):
+        self.playerName = name
+
+
+class PlayerTab(SettingsTab):
+    """
+    MediaTab is the Media settings tab in the settings dialog.
+    """
+    def __init__(self, parent, mainWindow):
+        self.parent = parent
+        self.mainWindow = mainWindow
+        self.mediaPlayers = mainWindow.mediaController.mediaPlayers
+        self.savedUsedPlayers = None
+        self.iconPath = u':/system/system_settings.png'
+        player_translated = translate('OpenLP.PlayerTab', 'Players')
+        SettingsTab.__init__(self, parent, u'Players', player_translated)
+
+    def setupUi(self):
+        self.setObjectName(u'MediaTab')
+        SettingsTab.setupUi(self)
+        self.bgColorGroupBox = QtGui.QGroupBox(self.leftColumn)
+        self.bgColorGroupBox.setObjectName(u'FontGroupBox')
+        self.formLayout = QtGui.QFormLayout(self.bgColorGroupBox)
+        self.formLayout.setObjectName(u'FormLayout')
+        self.colorLayout = QtGui.QHBoxLayout()
+        self.backgroundColorLabel = QtGui.QLabel(self.bgColorGroupBox)
+        self.backgroundColorLabel.setObjectName(u'BackgroundColorLabel')
+        self.colorLayout.addWidget(self.backgroundColorLabel)
+        self.backgroundColorButton = QtGui.QPushButton(self.bgColorGroupBox)
+        self.backgroundColorButton.setObjectName(u'BackgroundColorButton')
+        self.colorLayout.addWidget(self.backgroundColorButton)
+        self.formLayout.addRow(self.colorLayout)
+        self.informationLabel = QtGui.QLabel(self.bgColorGroupBox)
+        self.informationLabel.setObjectName(u'InformationLabel')
+        self.informationLabel.setWordWrap(True)
+        self.formLayout.addRow(self.informationLabel)
+        self.leftLayout.addWidget(self.bgColorGroupBox)
+        self.leftLayout.addStretch()
+        self.rightColumn.setSizePolicy(
+            QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred)
+        self.rightLayout.addStretch()
+        self.mediaPlayerGroupBox = QtGui.QGroupBox(self.leftColumn)
+        self.mediaPlayerGroupBox.setObjectName(u'mediaPlayerGroupBox')
+        self.mediaPlayerLayout = QtGui.QVBoxLayout(self.mediaPlayerGroupBox)
+        self.mediaPlayerLayout.setObjectName(u'mediaPlayerLayout')
+        self.playerCheckBoxes = {}
+        self.leftLayout.addWidget(self.mediaPlayerGroupBox)
+        self.playerOrderGroupBox = QtGui.QGroupBox(self.leftColumn)
+        self.playerOrderGroupBox.setObjectName(u'playerOrderGroupBox')
+        self.playerOrderLayout = QtGui.QHBoxLayout(self.playerOrderGroupBox)
+        self.playerOrderLayout.setObjectName(u'playerOrderLayout')
+        self.playerOrderlistWidget = QtGui.QListWidget(
+            self.playerOrderGroupBox)
+        sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum,
+            QtGui.QSizePolicy.Expanding)
+        sizePolicy.setHorizontalStretch(0)
+        sizePolicy.setVerticalStretch(0)
+        sizePolicy.setHeightForWidth(self.playerOrderlistWidget.\
+        sizePolicy().hasHeightForWidth())
+        self.playerOrderlistWidget.setSizePolicy(sizePolicy)
+        self.playerOrderlistWidget.setVerticalScrollBarPolicy(
+            QtCore.Qt.ScrollBarAsNeeded)
+        self.playerOrderlistWidget.setHorizontalScrollBarPolicy(
+            QtCore.Qt.ScrollBarAlwaysOff)
+        self.playerOrderlistWidget.setEditTriggers(
+            QtGui.QAbstractItemView.NoEditTriggers)
+        self.playerOrderlistWidget.setObjectName(u'playerOrderlistWidget')
+        self.playerOrderLayout.addWidget(self.playerOrderlistWidget)
+        self.orderingButtonLayout = QtGui.QVBoxLayout()
+        self.orderingButtonLayout.setObjectName(u'orderingButtonLayout')
+        self.orderingButtonLayout.addStretch(1)
+        self.orderingUpButton = create_button(self, u'orderingUpButton',
+            role=u'up', click=self.onUpButtonClicked)
+        self.orderingDownButton = create_button(self, u'orderingDownButton',
+            role=u'down', click=self.onDownButtonClicked)
+        self.orderingButtonLayout.addWidget(self.orderingUpButton)
+        self.orderingButtonLayout.addWidget(self.orderingDownButton)
+        self.orderingButtonLayout.addStretch(1)
+        self.playerOrderLayout.addLayout(self.orderingButtonLayout)
+        self.leftLayout.addWidget(self.playerOrderGroupBox)
+        self.leftLayout.addStretch()
+        self.rightLayout.addStretch()
+        # Signals and slots
+        QtCore.QObject.connect(self.backgroundColorButton,
+            QtCore.SIGNAL(u'clicked()'), self.onbackgroundColorButtonClicked)
+
+    def retranslateUi(self):
+        self.mediaPlayerGroupBox.setTitle(
+            translate('OpenLP.PlayerTab', 'Available Media Players'))
+        self.playerOrderGroupBox.setTitle(
+            translate('OpenLP.PlayerTab', 'Player Search Order'))
+        self.bgColorGroupBox.setTitle(
+            translate('ImagesPlugin.ImageTab', 'Background Color'))
+        self.backgroundColorLabel.setText(
+            translate('ImagesPlugin.ImageTab', 'Default Color:'))
+        self.informationLabel.setText(
+            translate('ImagesPlugin.ImageTab', 'Visible background for images '
+            'with aspect ratio different to screen.'))
+        self.retranslatePlayers()
+
+    def onbackgroundColorButtonClicked(self):
+        new_color = QtGui.QColorDialog.getColor(
+            QtGui.QColor(self.bg_color), self)
+        if new_color.isValid():
+            self.bg_color = new_color.name()
+            self.backgroundColorButton.setStyleSheet(
+                u'background-color: %s' % self.bg_color)
+
+    def onPlayerCheckBoxChanged(self, check_state):
+        player = self.sender().playerName
+        if check_state == QtCore.Qt.Checked:
+            if player not in self.usedPlayers:
+                self.usedPlayers.append(player)
+        else:
+            if player in self.usedPlayers:
+                self.usedPlayers.remove(player)
+        self.updatePlayerList()
+
+    def updatePlayerList(self):
+        self.playerOrderlistWidget.clear()
+        for player in self.usedPlayers:
+            if player in self.playerCheckBoxes.keys():
+                if len(self.usedPlayers) == 1:
+                    # At least one media player has to stay active
+                    self.playerCheckBoxes[u'%s' % player].setEnabled(False)
+                else:
+                    self.playerCheckBoxes[u'%s' % player].setEnabled(True)
+                self.playerOrderlistWidget.addItem(
+                    self.mediaPlayers[unicode(player)].original_name)
+
+    def onUpButtonClicked(self):
+        row = self.playerOrderlistWidget.currentRow()
+        if row <= 0:
+            return
+        item = self.playerOrderlistWidget.takeItem(row)
+        self.playerOrderlistWidget.insertItem(row - 1, item)
+        self.playerOrderlistWidget.setCurrentRow(row - 1)
+        self.usedPlayers.insert(row - 1, self.usedPlayers.pop(row))
+
+    def onDownButtonClicked(self):
+        row = self.playerOrderlistWidget.currentRow()
+        if row == -1 or row > self.playerOrderlistWidget.count() - 1:
+            return
+        item = self.playerOrderlistWidget.takeItem(row)
+        self.playerOrderlistWidget.insertItem(row + 1, item)
+        self.playerOrderlistWidget.setCurrentRow(row + 1)
+        self.usedPlayers.insert(row + 1, self.usedPlayers.pop(row))
+
+    def load(self):
+        if self.savedUsedPlayers:
+            self.usedPlayers = self.savedUsedPlayers
+        self.usedPlayers = get_media_players()[0]
+        self.savedUsedPlayers = self.usedPlayers
+
+        settings = Settings()
+        settings.beginGroup(self.settingsSection)
+        self.updatePlayerList()
+        self.bg_color = unicode(settings.value(
+            u'background color', QtCore.QVariant(u'#000000')).toString())
+        self.initial_color = self.bg_color
+        settings.endGroup()
+        self.backgroundColorButton.setStyleSheet(
+            u'background-color: %s' % self.bg_color)
+
+    def save(self):
+        player_string_changed = False
+        settings = Settings()
+        settings.beginGroup(self.settingsSection)
+        settings.setValue(u'background color', QtCore.QVariant(self.bg_color))
+        settings.endGroup()
+        old_players, override_player = get_media_players()
+        if self.usedPlayers != old_players:
+            # clean old Media stuff
+            set_media_players(self.usedPlayers, override_player)
+            player_string_changed = True
+        if player_string_changed:
+            self.parent.resetSupportedSuffixes()
+            Receiver.send_message(u'mediaitem_media_rebuild')
+            Receiver.send_message(u'config_screen_changed')
+
+    def postSetUp(self, postUpdate=False):
+        """
+        Late setup for players as the MediaController has to be initialised
+        first.
+        """
+        for key, player in self.mediaPlayers.iteritems():
+            player = self.mediaPlayers[key]
+            checkbox = MediaQCheckBox(self.mediaPlayerGroupBox)
+            checkbox.setEnabled(player.available)
+            checkbox.setObjectName(player.name + u'CheckBox')
+            checkbox.setToolTip(player.get_info())
+            checkbox.setPlayerName(player.name)
+            self.playerCheckBoxes[player.name] = checkbox
+            QtCore.QObject.connect(checkbox,QtCore.SIGNAL(u'stateChanged(int)'),
+                self.onPlayerCheckBoxChanged)
+            self.mediaPlayerLayout.addWidget(checkbox)
+            if player.available and player.name in self.usedPlayers:
+                checkbox.setChecked(True)
+            else:
+                checkbox.setChecked(False)
+        self.updatePlayerList()
+        self.retranslatePlayers()
+
+    def retranslatePlayers(self):
+        """
+        Translations for players is dependent on  their setup as well
+         """
+        for key in self.mediaPlayers:
+            player = self.mediaPlayers[key]
+            checkbox = self.playerCheckBoxes[player.name]
+            checkbox.setPlayerName(player.name)
+            if player.available:
+                checkbox.setText(player.display_name)
+            else:
+                checkbox.setText(
+                    unicode(translate('OpenLP.PlayerTab',
+                        '%s (unavailable)')) % player.display_name)
\ No newline at end of file

=== added directory 'openlp/core/ui/media/vendor'
=== added file 'openlp/core/ui/media/vendor/__init__.py'
--- openlp/core/ui/media/vendor/__init__.py	1970-01-01 00:00:00 +0000
+++ openlp/core/ui/media/vendor/__init__.py	2012-11-18 08:18:21 +0000
@@ -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-2012 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan      #
+# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub,      #
+# Meinert Jordan, Armin Köhler, Eric Ludin, 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,              #
+# Erode Woldsund, Martin Zibricky                                             #
+# --------------------------------------------------------------------------- #
+# 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                          #
+###############################################################################

=== renamed file 'openlp/core/ui/media/vlc.py' => 'openlp/core/ui/media/vendor/vlc.py'
=== modified file 'openlp/core/ui/media/vlcplayer.py'
--- openlp/core/ui/media/vlcplayer.py	2012-11-11 21:16:14 +0000
+++ openlp/core/ui/media/vlcplayer.py	2012-11-18 08:18:21 +0000
@@ -35,16 +35,16 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.lib import Receiver
+from openlp.core.lib import Receiver, translate
 from openlp.core.lib.settings import Settings
-from openlp.core.lib.mediaplayer import MediaPlayer
 from openlp.core.ui.media import MediaState
+from openlp.core.ui.media.mediaplayer import MediaPlayer
 
 log = logging.getLogger(__name__)
 
 VLC_AVAILABLE = False
 try:
-    import vlc
+    from openlp.core.ui.media.vendor import vlc
     VLC_AVAILABLE = bool(vlc.get_default_instance())
 except (ImportError, NameError, NotImplementedError):
     pass
@@ -188,7 +188,8 @@
     def play(self, display):
         controller = display.controller
         start_time = 0
-        if controller.media_info.start_time > 0:
+        if self.state != MediaState.Paused and \
+           controller.media_info.start_time > 0:
             start_time = controller.media_info.start_time
         display.vlcMediaPlayer.play()
         if not self.media_state_wait(display, vlc.State.Playing):
@@ -244,3 +245,10 @@
             controller.seekSlider.setSliderPosition( \
                 display.vlcMediaPlayer.get_time())
 
+    def get_info(self):
+        return(translate('Media.player', 'VLC is an external player which '
+            'supports a number of different formats.') +
+            u'<br/> <strong>' + translate('Media.player', 'Audio') +
+            u'</strong><br/>' + unicode(AUDIO_EXT) + u'<br/><strong>' +
+            translate('Media.player', 'Video') + u'</strong><br/>' +
+            unicode(VIDEO_EXT) + u'<br/>')
\ No newline at end of file

=== modified file 'openlp/core/ui/media/webkitplayer.py'
--- openlp/core/ui/media/webkitplayer.py	2012-11-11 21:16:14 +0000
+++ openlp/core/ui/media/webkitplayer.py	2012-11-18 08:18:21 +0000
@@ -27,22 +27,28 @@
 # Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
 ###############################################################################
 
+from PyQt4 import QtCore, QtGui
+
 import logging
 
-from openlp.core.lib.mediaplayer import MediaPlayer
+from openlp.core.lib import translate
 from openlp.core.ui.media import MediaState
+from openlp.core.ui.media.mediaplayer import MediaPlayer
+from openlp.core.lib.settings import Settings
 
 log = logging.getLogger(__name__)
 
 VIDEO_CSS = u"""
 #videobackboard {
     z-index:3;
-    background-color: black;
+    background-color: %s;
 }
 #video1 {
+    background-color: %s;
     z-index:4;
 }
 #video2 {
+    background-color: %s;
     z-index:4;
 }
 """
@@ -277,7 +283,10 @@
         """
         Add css style sheets to htmlbuilder
         """
-        return VIDEO_CSS + FLASH_CSS
+        background = unicode(QtGui.QColor(Settings().value(
+            u'players/background color', QtCore.QVariant(u'#000000'))).name())
+        css = VIDEO_CSS % (background,background,background)
+        return css + FLASH_CSS
 
     def get_media_display_javascript(self):
         """
@@ -324,7 +333,6 @@
         return True
 
     def resize(self, display):
-        controller = display.controller
         display.webView.resize(display.size())
 
     def play(self, display):
@@ -431,3 +439,12 @@
             controller.seekSlider.setMaximum(length)
             if not controller.seekSlider.isSliderDown():
                 controller.seekSlider.setSliderPosition(currentTime)
+
+    def get_info(self):
+        return(translate('Media.player', 'Webkit is a media player which runs '
+            'inside a web browser. This player allows text over video to be '
+            'rendered.') +
+            u'<br/> <strong>' + translate('Media.player', 'Audio') +
+            u'</strong><br/>' + unicode(AUDIO_EXT) + u'<br/><strong>' +
+            translate('Media.player', 'Video') + u'</strong><br/>' +
+            unicode(VIDEO_EXT) + u'<br/>')
\ No newline at end of file

=== modified file 'openlp/core/ui/servicemanager.py'
--- openlp/core/ui/servicemanager.py	2012-11-11 21:16:14 +0000
+++ openlp/core/ui/servicemanager.py	2012-11-18 08:18:21 +0000
@@ -99,8 +99,8 @@
     """
     Manages the services. This involves taking text strings from plugins and
     adding them to the service. This service can then be zipped up with all
-    the resources used into one OSZ file for use on any OpenLP v2 installation.
-    Also handles the UI tasks of moving things up and down etc.
+    the resources used into one OSZ or oszl file for use on any OpenLP v2
+    installation. Also handles the UI tasks of moving things up and down etc.
     """
     def __init__(self, mainwindow, parent=None):
         """
@@ -136,7 +136,7 @@
         self.toolbar.addToolbarAction(u'saveService',
             text=UiStrings().SaveService, icon=u':/general/general_save.png',
             tooltip=translate('OpenLP.ServiceManager', 'Save this service.'),
-            triggers=self.saveFile)
+            triggers=self.decideSaveMethod)
         self.toolbar.addSeparator()
         self.themeLabel = QtGui.QLabel(u'%s:' % UiStrings().Theme, self)
         self.themeLabel.setMargin(3)
@@ -307,6 +307,9 @@
         self.timeAction = create_widget_action(self.menu,
             text=translate('OpenLP.ServiceManager', '&Start Time'),
             icon=u':/media/media_time.png', triggers=self.onStartTimeForm)
+        self.autoStartAction = create_widget_action(self.menu,
+            text=u'',
+            icon=u':/media/media_time.png', triggers=self.onAutoStart)
         # Add already existing delete action to the menu.
         self.menu.addAction(self.serviceManagerList.delete)
         self.menu.addSeparator()
@@ -359,6 +362,7 @@
             self.shortFileName())
         Settings(). \
             setValue(u'servicemanager/last file',QtCore.QVariant(fileName))
+        self._saveLite = True if self._fileName.endswith(u'.oszl') else False
 
     def fileName(self):
         """
@@ -380,6 +384,13 @@
             u'advanced/expand service item',
             QtCore.QVariant(u'False')).toBool()
 
+    def resetSupportedSuffixes(self):
+        """
+        Resets the Suffexes list.
+
+        """
+        self.suffixes = []
+
     def supportedSuffixes(self, suffix):
         """
         Adds Suffixes supported to the master list.  Called from Plugins.
@@ -387,7 +398,8 @@
         ``suffix``
             New Suffix to be supported
         """
-        self.suffixes.append(suffix)
+        if not suffix in self.suffixes:
+            self.suffixes.append(suffix)
 
     def onNewServiceClicked(self):
         """
@@ -398,7 +410,7 @@
             if result == QtGui.QMessageBox.Cancel:
                 return False
             elif result == QtGui.QMessageBox.Save:
-                if not self.saveFile():
+                if not self.decideSaveMethod():
                     return False
         self.newFile()
 
@@ -416,7 +428,7 @@
             if result == QtGui.QMessageBox.Cancel:
                 return False
             elif result == QtGui.QMessageBox.Save:
-                self.saveFile()
+                self.decideSaveMethod()
         if not loadFile:
             fileName = unicode(QtGui.QFileDialog.getOpenFileName(
                 self.mainwindow,
@@ -424,7 +436,7 @@
                 SettingsManager.get_last_dir(
                 self.mainwindow.serviceManagerSettingsSection),
                 translate('OpenLP.ServiceManager',
-                'OpenLP Service Files (*.osz)')))
+                'OpenLP Service Files (*.osz *.oszl)')))
             if not fileName:
                 return False
         else:
@@ -596,6 +608,73 @@
         delete_file(temp_file_name)
         return success
 
+    def saveLocalFile(self):
+        """
+        Save the current service file.
+
+        A temporary file is created so that we don't overwrite the existing one
+        and leave a mangled service file should there be an error when saving.
+        No files are added to this version of the service as it is deisgned
+        to only work on the machine it was save on if there are files.
+        """
+        if not self.fileName():
+            return self.saveFileAs()
+        temp_file, temp_file_name = mkstemp(u'.oszl', u'openlp_')
+        # We don't need the file handle.
+        os.close(temp_file)
+        log.debug(temp_file_name)
+        path_file_name = unicode(self.fileName())
+        path, file_name = os.path.split(path_file_name)
+        basename = os.path.splitext(file_name)[0]
+        service_file_name = '%s.osd' % basename
+        log.debug(u'ServiceManager.saveFile - %s', path_file_name)
+        SettingsManager.set_last_dir(
+            self.mainwindow.serviceManagerSettingsSection,
+            path)
+        service = []
+        Receiver.send_message(u'cursor_busy')
+        # Number of items + 1 to zip it
+        self.mainwindow.displayProgressBar(len(self.serviceItems) + 1)
+        for item in self.serviceItems:
+            self.mainwindow.incrementProgressBar()
+            service_item = item[u'service_item']. \
+                get_service_repr(self._saveLite)
+            #@todo check for file item on save.
+            service.append({u'serviceitem': service_item})
+            self.mainwindow.incrementProgressBar()
+        service_content = cPickle.dumps(service)
+        zip = None
+        success = True
+        self.mainwindow.incrementProgressBar()
+        try:
+            zip = zipfile.ZipFile(temp_file_name, 'w', zipfile.ZIP_STORED,
+                True)
+            # First we add service contents.
+            zip.writestr(service_file_name.encode(u'utf-8'), service_content)
+        except IOError:
+            log.exception(u'Failed to save service to disk: %s', temp_file_name)
+            Receiver.send_message(u'openlp_error_message', {
+                u'title': translate(u'OpenLP.ServiceManager',
+                    u'Error Saving File'),
+                u'message': translate(u'OpenLP.ServiceManager',
+                    u'There was an error saving your file.')
+            })
+            success = False
+        finally:
+            if zip:
+                zip.close()
+        self.mainwindow.finishedProgressBar()
+        Receiver.send_message(u'cursor_normal')
+        if success:
+            try:
+                shutil.copy(temp_file_name, path_file_name)
+            except:
+                return self.saveFileAs()
+            self.mainwindow.addRecentFile(path_file_name)
+            self.setModified(False)
+        delete_file(temp_file_name)
+        return success
+
     def saveFileAs(self):
         """
         Get a file name and then call :func:`ServiceManager.saveFile` to
@@ -632,9 +711,19 @@
         directory = unicode(SettingsManager.get_last_dir(
             self.mainwindow.serviceManagerSettingsSection))
         path = os.path.join(directory, default_filename)
-        fileName = unicode(QtGui.QFileDialog.getSaveFileName(self.mainwindow,
-            UiStrings().SaveService, path,
-            translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz)')))
+        # SaveAs from osz to oszl is not valid as the files will be deleted
+        # on exit which is not sensible or usable in the long term.
+        if self._fileName.endswith(u'oszl') or not self._fileName:
+            fileName = unicode(QtGui.QFileDialog.getSaveFileName(
+                self.mainwindow, UiStrings().SaveService, path,
+                translate('OpenLP.ServiceManager',
+                    'OpenLP Service Files (*.osz);;'
+                    'OpenLP Service Files - lite (*.oszl)')))
+        else:
+            fileName = unicode(QtGui.QFileDialog.getSaveFileName(
+                self.mainwindow, UiStrings().SaveService, path,
+                translate('OpenLP.ServiceManager',
+                    'OpenLP Service Files (*.osz);;')))
         if not fileName:
             return False
         if os.path.splitext(fileName)[1] == u'':
@@ -643,9 +732,23 @@
             ext = os.path.splitext(fileName)[1]
             fileName.replace(ext, u'.osz')
         self.setFileName(fileName)
-        return self.saveFile()
+        self.decideSaveMethod()
+
+    def decideSaveMethod(self):
+        """
+        Determine which type of save method to use.
+        """
+        if not self.fileName():
+            return self.saveFileAs()
+        if self._saveLite:
+            return self.saveLocalFile()
+        else:
+            return self.saveFile()
 
     def loadFile(self, fileName):
+        """
+        Load an existing service file
+        """
         if not fileName:
             return False
         fileName = unicode(fileName)
@@ -680,12 +783,16 @@
                 items = cPickle.load(fileTo)
                 fileTo.close()
                 self.newFile()
+                self.setFileName(fileName)
                 self.mainwindow.displayProgressBar(len(items))
                 for item in items:
                     self.mainwindow.incrementProgressBar()
                     serviceItem = ServiceItem()
                     serviceItem.renderer = self.mainwindow.renderer
-                    serviceItem.set_from_service(item, self.servicePath)
+                    if self._saveLite:
+                        serviceItem.set_from_service(item)
+                    else:
+                        serviceItem.set_from_service(item, self.servicePath)
                     self.validateItem(serviceItem)
                     self.load_item_uuid = 0
                     if serviceItem.is_capable(ItemCapabilities.OnLoadUpdate):
@@ -697,7 +804,6 @@
                         serviceItem.temporary_edit = self.load_item_temporary
                     self.addServiceItem(serviceItem, repaint=False)
                 delete_file(p_file)
-                self.setFileName(fileName)
                 self.mainwindow.addRecentFile(fileName)
                 self.setModified(False)
                 Settings().setValue(
@@ -760,6 +866,7 @@
         self.maintainAction.setVisible(False)
         self.notesAction.setVisible(False)
         self.timeAction.setVisible(False)
+        self.autoStartAction.setVisible(False)
         if serviceItem[u'service_item'].is_capable(ItemCapabilities.CanEdit)\
             and serviceItem[u'service_item'].edit_id:
             self.editAction.setVisible(True)
@@ -771,6 +878,14 @@
         if serviceItem[u'service_item']\
             .is_capable(ItemCapabilities.HasVariableStartTime):
             self.timeAction.setVisible(True)
+        if serviceItem[u'service_item']\
+            .is_capable(ItemCapabilities.CanAutoStartForLive):
+            self.autoStartAction.setVisible(True)
+            self.autoStartAction.setText(translate('OpenLP.ServiceManager',
+                '&Auto Start - inactive'))
+            if serviceItem[u'service_item'].will_auto_start:
+                self.autoStartAction.setText(translate('OpenLP.ServiceManager',
+                    '&Auto Start - active'))
         self.themeMenu.menuAction().setVisible(False)
         # Set up the theme menu.
         if serviceItem[u'service_item'].is_text() and \
@@ -805,6 +920,14 @@
         if self.startTimeForm.exec_():
             self.repaintServiceList(item, -1)
 
+    def onAutoStart(self):
+        """
+        Toggles to Auto Start Setting.
+        """
+        item = self.findServiceItem()[0]
+        self.serviceItems[item][u'service_item'].will_auto_start = \
+            not self.serviceItems[item][u'service_item'].will_auto_start
+
     def onServiceItemEditForm(self):
         """
         Opens a dialog to edit the service item and update the service
@@ -1095,10 +1218,12 @@
         Validates the service item and if the suffix matches an accepted
         one it allows the item to be displayed.
         """
+        #@todo check file items exist
         if serviceItem.is_command():
             type = serviceItem._raw_frames[0][u'title'].split(u'.')[-1]
             if type.lower() not in self.suffixes:
                 serviceItem.is_valid = False
+            #@todo check file items exist
 
     def cleanUp(self):
         """
@@ -1379,6 +1504,9 @@
                 filename = unicode(url.toLocalFile())
                 if filename.endswith(u'.osz'):
                     self.onLoadServiceClicked(filename)
+                elif filename.endswith(u'.oszl'):
+                    # todo correct
+                    self.onLoadServiceClicked(filename)
         elif link.hasText():
             plugin = unicode(link.text())
             item = self.serviceManagerList.itemAt(event.pos())

=== modified file 'openlp/core/ui/settingsform.py'
--- openlp/core/ui/settingsform.py	2012-11-11 21:16:14 +0000
+++ openlp/core/ui/settingsform.py	2012-11-18 08:18:21 +0000
@@ -35,6 +35,7 @@
 
 from openlp.core.lib import Receiver, build_icon, PluginStatus
 from openlp.core.ui import AdvancedTab, GeneralTab, ThemesTab
+from openlp.core.ui.media import PlayerTab
 from settingsdialog import Ui_SettingsDialog
 
 log = logging.getLogger(__name__)
@@ -47,6 +48,7 @@
         """
         Initialise the settings form
         """
+        self.mainWindow = mainWindow
         QtGui.QDialog.__init__(self, parent)
         self.setupUi(self)
         # General tab
@@ -55,6 +57,8 @@
         self.themesTab = ThemesTab(self, mainWindow)
         # Advanced tab
         self.advancedTab = AdvancedTab(self)
+        # Advanced tab
+        self.playerTab = PlayerTab(self, mainWindow)
 
     def exec_(self):
         # load all the settings
@@ -65,7 +69,8 @@
         self.insertTab(self.generalTab, 0, PluginStatus.Active)
         self.insertTab(self.themesTab, 1, PluginStatus.Active)
         self.insertTab(self.advancedTab, 2, PluginStatus.Active)
-        count = 3
+        self.insertTab(self.playerTab, 3, PluginStatus.Active)
+        count = 4
         for plugin in self.plugins:
             if plugin.settingsTab:
                 self.insertTab(plugin.settingsTab, count, plugin.status)
@@ -94,6 +99,7 @@
         """
         Process the form saving the settings
         """
+        self.resetSuffexes = True
         for tabIndex in range(self.stackedLayout.count()):
             self.stackedLayout.widget(tabIndex).save()
         # Must go after all settings are save
@@ -115,6 +121,7 @@
         self.generalTab.postSetUp()
         self.themesTab.postSetUp()
         self.advancedTab.postSetUp()
+        self.playerTab.postSetUp()
         for plugin in self.plugins:
             if plugin.settingsTab:
                 plugin.settingsTab.postSetUp()
@@ -125,3 +132,13 @@
         """
         self.stackedLayout.setCurrentIndex(tabIndex)
         self.stackedLayout.currentWidget().tabVisible()
+
+    def resetSupportedSuffixes(self):
+        """
+        Control the resetting of the serviceManager suffex list as can be
+        called by a number of settings tab and only needs to be called once
+        per save.
+        """
+        if self.resetSuffexes:
+            self.mainWindow.serviceManagerContents.resetSupportedSuffixes()
+            self.resetSuffexes = False
\ No newline at end of file

=== modified file 'openlp/core/ui/slidecontroller.py'
--- openlp/core/ui/slidecontroller.py	2012-11-11 21:16:14 +0000
+++ openlp/core/ui/slidecontroller.py	2012-11-18 08:18:21 +0000
@@ -39,7 +39,8 @@
 from openlp.core.lib.ui import UiStrings, create_action
 from openlp.core.lib.settings import Settings
 from openlp.core.lib import SlideLimits, ServiceItemAction
-from openlp.core.ui import HideMode, MainDisplay, Display, ScreenList
+from openlp.core.ui import HideMode, MainDisplay, Display, ScreenList, \
+    DisplayControllerType
 from openlp.core.utils.actions import ActionList, CategoryOrder
 
 log = logging.getLogger(__name__)
@@ -53,9 +54,9 @@
         QtGui.QTableWidget.__init__(self, parent.controller)
 
 
-class Controller(QtGui.QWidget):
+class DisplayController(QtGui.QWidget):
     """
-    Controller is a general controller widget.
+    Controller is a general display controller widget.
     """
     def __init__(self, parent, isLive=False):
         """
@@ -64,6 +65,7 @@
         QtGui.QWidget.__init__(self, parent)
         self.isLive = isLive
         self.display = None
+        self.controllerType = DisplayControllerType.Plugin
 
     def sendToPlugins(self, *args):
         """
@@ -77,7 +79,7 @@
         Receiver.send_message('%s' % sender, [controller, args])
 
 
-class SlideController(Controller):
+class SlideController(DisplayController):
     """
     SlideController is the slide controller widget. This widget is what the
     user uses to control the displaying of verses/slides/etc on the screen.
@@ -86,7 +88,7 @@
         """
         Set up the Slide Controller.
         """
-        Controller.__init__(self, parent, isLive)
+        DisplayController.__init__(self, parent, isLive)
         self.screens = ScreenList()
         try:
             self.ratio = float(self.screens.current[u'size'].width()) / \
@@ -193,7 +195,9 @@
             category=self.category, triggers=self.onSlideSelectedNextAction)
         self.toolbar.addAction(self.nextItem)
         self.toolbar.addSeparator()
+        self.controllerType = DisplayControllerType.Preview
         if self.isLive:
+            self.controllerType = DisplayControllerType.Live
             # Hide Menu
             self.hideMenu = QtGui.QToolButton(self.toolbar)
             self.hideMenu.setObjectName(u'hideMenu')
@@ -271,7 +275,7 @@
                 'Edit and reload song preview.'), triggers=self.onEditSong)
         self.controllerLayout.addWidget(self.toolbar)
         # Build the Media Toolbar
-        self.mediaController.add_controller_items(self, self.controllerLayout)
+        self.mediaController.register_controller(self)
         if self.isLive:
             # Build the Song Toolbar
             self.songMenu = QtGui.QToolButton(self.toolbar)
@@ -524,7 +528,7 @@
 
     def liveEscape(self):
         self.display.setVisible(False)
-        self.mediaController.video_stop([self])
+        self.mediaController.media_stop(self)
 
     def toggleDisplay(self, action):
         """
@@ -594,14 +598,14 @@
                 float(self.screens.current[u'size'].height())
         except ZeroDivisionError:
             self.ratio = 1
-        self.mediaController.setup_display(self.display)
+        self.mediaController.setup_display(self.display, False)
         self.previewSizeChanged()
         self.previewDisplay.setup()
         serviceItem = ServiceItem()
         self.previewDisplay.webView.setHtml(build_html(serviceItem,
             self.previewDisplay.screen, None, self.isLive,
             plugins=PluginManager.get_instance().plugins))
-        self.mediaController.setup_display(self.previewDisplay)
+        self.mediaController.setup_display(self.previewDisplay,True)
         if self.serviceItem:
             self.refreshServiceItem()
 
@@ -1351,9 +1355,8 @@
         Respond to the arrival of a media service item
         """
         log.debug(u'SlideController onMediaStart')
-        file = item.get_frame_path()
-        self.mediaController.video(self, file, False, False, self.hideMode())
-        if not self.isLive or self.mediaController.withLivePreview:
+        self.mediaController.video(self.controllerType, item, self.hideMode())
+        if not self.isLive:
             self.previewDisplay.show()
             self.slidePreview.hide()
 
@@ -1362,7 +1365,7 @@
         Respond to a request to close the Video
         """
         log.debug(u'SlideController onMediaClose')
-        self.mediaController.video_reset(self)
+        self.mediaController.media_reset(self)
         self.previewDisplay.hide()
         self.slidePreview.show()
 

=== modified file 'openlp/plugins/bibles/lib/mediaitem.py'
--- openlp/plugins/bibles/lib/mediaitem.py	2012-11-11 21:16:14 +0000
+++ openlp/plugins/bibles/lib/mediaitem.py	2012-11-18 08:18:21 +0000
@@ -32,7 +32,7 @@
 from PyQt4 import QtCore, QtGui
 
 from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \
-    translate, create_separated_list
+    translate, create_separated_list, ServiceItemContext
 from openlp.core.lib.searchedit import SearchEdit
 from openlp.core.lib.settings import Settings
 from openlp.core.lib.ui import UiStrings, set_case_insensitive_completer, \
@@ -894,7 +894,7 @@
         return items
 
     def generateSlideData(self, service_item, item=None, xmlVersion=False,
-        remote=False):
+        remote=False,context=ServiceItemContext.Service):
         """
         Generates and formats the slides for the service item as well as the
         service item's title.

=== modified file 'openlp/plugins/custom/lib/mediaitem.py'
--- openlp/plugins/custom/lib/mediaitem.py	2012-11-11 21:16:14 +0000
+++ openlp/plugins/custom/lib/mediaitem.py	2012-11-18 08:18:21 +0000
@@ -33,7 +33,7 @@
 from sqlalchemy.sql import or_, func
 
 from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \
-    check_item_selected, translate
+    check_item_selected, translate, ServiceItemContext
 from openlp.core.lib.ui import UiStrings
 from openlp.core.lib.settings import Settings
 from openlp.plugins.custom.forms import EditCustomForm
@@ -195,7 +195,7 @@
         self.searchTextEdit.setFocus()
 
     def generateSlideData(self, service_item, item=None, xmlVersion=False,
-        remote=False):
+        remote=False, context=ServiceItemContext.Service):
         item_id = self._getIdOfItemToGenerate(item, self.remoteCustom)
         service_item.add_capability(ItemCapabilities.CanEdit)
         service_item.add_capability(ItemCapabilities.CanPreview)

=== modified file 'openlp/plugins/images/lib/mediaitem.py'
--- openlp/plugins/images/lib/mediaitem.py	2012-11-11 21:16:14 +0000
+++ openlp/plugins/images/lib/mediaitem.py	2012-11-18 08:18:21 +0000
@@ -34,7 +34,7 @@
 
 from openlp.core.lib import MediaManagerItem, build_icon, ItemCapabilities, \
     SettingsManager, translate, check_item_selected, check_directory_exists, \
-    Receiver, create_thumb, validate_thumb
+    Receiver, create_thumb, validate_thumb, ServiceItemContext
 from openlp.core.lib.ui import UiStrings, critical_error_message_box
 from openlp.core.lib.settings import Settings
 from openlp.core.utils import AppLocation, delete_file, locale_compare, \
@@ -153,7 +153,7 @@
             Receiver.send_message(u'cursor_normal')
 
     def generateSlideData(self, service_item, item=None, xmlVersion=False,
-        remote=False):
+        remote=False,context=ServiceItemContext.Service):
         background = QtGui.QColor(Settings().value(self.settingsSection
             + u'/background color', QtCore.QVariant(u'#000000')))
         if item:

=== modified file 'openlp/plugins/media/lib/mediaitem.py'
--- openlp/plugins/media/lib/mediaitem.py	2012-11-11 21:16:14 +0000
+++ openlp/plugins/media/lib/mediaitem.py	2012-11-18 08:18:21 +0000
@@ -34,20 +34,21 @@
 
 from openlp.core.lib import MediaManagerItem, build_icon, ItemCapabilities, \
     SettingsManager, translate, check_item_selected, Receiver, MediaType, \
-    ServiceItem, build_html
+    ServiceItem, build_html, ServiceItemContext
+from openlp.core.lib.settings import Settings
 from openlp.core.lib.ui import UiStrings, critical_error_message_box, \
     create_horizontal_adjusting_combo_box
-from openlp.core.ui import Controller, Display
+from openlp.core.ui import DisplayController, Display, DisplayControllerType
 from openlp.core.ui.media import get_media_players, set_media_players
 from openlp.core.utils import locale_compare
 
 log = logging.getLogger(__name__)
 
 CLAPPERBOARD = u':/media/slidecontroller_multimedia.png'
-VIDEO = QtGui.QImage(u':/media/media_video.png')
-AUDIO = QtGui.QImage(u':/media/media_audio.png')
-DVD_ICON = QtGui.QImage(u':/media/media_video.png')
-ERROR = QtGui.QImage(u':/general/general_delete.png')
+VIDEO = build_icon(QtGui.QImage(u':/media/media_video.png'))
+AUDIO = build_icon(QtGui.QImage(u':/media/media_audio.png'))
+DVDICON = build_icon(QtGui.QImage(u':/media/media_video.png'))
+ERROR = build_icon(QtGui.QImage(u':/general/general_delete.png'))
 
 class MediaMediaItem(MediaManagerItem):
     """
@@ -58,39 +59,31 @@
     def __init__(self, parent, plugin, icon):
         self.iconPath = u'images/image'
         self.background = False
-        self.previewFunction = CLAPPERBOARD
         self.automatic = u''
         MediaManagerItem.__init__(self, parent, plugin, icon)
         self.singleServiceItem = False
         self.hasSearch = True
         self.mediaObject = None
-        self.mediaController = Controller(parent)
-        self.mediaController.controllerLayout = QtGui.QVBoxLayout()
-        self.plugin.mediaController.add_controller_items(self.mediaController, \
-            self.mediaController.controllerLayout)
-        self.plugin.mediaController.set_controls_visible(self.mediaController, \
+        self.displayController = DisplayController(parent)
+        self.displayController.controllerLayout = QtGui.QVBoxLayout()
+        self.plugin.mediaController.register_controller(self.displayController)
+        self.plugin.mediaController.set_controls_visible(self.displayController,
             False)
-        self.mediaController.previewDisplay = Display(self.mediaController, \
-            False, self.mediaController)
-        self.mediaController.previewDisplay.setGeometry(
+        self.displayController.previewDisplay = Display(self.displayController,
+            False, self.displayController)
+        self.displayController.previewDisplay.hide()
+        self.displayController.previewDisplay.setGeometry(
             QtCore.QRect(0, 0, 300, 300))
-        self.mediaController.previewDisplay.screen = \
-            {u'size':self.mediaController.previewDisplay.geometry()}
-        self.mediaController.previewDisplay.setup()
-        serviceItem = ServiceItem()
-        self.mediaController.previewDisplay.webView.setHtml(build_html( \
-            serviceItem, self.mediaController.previewDisplay.screen, None, \
-            False, None))
-        self.mediaController.previewDisplay.setup()
-        self.plugin.mediaController.setup_display( \
-            self.mediaController.previewDisplay)
-        self.mediaController.previewDisplay.hide()
-
+        self.displayController.previewDisplay.screen = \
+            {u'size':self.displayController.previewDisplay.geometry()}
+        self.displayController.previewDisplay.setup()
+        self.plugin.mediaController.setup_display(
+            self.displayController.previewDisplay, False)
         QtCore.QObject.connect(Receiver.get_receiver(),
             QtCore.SIGNAL(u'video_background_replaced'),
             self.videobackgroundReplaced)
         QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'mediaitem_media_rebuild'), self.rebuild)
+            QtCore.SIGNAL(u'mediaitem_media_rebuild'), self.rebuild_players)
         QtCore.QObject.connect(Receiver.get_receiver(),
             QtCore.SIGNAL(u'config_screen_changed'), self.displaySetup)
         # Allow DnD from the desktop
@@ -98,10 +91,6 @@
 
     def retranslateUi(self):
         self.onNewPrompt = translate('MediaPlugin.MediaItem', 'Select Media')
-        self.onNewFileMasks = unicode(translate('MediaPlugin.MediaItem',
-            'Videos (%s);;Audio (%s);;%s (*)')) % (
-            u' '.join(self.plugin.video_extensions_list),
-            u' '.join(self.plugin.audio_extensions_list), UiStrings().AllFiles)
         self.replaceAction.setText(UiStrings().ReplaceBG)
         self.replaceAction.setToolTip(UiStrings().ReplaceLiveBG)
         self.resetAction.setText(UiStrings().ResetBG)
@@ -157,7 +146,7 @@
         """
         Called to reset the Live background with the media selected,
         """
-        self.plugin.liveController.mediaController.video_reset( \
+        self.plugin.liveController.mediaController.media_reset(
             self.plugin.liveController)
         self.resetAction.setVisible(False)
 
@@ -177,8 +166,15 @@
             item = self.listView.currentItem()
             filename = unicode(item.data(QtCore.Qt.UserRole).toString())
             if os.path.exists(filename):
-                if self.plugin.liveController.mediaController.video( \
-                    self.plugin.liveController, filename, True, True):
+                service_item = ServiceItem()
+                service_item.title = \
+                    unicode(self.displayTypeComboBox.currentText())
+                service_item.shortname = service_item.title
+                (path, name) = os.path.split(filename)
+                service_item.add_from_command(path, name,CLAPPERBOARD)
+                if self.plugin.liveController.mediaController.video(
+                    DisplayControllerType.Live, service_item,
+                        videoBehindText=True):
                     self.resetAction.setVisible(True)
                 else:
                     critical_error_message_box(UiStrings().LiveBGError,
@@ -191,7 +187,7 @@
                     'the media file "%s" no longer exists.')) % filename)
 
     def generateSlideData(self, service_item, item=None, xmlVersion=False,
-        remote=False):
+        remote=False, context=ServiceItemContext.Live):
         if item is None:
             item = self.listView.currentItem()
             if item is None:
@@ -202,37 +198,27 @@
                 # File is no longer present
                 critical_error_message_box(
                     translate('MediaPlugin.MediaItem', 'Missing Media File'),
-                        unicode(translate('MediaPlugin.MediaItem',
-                            'The file %s no longer exists.')) % filename)
-            return False
-        self.mediaLength = 0
-        # Get media information and its length.
-        #
-        # This code (mediaController.video()) starts playback but we
-        # need only media information not video to start. Otherwise
-        # video is played twice. Find another way to get media info
-        # without loading and starting video playback.
-        #
-        # TODO Test getting media length with other media backends
-        # Phonon/Webkit.
-        if self.plugin.mediaController.video(self.mediaController,
-                    filename, muted=False, isBackground=False, isInfo=True,
-                    controlsVisible=False):
-            self.mediaLength = self.mediaController.media_info.length
-            service_item.media_length = self.mediaLength
-            if self.mediaLength > 0:
-                service_item.add_capability(
-                    ItemCapabilities.HasVariableStartTime)
-        else:
-            return False
-        service_item.media_length = self.mediaLength
-        service_item.title = unicode(self.plugin.nameStrings[u'singular'])
+                    unicode(translate('MediaPlugin.MediaItem',
+                        'The file %s no longer exists.')) % filename)
+            return False
+        service_item.title = unicode(self.displayTypeComboBox.currentText())
+        service_item.shortname = service_item.title
+        (path, name) = os.path.split(filename)
+        service_item.add_from_command(path, name, CLAPPERBOARD)
+        # Only get start and end times if going to a service
+        if context == ServiceItemContext.Service:
+            # Start media and obtain the length
+            if not self.plugin.mediaController.media_length(service_item):
+                return False
+        service_item.add_capability(ItemCapabilities.CanAutoStartForLive)
         service_item.add_capability(ItemCapabilities.RequiresMedia)
-        # force a non-existent theme
+        service_item.add_capability(ItemCapabilities.HasDetailedTitleDisplay)
+        if Settings().value(self.settingsSection + u'/media auto start',
+            QtCore.QVariant(QtCore.Qt.Unchecked)).toInt()[0]\
+            == QtCore.Qt.Checked:
+            service_item.will_auto_start = True
+            # force a non-existent theme
         service_item.theme = -1
-        frame = CLAPPERBOARD
-        (path, name) = os.path.split(filename)
-        service_item.add_from_command(path, name, frame)
         return True
 
     def initialise(self):
@@ -241,7 +227,7 @@
         self.loadList(SettingsManager.load_list(self.settingsSection, u'media'))
         self.populateDisplayTypes()
 
-    def rebuild(self):
+    def rebuild_players(self):
         """
         Rebuild the tab in the media manager when changes are made in
         the settings
@@ -249,12 +235,13 @@
         self.populateDisplayTypes()
         self.onNewFileMasks = unicode(translate('MediaPlugin.MediaItem',
             'Videos (%s);;Audio (%s);;%s (*)')) % (
-            u' '.join(self.plugin.video_extensions_list),
-            u' '.join(self.plugin.audio_extensions_list), UiStrings().AllFiles)
+            u' '.join(self.plugin.mediaController.video_extensions_list),
+            u' '.join(self.plugin.mediaController.audio_extensions_list),
+            UiStrings().AllFiles)
 
     def displaySetup(self):
-        self.plugin.mediaController.setup_display( \
-            self.mediaController.previewDisplay)
+        self.plugin.mediaController.setup_display(
+            self.displayController.previewDisplay, False)
 
     def populateDisplayTypes(self):
         """
@@ -305,18 +292,21 @@
             if not os.path.exists(track):
                 filename = os.path.split(unicode(track))[1]
                 item_name = QtGui.QListWidgetItem(filename)
-                item_name.setIcon(build_icon(ERROR))
+                item_name.setIcon(ERROR)
                 item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(track))
             elif track_info.isFile():
                 filename = os.path.split(unicode(track))[1]
                 item_name = QtGui.QListWidgetItem(filename)
-                item_name.setIcon(build_icon(VIDEO))
+                if u'*.%s' % (filename.split(u'.')[-1].lower()) in \
+                    self.plugin.mediaController.audio_extensions_list:
+                    item_name.setIcon(AUDIO)
+                else:
+                    item_name.setIcon(VIDEO)
                 item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(track))
             else:
                 filename = os.path.split(unicode(track))[1]
                 item_name = QtGui.QListWidgetItem(filename)
-                #TODO: add the appropriate Icon
-                #item_name.setIcon(build_icon(DVD_ICON))
+                item_name.setIcon(build_icon(DVDICON))
                 item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(track))
             item_name.setToolTip(track)
             self.listView.addItem(item_name)

=== modified file 'openlp/plugins/media/lib/mediatab.py'
--- openlp/plugins/media/lib/mediatab.py	2012-11-11 21:16:14 +0000
+++ openlp/plugins/media/lib/mediatab.py	2012-11-18 08:18:21 +0000
@@ -33,6 +33,7 @@
 from openlp.core.lib.ui import UiStrings, create_button
 from openlp.core.lib.settings import Settings
 from openlp.core.ui.media import get_media_players, set_media_players
+
 class MediaQCheckBox(QtGui.QCheckBox):
     """
     MediaQCheckBox adds an extra property, playerName to the QCheckBox class.
@@ -45,60 +46,13 @@
     """
     MediaTab is the Media settings tab in the settings dialog.
     """
-    def __init__(self, parent, title, visible_title, media_players, icon_path):
-        self.mediaPlayers = media_players
-        self.savedUsedPlayers = None
+    def __init__(self, parent, title, visible_title, icon_path):
+        self.parent = parent
         SettingsTab.__init__(self, parent, title, visible_title, icon_path)
 
     def setupUi(self):
         self.setObjectName(u'MediaTab')
         SettingsTab.setupUi(self)
-        self.mediaPlayerGroupBox = QtGui.QGroupBox(self.leftColumn)
-        self.mediaPlayerGroupBox.setObjectName(u'mediaPlayerGroupBox')
-        self.mediaPlayerLayout = QtGui.QVBoxLayout(self.mediaPlayerGroupBox)
-        self.mediaPlayerLayout.setObjectName(u'mediaPlayerLayout')
-        self.playerCheckBoxes = {}
-        for key, player in self.mediaPlayers.iteritems():
-            player = self.mediaPlayers[key]
-            checkbox = MediaQCheckBox(self.mediaPlayerGroupBox)
-            checkbox.setEnabled(player.available)
-            checkbox.setObjectName(player.name + u'CheckBox')
-            self.playerCheckBoxes[player.name] = checkbox
-            self.mediaPlayerLayout.addWidget(checkbox)
-        self.leftLayout.addWidget(self.mediaPlayerGroupBox)
-        self.playerOrderGroupBox = QtGui.QGroupBox(self.leftColumn)
-        self.playerOrderGroupBox.setObjectName(u'playerOrderGroupBox')
-        self.playerOrderLayout = QtGui.QHBoxLayout(self.playerOrderGroupBox)
-        self.playerOrderLayout.setObjectName(u'playerOrderLayout')
-        self.playerOrderlistWidget = QtGui.QListWidget( \
-            self.playerOrderGroupBox)
-        sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum,
-            QtGui.QSizePolicy.Expanding)
-        sizePolicy.setHorizontalStretch(0)
-        sizePolicy.setVerticalStretch(0)
-        sizePolicy.setHeightForWidth(self.playerOrderlistWidget. \
-            sizePolicy().hasHeightForWidth())
-        self.playerOrderlistWidget.setSizePolicy(sizePolicy)
-        self.playerOrderlistWidget.setVerticalScrollBarPolicy( \
-            QtCore.Qt.ScrollBarAsNeeded)
-        self.playerOrderlistWidget.setHorizontalScrollBarPolicy( \
-            QtCore.Qt.ScrollBarAlwaysOff)
-        self.playerOrderlistWidget.setEditTriggers( \
-            QtGui.QAbstractItemView.NoEditTriggers)
-        self.playerOrderlistWidget.setObjectName(u'playerOrderlistWidget')
-        self.playerOrderLayout.addWidget(self.playerOrderlistWidget)
-        self.orderingButtonLayout = QtGui.QVBoxLayout()
-        self.orderingButtonLayout.setObjectName(u'orderingButtonLayout')
-        self.orderingButtonLayout.addStretch(1)
-        self.orderingUpButton = create_button(self, u'orderingUpButton',
-            role=u'up', click=self.onUpButtonClicked)
-        self.orderingDownButton = create_button(self, u'orderingDownButton',
-            role=u'down', click=self.onDownButtonClicked)
-        self.orderingButtonLayout.addWidget(self.orderingUpButton)
-        self.orderingButtonLayout.addWidget(self.orderingDownButton)
-        self.orderingButtonLayout.addStretch(1)
-        self.playerOrderLayout.addLayout(self.orderingButtonLayout)
-        self.leftLayout.addWidget(self.playerOrderGroupBox)
         self.advancedGroupBox = QtGui.QGroupBox(self.leftColumn)
         self.advancedGroupBox.setObjectName(u'advancedGroupBox')
         self.advancedLayout = QtGui.QVBoxLayout(self.advancedGroupBox)
@@ -106,110 +60,44 @@
         self.overridePlayerCheckBox = QtGui.QCheckBox(self.advancedGroupBox)
         self.overridePlayerCheckBox.setObjectName(u'overridePlayerCheckBox')
         self.advancedLayout.addWidget(self.overridePlayerCheckBox)
+        self.autoStartCheckBox = QtGui.QCheckBox(self.advancedGroupBox)
+        self.autoStartCheckBox.setObjectName(u'autoStartCheckBox')
+        self.advancedLayout.addWidget(self.autoStartCheckBox)
         self.leftLayout.addWidget(self.advancedGroupBox)
         self.leftLayout.addStretch()
         self.rightLayout.addStretch()
-        for key in self.mediaPlayers:
-            player = self.mediaPlayers[key]
-            checkbox = self.playerCheckBoxes[player.name]
-            QtCore.QObject.connect(checkbox,
-                QtCore.SIGNAL(u'stateChanged(int)'),
-                self.onPlayerCheckBoxChanged)
 
     def retranslateUi(self):
-        self.mediaPlayerGroupBox.setTitle(
-            translate('MediaPlugin.MediaTab', 'Available Media Players'))
-        for key in self.mediaPlayers:
-            player = self.mediaPlayers[key]
-            checkbox = self.playerCheckBoxes[player.name]
-            checkbox.setPlayerName(player.name)
-            if player.available:
-                checkbox.setText(player.display_name)
-            else:
-                checkbox.setText(
-                    unicode(translate('MediaPlugin.MediaTab',
-                    '%s (unavailable)')) % player.display_name)
-        self.playerOrderGroupBox.setTitle(
-            translate('MediaPlugin.MediaTab', 'Player Order'))
         self.advancedGroupBox.setTitle(UiStrings().Advanced)
         self.overridePlayerCheckBox.setText(
             translate('MediaPlugin.MediaTab',
             'Allow media player to be overridden'))
-
-    def onPlayerCheckBoxChanged(self, check_state):
-        player = self.sender().playerName
-        if check_state == QtCore.Qt.Checked:
-            if player not in self.usedPlayers:
-                self.usedPlayers.append(player)
-        else:
-            if player in self.usedPlayers:
-                self.usedPlayers.remove(player)
-        self.updatePlayerList()
-
-    def updatePlayerList(self):
-        self.playerOrderlistWidget.clear()
-        for player in self.usedPlayers:
-            if player in self.playerCheckBoxes.keys():
-                if len(self.usedPlayers) == 1:
-                    # At least one media player has to stay active
-                    self.playerCheckBoxes[u'%s' % player].setEnabled(False)
-                else:
-                    self.playerCheckBoxes[u'%s' % player].setEnabled(True)
-                self.playerOrderlistWidget.addItem(
-                    self.mediaPlayers[unicode(player)].original_name)
-
-    def onUpButtonClicked(self):
-        row = self.playerOrderlistWidget.currentRow()
-        if row <= 0:
-            return
-        item = self.playerOrderlistWidget.takeItem(row)
-        self.playerOrderlistWidget.insertItem(row - 1, item)
-        self.playerOrderlistWidget.setCurrentRow(row - 1)
-        self.usedPlayers.insert(row - 1, self.usedPlayers.pop(row))
-
-    def onDownButtonClicked(self):
-        row = self.playerOrderlistWidget.currentRow()
-        if row == -1 or row > self.playerOrderlistWidget.count() - 1:
-            return
-        item = self.playerOrderlistWidget.takeItem(row)
-        self.playerOrderlistWidget.insertItem(row + 1, item)
-        self.playerOrderlistWidget.setCurrentRow(row + 1)
-        self.usedPlayers.insert(row + 1, self.usedPlayers.pop(row))
+        self.autoStartCheckBox.setText(
+            translate('MediaPlugin.MediaTab',
+                'Start Live items automatically'))
 
     def load(self):
-        if self.savedUsedPlayers:
-            self.usedPlayers = self.savedUsedPlayers
-        self.usedPlayers = get_media_players()[0]
-        self.savedUsedPlayers = self.usedPlayers
-        for key in self.mediaPlayers:
-            player = self.mediaPlayers[key]
-            checkbox = self.playerCheckBoxes[player.name]
-            if player.available and player.name in self.usedPlayers:
-                checkbox.setChecked(True)
-            else:
-                checkbox.setChecked(False)
-        self.updatePlayerList()
         self.overridePlayerCheckBox.setChecked(Settings().value(
             self.settingsSection + u'/override player',
             QtCore.QVariant(QtCore.Qt.Unchecked)).toInt()[0])
+        self.autoStartCheckBox.setChecked(Settings().value(
+            self.settingsSection + u'/media auto start',
+            QtCore.QVariant(QtCore.Qt.Unchecked)).toInt()[0])
 
     def save(self):
         override_changed = False
-        player_string_changed = False
-        old_players, override_player = get_media_players()
-        if self.usedPlayers != old_players:
-            # clean old Media stuff
-            set_media_players(self.usedPlayers, override_player)
-            player_string_changed = True
-            override_changed = True
         setting_key = self.settingsSection + u'/override player'
         if Settings().value(setting_key).toInt()[0] != \
             self.overridePlayerCheckBox.checkState():
             Settings().setValue(setting_key,
                 QtCore.QVariant(self.overridePlayerCheckBox.checkState()))
             override_changed = True
+        setting_key = self.settingsSection + u'/media auto start'
+        if Settings().value(setting_key).toInt()[0] !=\
+           self.autoStartCheckBox.checkState():
+            Settings().setValue(setting_key,
+                QtCore.QVariant(self.autoStartCheckBox.checkState()))
         if override_changed:
-            Receiver.send_message(u'mediaitem_media_rebuild')
-        if player_string_changed:
-            Receiver.send_message(u'mediaitem_media_rebuild')
-            Receiver.send_message(u'config_screen_changed')
+            self.parent.resetSupportedSuffixes()
+            Receiver.send_message(u'mediaitem_media_rebuild')
+            Receiver.send_message(u'mediaitem_suffexes')
\ No newline at end of file

=== modified file 'openlp/plugins/media/mediaplugin.py'
--- openlp/plugins/media/mediaplugin.py	2012-11-11 21:16:14 +0000
+++ openlp/plugins/media/mediaplugin.py	2012-11-18 08:18:21 +0000
@@ -48,14 +48,6 @@
         self.icon = build_icon(self.iconPath)
         # passed with drag and drop messages
         self.dnd_id = u'Media'
-        self.audio_extensions_list = \
-            self.mediaController.get_audio_extensions_list()
-        for ext in self.audio_extensions_list:
-            self.serviceManager.supportedSuffixes(ext[2:])
-        self.video_extensions_list = \
-            self.mediaController.get_video_extensions_list()
-        for ext in self.video_extensions_list:
-            self.serviceManager.supportedSuffixes(ext[2:])
 
     def createSettingsTab(self, parent):
         """
@@ -63,7 +55,7 @@
         """
         visible_name = self.getString(StringContent.VisibleName)
         self.settingsTab = MediaTab(parent, self.name, visible_name[u'title'],
-            self.mediaController.mediaPlayers, self.iconPath)
+            self.iconPath)
 
     def about(self):
         about_text = translate('MediaPlugin', '<strong>Media Plugin</strong>'

=== modified file 'openlp/plugins/presentations/lib/mediaitem.py'
--- openlp/plugins/presentations/lib/mediaitem.py	2012-11-11 21:16:14 +0000
+++ openlp/plugins/presentations/lib/mediaitem.py	2012-11-18 08:18:21 +0000
@@ -34,7 +34,7 @@
 
 from openlp.core.lib import MediaManagerItem, build_icon, SettingsManager, \
     translate, check_item_selected, Receiver, ItemCapabilities, create_thumb, \
-    validate_thumb
+    validate_thumb, ServiceItemContext
 from openlp.core.lib.ui import UiStrings, critical_error_message_box, \
     create_horizontal_adjusting_combo_box
 from openlp.core.lib.settings import Settings
@@ -64,7 +64,10 @@
         self.hasSearch = True
         self.singleServiceItem = False
         QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'mediaitem_presentation_rebuild'), self.rebuild)
+            QtCore.SIGNAL(u'mediaitem_presentation_rebuild'),
+            self.populateDisplayTypes)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'mediaitem_suffexes'), self.buildFileMaskString)
         # Allow DnD from the desktop
         self.listView.activateDnD()
 
@@ -133,14 +136,6 @@
         self.loadList(files, True)
         self.populateDisplayTypes()
 
-    def rebuild(self):
-        """
-        Rebuild the tab in the media manager when changes are made in
-        the settings
-        """
-        self.populateDisplayTypes()
-        self.buildFileMaskString()
-
     def populateDisplayTypes(self):
         """
         Load the combobox with the enabled presentation controllers,
@@ -260,7 +255,7 @@
                 u'presentations', self.getFileList())
 
     def generateSlideData(self, service_item, item=None, xmlVersion=False,
-        remote=False):
+        remote=False, context=ServiceItemContext.Service):
         """
         Load the relevant information for displaying the presentation
         in the slidecontroller. In the case of powerpoints, an image

=== modified file 'openlp/plugins/presentations/lib/presentationtab.py'
--- openlp/plugins/presentations/lib/presentationtab.py	2012-11-11 21:16:14 +0000
+++ openlp/plugins/presentations/lib/presentationtab.py	2012-11-18 08:18:21 +0000
@@ -41,6 +41,7 @@
         """
         Constructor
         """
+        self.parent = parent
         self.controllers = controllers
         SettingsTab.__init__(self, parent, title, visible_title, icon_path)
         self.activated = False
@@ -142,7 +143,9 @@
                 QtCore.QVariant(self.OverrideAppCheckBox.checkState()))
             changed = True
         if changed:
+            self.parent.resetSupportedSuffixes()
             Receiver.send_message(u'mediaitem_presentation_rebuild')
+            Receiver.send_message(u'mediaitem_suffexes')
 
     def tabVisible(self):
         """

=== modified file 'openlp/plugins/songs/lib/mediaitem.py'
--- openlp/plugins/songs/lib/mediaitem.py	2012-11-11 21:16:14 +0000
+++ openlp/plugins/songs/lib/mediaitem.py	2012-11-18 08:18:21 +0000
@@ -37,7 +37,7 @@
 
 from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \
     translate, check_item_selected, PluginStatus, create_separated_list, \
-    check_directory_exists
+    check_directory_exists, ServiceItemContext
 from openlp.core.lib.ui import UiStrings, create_widget_action
 from openlp.core.lib.settings import Settings
 from openlp.core.utils import AppLocation, locale_direct_compare
@@ -451,7 +451,7 @@
         self.onSongListLoad()
 
     def generateSlideData(self, service_item, item=None, xmlVersion=False,
-        remote=False):
+        remote=False, context=ServiceItemContext.Service):
         log.debug(u'generateSlideData: %s, %s, %s' %
             (service_item, item, self.remoteSong))
         item_id = self._getIdOfItemToGenerate(item, self.remoteSong)

=== modified file 'resources/openlp.xml'
--- resources/openlp.xml	2011-03-25 18:50:25 +0000
+++ resources/openlp.xml	2012-11-18 08:18:21 +0000
@@ -18,6 +18,11 @@
     <comment>OpenLP Service File</comment>
     <glob pattern="*.osz"/>
   </mime-type> 
+    <mime-type type="application/x-openlp-service">
+    <sub-class-of type="application/zip"/>
+    <comment>OpenLP Service File</comment>
+    <glob pattern="*.oszl"/>
+  </mime-type> 
   <mime-type type="application/x-openlp-theme">
     <sub-class-of type="application/zip"/>
     <comment>OpenLP Theme File</comment>


Follow ups