← Back to team overview

openlp-core team mailing list archive

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

 

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

Requested reviews:
  OpenLP Core (openlp-core)
Related bugs:
  Bug #1022053 in OpenLP: "Previewing media item interferes with live media item"
  https://bugs.launchpad.net/openlp/+bug/1022053
  Bug #1079874 in OpenLP: "Time of playing viedeo and video length"
  https://bugs.launchpad.net/openlp/+bug/1079874
  Bug #1225772 in OpenLP: "Replaced background with video appears on main window"
  https://bugs.launchpad.net/openlp/+bug/1225772
  Bug #1414563 in OpenLP: "Volume slider shows in preview controller"
  https://bugs.launchpad.net/openlp/+bug/1414563
  Bug #1458347 in OpenLP: "Expose more playback options for VLC"
  https://bugs.launchpad.net/openlp/+bug/1458347
  Bug #1463286 in OpenLP: "Live media restarts when adding media to servicemanager"
  https://bugs.launchpad.net/openlp/+bug/1463286
  Bug #1514155 in OpenLP: "Videos only play audio after resumed from escape item"
  https://bugs.launchpad.net/openlp/+bug/1514155

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

Test proposal to check changes and understand scope of changes.
Fixing up the tests
-- 
Your team OpenLP Core is requested to review the proposed merge of lp:~trb143/openlp/media_timer2 into lp:openlp.
=== modified file 'openlp/core/common/uistrings.py'
--- openlp/core/common/uistrings.py	2016-01-23 08:15:37 +0000
+++ openlp/core/common/uistrings.py	2016-04-07 18:04:52 +0000
@@ -110,7 +110,7 @@
         self.NISp = translate('OpenLP.Ui', 'No Items Selected', 'Plural')
         self.OLP = translate('OpenLP.Ui', 'OpenLP')
         self.OLPV2 = "%s %s" % (self.OLP, "2")
-        self.OLPV2x = "%s %s" % (self.OLP, "2.4")
+        self.OLPV2x = "%s %s" % (self.OLP, "2.6")
         self.OpenLPStart = translate('OpenLP.Ui', 'OpenLP is already running. Do you wish to continue?')
         self.OpenService = translate('OpenLP.Ui', 'Open service.')
         self.PlaySlidesInLoop = translate('OpenLP.Ui', 'Play Slides in Loop')

=== modified file 'openlp/core/lib/serviceitem.py'
--- openlp/core/lib/serviceitem.py	2015-12-31 22:46:06 +0000
+++ openlp/core/lib/serviceitem.py	2016-04-07 18:04:52 +0000
@@ -610,7 +610,7 @@
                 str(datetime.timedelta(seconds=self.start_time))
         if self.media_length != 0:
             end = translate('OpenLP.ServiceItem', '<strong>Length</strong>: %s') % \
-                str(datetime.timedelta(seconds=self.media_length))
+                str(datetime.timedelta(seconds=self.media_length // 1000))
         if not start and not end:
             return ''
         elif start and not end:

=== modified file 'openlp/core/ui/__init__.py'
--- openlp/core/ui/__init__.py	2015-12-31 22:46:06 +0000
+++ openlp/core/ui/__init__.py	2016-04-07 18:04:52 +0000
@@ -68,7 +68,6 @@
     """
     Live = 0
     Preview = 1
-    Plugin = 2
 
 
 class SingleColumnTableWidget(QtWidgets.QTableWidget):

=== modified file 'openlp/core/ui/media/__init__.py'
--- openlp/core/ui/media/__init__.py	2015-12-31 22:46:06 +0000
+++ openlp/core/ui/media/__init__.py	2016-04-07 18:04:52 +0000
@@ -60,12 +60,14 @@
     """
     file_info = None
     volume = 100
-    is_flash = False
     is_background = False
+    loop_playback = False
     length = 0
     start_time = 0
     end_time = 0
     title_track = 0
+    playing = False
+    timer = 1000
     audio_track = 0
     subtitle_track = 0
     media_type = MediaType()
@@ -104,15 +106,15 @@
     Settings().setValue('media/players', players)
 
 
-def parse_optical_path(input):
+def parse_optical_path(input_string):
     """
     Split the optical path info.
 
-    :param input: The string to parse
+    :param input_string: The string to parse
     :return: The elements extracted from the string:  filename, title, audio_track, subtitle_track, start, end
     """
-    log.debug('parse_optical_path, about to parse: "%s"' % input)
-    clip_info = input.split(sep=':')
+    log.debug('parse_optical_path, about to parse: "%s"' % input_string)
+    clip_info = input_string.split(sep=':')
     title = int(clip_info[1])
     audio_track = int(clip_info[2])
     subtitle_track = int(clip_info[3])

=== modified file 'openlp/core/ui/media/mediacontroller.py'
--- openlp/core/ui/media/mediacontroller.py	2015-12-31 22:46:06 +0000
+++ openlp/core/ui/media/mediacontroller.py	2016-04-07 18:04:52 +0000
@@ -33,12 +33,15 @@
 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,\
     parse_optical_path
+from openlp.core.ui.media.vendor.mediainfoWrapper import MediaInfoWrapper
 from openlp.core.ui.media.mediaplayer import MediaPlayer
 from openlp.core.common import AppLocation
 from openlp.core.ui import DisplayControllerType
 
 log = logging.getLogger(__name__)
 
+TICK_TIME = 200
+
 
 class MediaSlider(QtWidgets.QSlider):
     """
@@ -51,10 +54,13 @@
         super(MediaSlider, self).__init__(direction)
         self.manager = manager
         self.controller = controller
+        self.no_matching_player = translate('MediaPlugin.MediaItem', 'File %s not supported using player %s')
 
     def mouseMoveEvent(self, event):
         """
         Override event to allow hover time to be displayed.
+
+        :param event: The triggering event
         """
         time_value = QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width())
         self.setToolTip('%s' % datetime.timedelta(seconds=int(time_value / 1000)))
@@ -63,12 +69,16 @@
     def mousePressEvent(self, event):
         """
         Mouse Press event no new functionality
+
+        :param event: The triggering event
         """
         QtWidgets.QSlider.mousePressEvent(self, event)
 
     def mouseReleaseEvent(self, event):
         """
         Set the slider position when the mouse is clicked and released on the slider.
+
+        :param event: The triggering event
         """
         self.setValue(QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width()))
         QtWidgets.QSlider.mouseReleaseEvent(self, event)
@@ -96,13 +106,17 @@
         self.display_controllers = {}
         self.current_media_players = {}
         # Timer for video state
-        self.timer = QtCore.QTimer()
-        self.timer.setInterval(200)
+        self.live_timer = QtCore.QTimer()
+        self.live_timer.setInterval(TICK_TIME)
+        self.preview_timer = QtCore.QTimer()
+        self.preview_timer.setInterval(TICK_TIME)
         # Signals
-        self.timer.timeout.connect(self.media_state)
+        self.live_timer.timeout.connect(self.media_state_live)
+        self.preview_timer.timeout.connect(self.media_state_preview)
         Registry().register_function('playbackPlay', self.media_play_msg)
         Registry().register_function('playbackPause', self.media_pause_msg)
         Registry().register_function('playbackStop', self.media_stop_msg)
+        Registry().register_function('playbackLoop', self.media_loop_msg)
         Registry().register_function('seek_slider', self.media_seek_msg)
         Registry().register_function('volume_slider', self.media_volume_msg)
         Registry().register_function('media_hide', self.media_hide)
@@ -172,8 +186,7 @@
                         log.warning('Failed to import %s on path %s', module_name, path)
         player_classes = MediaPlayer.__subclasses__()
         for player_class in player_classes:
-            player = player_class(self)
-            self.register_players(player)
+            self.register_players(player_class(self))
         if not self.media_players:
             return False
         saved_players, overridden_player = get_media_players()
@@ -188,31 +201,39 @@
         self._generate_extensions_lists()
         return True
 
-    def media_state(self):
-        """
-        Check if there is a running media Player and do updating stuff (e.g. update the UI)
-        """
-        if not list(self.current_media_players.keys()):
-            self.timer.stop()
-        else:
-            any_active = False
-            for source in list(self.current_media_players.keys()):
-                display = self._define_display(self.display_controllers[source])
-                self.current_media_players[source].resize(display)
-                self.current_media_players[source].update_ui(display)
-                if self.current_media_players[source].state == MediaState.Playing:
-                    any_active = True
-        # There are still any active players - no need to stop timer.
-            if any_active:
-                return
-        # no players are active anymore
-        for source in list(self.current_media_players.keys()):
-            if self.current_media_players[source].state != MediaState.Paused:
-                display = self._define_display(self.display_controllers[source])
-                display.controller.seek_slider.setSliderPosition(0)
-                display.controller.mediabar.actions['playbackPlay'].setVisible(True)
-                display.controller.mediabar.actions['playbackPause'].setVisible(False)
-        self.timer.stop()
+    def media_state_live(self):
+        """
+        Check if there is a running Live media Player and do updating stuff (e.g. update the UI)
+        """
+        display = self._define_display(self.display_controllers[DisplayControllerType.Live])
+        if DisplayControllerType.Live in self.current_media_players:
+            self.current_media_players[DisplayControllerType.Live].resize(display)
+            self.current_media_players[DisplayControllerType.Live].update_ui(display)
+            self.tick(self.display_controllers[DisplayControllerType.Live])
+            if self.current_media_players[DisplayControllerType.Live].get_live_state() is not MediaState.Playing:
+                self.live_timer.stop()
+        else:
+            self.live_timer.stop()
+            self.media_stop(self.display_controllers[DisplayControllerType.Live])
+            if self.display_controllers[DisplayControllerType.Live].media_info.loop_playback:
+                self.media_play(self.display_controllers[DisplayControllerType.Live], True)
+
+    def media_state_preview(self):
+        """
+        Check if there is a running Preview media Player and do updating stuff (e.g. update the UI)
+        """
+        display = self._define_display(self.display_controllers[DisplayControllerType.Preview])
+        if DisplayControllerType.Preview in self.current_media_players:
+            self.current_media_players[DisplayControllerType.Preview].resize(display)
+            self.current_media_players[DisplayControllerType.Preview].update_ui(display)
+            self.tick(self.display_controllers[DisplayControllerType.Preview])
+            if self.current_media_players[DisplayControllerType.Preview].get_preview_state() is not MediaState.Playing:
+                self.preview_timer.stop()
+        else:
+            self.preview_timer.stop()
+            self.media_stop(self.display_controllers[DisplayControllerType.Preview])
+            if self.display_controllers[DisplayControllerType.Preview].media_info.loop_playback:
+                self.media_play(self.display_controllers[DisplayControllerType.Preview], True)
 
     def get_media_display_css(self):
         """
@@ -274,6 +295,15 @@
                                                icon=':/slides/media_playback_stop.png',
                                                tooltip=translate('OpenLP.SlideController', 'Stop playing media.'),
                                                triggers=controller.send_to_plugins)
+        controller.mediabar.add_toolbar_action('playbackLoop', text='media_playback_loop',
+                                               icon=':/slides/media_playback_stop.png', checked=False,
+                                               tooltip=translate('OpenLP.SlideController', 'Loop playing media.'),
+                                               triggers=controller.send_to_plugins)
+        controller.position_label = QtWidgets.QLabel()
+        controller.position_label.setText(' 00:00 / 00:00')
+        controller.position_label.setToolTip(translate('OpenLP.SlideController', 'Video timer.'))
+        controller.position_label.setObjectName('position_label')
+        controller.mediabar.add_toolbar_widget(controller.position_label)
         # Build the seek_slider.
         controller.seek_slider = MediaSlider(QtCore.Qt.Horizontal, self, controller)
         controller.seek_slider.setMaximum(1000)
@@ -297,6 +327,8 @@
         controller.mediabar.add_toolbar_widget(controller.volume_slider)
         controller.controller_layout.addWidget(controller.mediabar)
         controller.mediabar.setVisible(False)
+        if not controller.is_live:
+            controller.volume_slider.setEnabled(False)
         # Signals
         controller.seek_slider.valueChanged.connect(controller.send_to_plugins)
         controller.volume_slider.valueChanged.connect(controller.send_to_plugins)
@@ -335,7 +367,8 @@
                 if self.current_media_players[controller.controller_type] != self.media_players['webkit']:
                     controller.display.set_transparency(False)
 
-    def resize(self, display, player):
+    @staticmethod
+    def resize(display, player):
         """
         After Mainwindow changes or Splitter moved all related media widgets have to be resized
 
@@ -353,7 +386,6 @@
         :param hidden: The player which is doing the playing
         :param video_behind_text: Is the video to be played behind text.
         """
-        log.debug('video')
         is_valid = False
         controller = self.display_controllers[source]
         # stop running videos
@@ -361,6 +393,8 @@
         controller.media_info = MediaInfo()
         controller.media_info.volume = controller.volume_slider.value()
         controller.media_info.is_background = video_behind_text
+        # background will always loop video.
+        controller.media_info.loop_playback = video_behind_text
         controller.media_info.file_info = QtCore.QFileInfo(service_item.get_frame_path())
         display = self._define_display(controller)
         if controller.is_live:
@@ -373,6 +407,7 @@
                                                     controller)
             else:
                 log.debug('video is not optical and live')
+                controller.media_info.length = service_item.media_length
                 is_valid = self._check_file_type(controller, display, service_item)
             display.override['theme'] = ''
             display.override['video'] = True
@@ -392,6 +427,7 @@
                                                     controller)
             else:
                 log.debug('video is not optical and preview')
+                controller.media_info.length = service_item.media_length
                 is_valid = self._check_file_type(controller, display, service_item)
         if not is_valid:
             # Media could not be loaded correctly
@@ -428,26 +464,22 @@
 
         :param service_item: The ServiceItem containing the details to be played.
         """
-        controller = self.display_controllers[DisplayControllerType.Plugin]
-        log.debug('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(service_item.get_frame_path())
-        display = controller.preview_display
-        if not self._check_file_type(controller, display, service_item):
+        media_info = MediaInfo()
+        media_info.volume = 0
+        media_info.file_info = QtCore.QFileInfo(service_item.get_frame_path())
+        # display = controller.preview_display
+        suffix = '*.%s' % media_info.file_info.suffix().lower()
+        used_players = get_media_players()[0]
+        player = self.media_players[used_players[0]]
+        if suffix not in player.video_extensions_list and suffix not in player.audio_extensions_list:
             # Media could not be loaded correctly
-            critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'),
-                                       translate('MediaPlugin.MediaItem', 'Unsupported File'))
-            return False
-        if not self.media_play(controller):
-            critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'),
-                                       translate('MediaPlugin.MediaItem', 'Unsupported File'))
-            return False
-        service_item.set_media_length(controller.media_info.length)
-        self.media_stop(controller)
-        log.debug('use %s controller' % self.current_media_players[controller.controller_type])
+            critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported Media File'),
+                                       translate('MediaPlugin.MediaItem', 'File %s not supported using player %s') %
+                                       (service_item.get_frame_path(), used_players[0]))
+            return False
+        media_data = MediaInfoWrapper.parse(service_item.get_frame_path())
+        # duration returns in milli seconds
+        service_item.set_media_length(media_data.tracks[0].duration)
         return True
 
     def media_setup_optical(self, filename, title, audio_track, subtitle_track, start, end, display, controller):
@@ -458,13 +490,12 @@
         :param title: The main/title track to play.
         :param audio_track: The audio track to play.
         :param subtitle_track: The subtitle track to play.
-        :param start: Start position in miliseconds.
-        :param end: End position in miliseconds.
+        :param start: Start position in milliseconds.
+        :param end: End position in milliseconds.
         :param display: The display to play the media.
-        :param controller: The media contraoller.
-        :return: True if setup succeded else False.
+        :param controller: The media controller.
+        :return: True if setup succeeded else False.
         """
-        log.debug('media_setup_optical')
         if controller is None:
             controller = self.display_controllers[DisplayControllerType.Plugin]
         # stop running videos
@@ -476,9 +507,9 @@
             controller.media_info.media_type = MediaType.CD
         else:
             controller.media_info.media_type = MediaType.DVD
-        controller.media_info.start_time = start / 1000
-        controller.media_info.end_time = end / 1000
-        controller.media_info.length = (end - start) / 1000
+        controller.media_info.start_time = start // 1000
+        controller.media_info.end_time = end // 1000
+        controller.media_info.length = (end - start) // 1000
         controller.media_info.title_track = title
         controller.media_info.audio_track = audio_track
         controller.media_info.subtitle_track = subtitle_track
@@ -506,13 +537,13 @@
             controller.media_info.media_type = MediaType.DVD
         return True
 
-    def _check_file_type(self, controller, display, service_item):
+    @staticmethod
+    def _get_used_players(service_item):
         """
-        Select the correct media Player type from the prioritized Player list
+        Find the player for a given service item
 
-        :param controller: First element is the controller which should be used
-        :param display: Which display to use
-        :param service_item: The ServiceItem containing the details to be played.
+        :param service_item: where the information is about the media and required player
+        :return: player description
         """
         used_players = get_media_players()[0]
         # If no player, we can't play
@@ -525,6 +556,17 @@
                 used_players = default_player
             else:
                 used_players = [service_item.processor.lower()]
+        return used_players
+
+    def _check_file_type(self, controller, display, service_item):
+        """
+        Select the correct media Player type from the prioritized Player list
+
+        :param controller: First element is the controller which should be used
+        :param display: Which display to use
+        :param service_item: The ServiceItem containing the details to be played.
+        """
+        used_players = self._get_used_players(service_item)
         if controller.media_info.file_info.isFile():
             suffix = '*.%s' % controller.media_info.file_info.suffix().lower()
             for title in used_players:
@@ -573,17 +615,15 @@
         :param msg: First element is the controller which should be used
         :param status:
         """
-        log.debug('media_play_msg')
         self.media_play(msg[0], status)
 
-    def media_play(self, controller, status=True):
+    def media_play(self, controller, first_time=True):
         """
         Responds to the request to play a loaded video
 
         :param controller: The controller to be played
-        :param status:
+        :param first_time:
         """
-        log.debug('media_play')
         controller.seek_slider.blockSignals(True)
         controller.volume_slider.blockSignals(True)
         display = self._define_display(controller)
@@ -595,35 +635,60 @@
             self.media_volume(controller, 0)
         else:
             self.media_volume(controller, controller.media_info.volume)
-        if status:
+        if first_time:
             if not controller.media_info.is_background:
                 display.frame.evaluateJavaScript('show_blank("desktop");')
             self.current_media_players[controller.controller_type].set_visible(display, True)
-            # Flash needs to be played and will not AutoPlay
-            if controller.media_info.is_flash:
-                controller.mediabar.actions['playbackPlay'].setVisible(True)
-                controller.mediabar.actions['playbackPause'].setVisible(False)
-            else:
-                controller.mediabar.actions['playbackPlay'].setVisible(False)
-                controller.mediabar.actions['playbackPause'].setVisible(True)
+            controller.mediabar.actions['playbackPlay'].setVisible(False)
+            controller.mediabar.actions['playbackPause'].setVisible(True)
             controller.mediabar.actions['playbackStop'].setDisabled(False)
-            if controller.is_live:
-                if controller.hide_menu.defaultAction().isChecked() and not controller.media_info.is_background:
-                    controller.hide_menu.defaultAction().trigger()
-        # Start Timer for ui updates
-        if not self.timer.isActive():
-            self.timer.start()
+        if controller.is_live:
+            if controller.hide_menu.defaultAction().isChecked() and not controller.media_info.is_background:
+                controller.hide_menu.defaultAction().trigger()
+            # Start Timer for ui updates
+            if not self.live_timer.isActive():
+                self.live_timer.start()
+        else:
+            # Start Timer for ui updates
+            if not self.preview_timer.isActive():
+                self.preview_timer.start()
         controller.seek_slider.blockSignals(False)
         controller.volume_slider.blockSignals(False)
+        controller.media_info.playing = True
+        display = self._define_display(controller)
+        display.setVisible(True)
         return True
 
+    def tick(self, controller):
+        """
+        Add a tick while the media is playing but only count if not paused
+
+        :param controller:  The Controller to be processed
+        """
+        start_again = False
+        if controller.media_info.playing and controller.media_info.length > 0:
+            if controller.media_info.timer > controller.media_info.length:
+                self.media_stop(controller, True)
+                if controller.media_info.loop_playback:
+                    start_again = True
+            controller.media_info.timer += TICK_TIME
+            seconds = controller.media_info.timer // 1000
+            minutes = seconds // 60
+            seconds %= 60
+            total_seconds = controller.media_info.length // 1000
+            total_minutes = total_seconds // 60
+            total_seconds %= 60
+            controller.position_label.setText(' %02d:%02d / %02d:%02d' %
+                                              (minutes, seconds, total_minutes, total_seconds))
+        if start_again:
+            self.media_play(controller, True)
+
     def media_pause_msg(self, msg):
         """
         Responds to the request to pause a loaded video
 
         :param msg: First element is the controller which should be used
         """
-        log.debug('media_pause_msg')
         self.media_pause(msg[0])
 
     def media_pause(self, controller):
@@ -632,12 +697,31 @@
 
         :param controller: The Controller to be paused
         """
-        log.debug('media_pause')
         display = self._define_display(controller)
-        self.current_media_players[controller.controller_type].pause(display)
-        controller.mediabar.actions['playbackPlay'].setVisible(True)
-        controller.mediabar.actions['playbackStop'].setDisabled(False)
-        controller.mediabar.actions['playbackPause'].setVisible(False)
+        if controller.controller_type in self.current_media_players:
+            self.current_media_players[controller.controller_type].pause(display)
+            controller.mediabar.actions['playbackPlay'].setVisible(True)
+            controller.mediabar.actions['playbackStop'].setDisabled(False)
+            controller.mediabar.actions['playbackPause'].setVisible(False)
+            controller.media_info.playing = False
+
+    def media_loop_msg(self, msg):
+        """
+        Responds to the request to loop a loaded video
+
+        :param msg: First element is the controller which should be used
+        """
+        self.media_loop(msg[0])
+
+    @staticmethod
+    def media_loop(controller):
+        """
+        Responds to the request to loop a loaded video
+
+        :param controller: The controller that needs to be stopped
+        """
+        controller.media_info.loop_playback = not controller.media_info.loop_playback
+        controller.mediabar.actions['playbackLoop'].setChecked(controller.media_info.loop_playback)
 
     def media_stop_msg(self, msg):
         """
@@ -645,25 +729,28 @@
 
         :param msg: First element is the controller which should be used
         """
-        log.debug('media_stop_msg')
         self.media_stop(msg[0])
 
-    def media_stop(self, controller):
+    def media_stop(self, controller, looping_background=False):
         """
         Responds to the request to stop a loaded video
 
         :param controller: The controller that needs to be stopped
+        :param looping_background: The background is looping so do not blank.
         """
-        log.debug('media_stop')
         display = self._define_display(controller)
         if controller.controller_type in self.current_media_players:
-            display.frame.evaluateJavaScript('show_blank("black");')
+            if not looping_background:
+                display.frame.evaluateJavaScript('show_blank("black");')
             self.current_media_players[controller.controller_type].stop(display)
             self.current_media_players[controller.controller_type].set_visible(display, False)
             controller.seek_slider.setSliderPosition(0)
             controller.mediabar.actions['playbackPlay'].setVisible(True)
             controller.mediabar.actions['playbackStop'].setDisabled(True)
             controller.mediabar.actions['playbackPause'].setVisible(False)
+            controller.media_info.playing = False
+            controller.media_info.timer = 1000
+            controller.media_timer = 0
 
     def media_volume_msg(self, msg):
         """
@@ -694,7 +781,6 @@
         :param msg: First element is the controller which should be used
             Second element is a list with the seek value as first element
         """
-        log.debug('media_seek')
         controller = msg[0]
         seek_value = msg[1][0]
         self.media_seek(controller, seek_value)
@@ -706,15 +792,15 @@
         :param controller: The controller to use.
         :param seek_value: The value to set.
         """
-        log.debug('media_seek')
         display = self._define_display(controller)
         self.current_media_players[controller.controller_type].seek(display, seek_value)
+        controller.media_info.timer = seek_value
 
     def media_reset(self, controller):
         """
         Responds to the request to reset a loaded video
+        :param controller: The controller to use.
         """
-        log.debug('media_reset')
         self.set_controls_visible(controller, False)
         display = self._define_display(controller)
         if controller.controller_type in self.current_media_players:
@@ -735,7 +821,7 @@
             return
         display = self._define_display(self.live_controller)
         if self.live_controller.controller_type in self.current_media_players and \
-                self.current_media_players[self.live_controller.controller_type].state == MediaState.Playing:
+                self.current_media_players[self.live_controller.controller_type].get_live_state() == MediaState.Playing:
             self.current_media_players[self.live_controller.controller_type].pause(display)
             self.current_media_players[self.live_controller.controller_type].set_visible(display, False)
 
@@ -753,7 +839,7 @@
         Registry().execute('live_display_hide', hide_mode)
         display = self._define_display(self.live_controller)
         if self.live_controller.controller_type in self.current_media_players and \
-                self.current_media_players[self.live_controller.controller_type].state == MediaState.Playing:
+                self.current_media_players[self.live_controller.controller_type].get_live_state() == MediaState.Playing:
             self.current_media_players[self.live_controller.controller_type].pause(display)
             self.current_media_players[self.live_controller.controller_type].set_visible(display, False)
 
@@ -770,22 +856,25 @@
             return
         display = self._define_display(self.live_controller)
         if self.live_controller.controller_type in self.current_media_players and \
-                self.current_media_players[self.live_controller.controller_type].state != MediaState.Playing:
+                self.current_media_players[self.live_controller.controller_type].get_live_state() != \
+                MediaState.Playing:
             if self.current_media_players[self.live_controller.controller_type].play(display):
                 self.current_media_players[self.live_controller.controller_type].set_visible(display, True)
                 # Start Timer for ui updates
-                if not self.timer.isActive():
-                    self.timer.start()
+                if not self.live_timer.isActive():
+                    self.live_timer.start()
 
     def finalise(self):
         """
         Reset all the media controllers when OpenLP shuts down
         """
-        self.timer.stop()
+        self.live_timer.stop()
+        self.preview_timer.stop()
         for controller in self.display_controllers:
             self.media_reset(self.display_controllers[controller])
 
-    def _define_display(self, controller):
+    @staticmethod
+    def _define_display(controller):
         """
         Extract the correct display for a given controller
 

=== modified file 'openlp/core/ui/media/mediaplayer.py'
--- openlp/core/ui/media/mediaplayer.py	2015-12-31 22:46:06 +0000
+++ openlp/core/ui/media/mediaplayer.py	2016-04-07 18:04:52 +0000
@@ -41,7 +41,7 @@
         self.is_active = False
         self.can_background = False
         self.can_folder = False
-        self.state = MediaState.Off
+        self.state = {0: MediaState.Off, 1: MediaState.Off}
         self.has_own_widget = False
         self.audio_extensions_list = []
         self.video_extensions_list = []
@@ -55,12 +55,16 @@
     def setup(self, display):
         """
         Create the related widgets for the current display
+
+        :param display: The display to be updated.
         """
         pass
 
     def load(self, display):
         """
         Load a new media file and check if it is valid
+
+        :param display: The display to be updated.
         """
         return True
 
@@ -68,54 +72,75 @@
         """
         If the main display size or position is changed, the media widgets
         should also resized
+
+        :param display: The display to be updated.
         """
         pass
 
     def play(self, display):
         """
         Starts playing of current Media File
+
+        :param display: The display to be updated.
         """
         pass
 
     def pause(self, display):
         """
         Pause of current Media File
+
+        :param display: The display to be updated.
         """
         pass
 
     def stop(self, display):
         """
         Stop playing of current Media File
+
+        :param display: The display to be updated.
         """
         pass
 
-    def volume(self, display, vol):
+    def volume(self, display, volume):
         """
         Change volume of current Media File
+
+        :param display: The display to be updated.
+        :param volume: The volume to set.
         """
         pass
 
     def seek(self, display, seek_value):
         """
         Change playing position of current Media File
+
+        :param display: The display to be updated.
+        :param seek_value: The where to seek to.
         """
         pass
 
     def reset(self, display):
         """
         Remove the current loaded video
+
+        :param display: The display to be updated.
         """
         pass
 
     def set_visible(self, display, status):
         """
         Show/Hide the media widgets
+
+        :param display: The display to be updated.
+        :param status: The status to be set.
         """
         pass
 
     def update_ui(self, display):
         """
         Do some ui related stuff (e.g. update the seek slider)
+
+        :param display: The display to be updated.
         """
         pass
 
@@ -142,3 +167,21 @@
         Returns Information about the player
         """
         return ''
+
+    def get_live_state(self):
+        return self.state[0]
+
+    def set_live_state(self, state):
+        self.state[0] = state
+
+    def get_preview_state(self):
+        return self.state[1]
+
+    def set_preview_state(self, state):
+        self.state[1] = state
+
+    def set_state(self, state, display):
+        if display.controller.is_live:
+            self.set_live_state(state)
+        else:
+            self.set_preview_state(state)

=== modified file 'openlp/core/ui/media/playertab.py'
--- openlp/core/ui/media/playertab.py	2015-12-31 22:46:06 +0000
+++ openlp/core/ui/media/playertab.py	2016-04-07 18:04:52 +0000
@@ -133,12 +133,16 @@
     def on_background_color_changed(self, color):
         """
         Set the background color
+
+        :param color: The color to be set.
         """
         self.background_color = color
 
     def on_player_check_box_changed(self, check_state):
         """
         Add or remove players depending on their status
+
+        :param check_state: The requested status.
         """
         player = self.sender().player_name
         if check_state == QtCore.Qt.Checked:

=== modified file 'openlp/core/ui/media/systemplayer.py'
--- openlp/core/ui/media/systemplayer.py	2015-12-10 19:03:57 +0000
+++ openlp/core/ui/media/systemplayer.py	2016-04-07 18:04:52 +0000
@@ -4,14 +4,7 @@
 ###############################################################################
 # OpenLP - Open Source Lyrics Projection                                      #
 # --------------------------------------------------------------------------- #
-# Copyright (c) 2008-2014 Raoul Snyman                                        #
-# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan      #
-# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub,      #
-# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer.   #
-# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru,          #
-# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith,             #
-# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock,              #
-# Frode Woldsund, Martin Zibricky, Patrick Zimmermann                         #
+# Copyright (c) 2008-2016 OpenLP Developers                                   #
 # --------------------------------------------------------------------------- #
 # This program is free software; you can redistribute it and/or modify it     #
 # under the terms of the GNU General Public License as published by the Free  #
@@ -124,7 +117,8 @@
     def load(self, display):
         """
         Load a video into the display
-        :param display:
+
+        :param display: The display where the media is
         """
         log.debug('load vid in System Controller')
         controller = display.controller
@@ -141,93 +135,122 @@
     def resize(self, display):
         """
         Resize the display
-        :param display:
+
+        :param display: The display where the media is
         """
         display.video_widget.resize(display.size())
 
     def play(self, display):
         """
         Play the current media item
-        :param display:
+
+        :param display: The display where the media is
         """
         log.info('Play the current item')
         controller = display.controller
         start_time = 0
-        if display.media_player.state() != QtMultimedia.QMediaPlayer.PausedState and \
-                controller.media_info.start_time > 0:
-            start_time = controller.media_info.start_time
+        if display.controller.is_live:
+            if self.get_live_state() != QtMultimedia.QMediaPlayer.PausedState and controller.media_info.start_time > 0:
+                start_time = controller.media_info.start_time
+        else:
+            if self.get_preview_state() != QtMultimedia.QMediaPlayer.PausedState and \
+                            controller.media_info.start_time > 0:
+                start_time = controller.media_info.start_time
         display.media_player.play()
         if start_time > 0:
             self.seek(display, controller.media_info.start_time * 1000)
         self.volume(display, controller.media_info.volume)
         display.media_player.durationChanged.connect(functools.partial(self.set_duration, controller))
-        self.state = MediaState.Playing
+        self.set_state(MediaState.Playing, display)
         display.video_widget.raise_()
         return True
 
     def pause(self, display):
         """
         Pause the current media item
+
+        :param display: The display where the media is
         """
         display.media_player.pause()
-        if display.media_player.state() == QtMultimedia.QMediaPlayer.PausedState:
-            self.state = MediaState.Paused
+        if display.controller.is_live:
+            if self.get_live_state() == QtMultimedia.QMediaPlayer.PausedState:
+                self.set_state(MediaState.Paused, display)
+        else:
+            if self.get_preview_state() == QtMultimedia.QMediaPlayer.PausedState:
+                self.set_state(MediaState.Paused, display)
 
     def stop(self, display):
         """
         Stop the current media item
+
+        :param display: The display where the media is
         """
-        display.media_player.blockSignals(True)
-        display.media_player.durationChanged.disconnect()
-        display.media_player.blockSignals(False)
         display.media_player.stop()
         self.set_visible(display, False)
-        self.state = MediaState.Stopped
+        self.set_state(MediaState.Stopped, display)
 
-    def volume(self, display, vol):
+    def volume(self, display, volume):
         """
         Set the volume
+
+        :param display: The display where the media is
+        :param volume: The volume to be set
         """
         if display.has_audio:
-            display.media_player.setVolume(vol)
+            display.media_player.setVolume(volume)
 
     def seek(self, display, seek_value):
         """
         Go to a particular point in the current media item
+
+        :param display: The display where the media is
+        :param seek_value: The where to seek to
         """
         display.media_player.setPosition(seek_value)
 
     def reset(self, display):
         """
         Reset the media player
+
+        :param display: The display where the media is
         """
         display.media_player.stop()
         display.media_player.setMedia(QtMultimedia.QMediaContent())
         self.set_visible(display, False)
         display.video_widget.setVisible(False)
-        self.state = MediaState.Off
+        self.set_state(MediaState.Off, display)
 
     def set_visible(self, display, status):
         """
         Set the visibility of the widget
+
+        :param display: The display where the media is
+        :param status: The visibility status to be set
         """
         if self.has_own_widget:
             display.video_widget.setVisible(status)
 
     @staticmethod
     def set_duration(controller, duration):
-        controller.media_info.length = int(duration / 1000)
-        controller.seek_slider.setMaximum(controller.media_info.length * 1000)
+        """
+
+        :param controller: the controller displaying the media
+        :param duration: how long is the media
+        :return:
+        """
+        controller.seek_slider.setMaximum(controller.media_info.length)
 
     def update_ui(self, display):
         """
         Update the UI
+
+        :param display: The display where the media is
         """
         if display.media_player.state() == QtMultimedia.QMediaPlayer.PausedState and self.state != MediaState.Paused:
             self.stop(display)
         controller = display.controller
         if controller.media_info.end_time > 0:
-            if display.media_player.position() > controller.media_info.end_time * 1000:
+            if display.media_player.position() > controller.media_info.end_time:
                 self.stop(display)
                 self.set_visible(display, False)
         if not controller.seek_slider.isSliderDown():

=== added file 'openlp/core/ui/media/vendor/mediainfoWrapper.py'
--- openlp/core/ui/media/vendor/mediainfoWrapper.py	1970-01-01 00:00:00 +0000
+++ openlp/core/ui/media/vendor/mediainfoWrapper.py	2016-04-07 18:04:52 +0000
@@ -0,0 +1,140 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2016 OpenLP Developers                                   #
+# --------------------------------------------------------------------------- #
+# This program is free software; you can redistribute it and/or modify it     #
+# under the terms of the GNU General Public License as published by the Free  #
+# Software Foundation; version 2 of the License.                              #
+#                                                                             #
+# This program is distributed in the hope that it will be useful, but WITHOUT #
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       #
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    #
+# more details.                                                               #
+#                                                                             #
+# You should have received a copy of the GNU General Public License along     #
+# with this program; if not, write to the Free Software Foundation, Inc., 59  #
+# Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
+###############################################################################
+"""
+The :mod:`~openlp.core.ui.media.mediainfo` module contains code to run mediainfo on a media file and obtain
+information related to the rwquested media.
+"""
+import json
+import os
+from subprocess import Popen
+from tempfile import mkstemp
+
+import six
+from bs4 import BeautifulSoup, NavigableString
+
+ENV_DICT = os.environ
+
+
+class Track(object):
+
+    def __getattribute__(self, name):
+        try:
+            return object.__getattribute__(self, name)
+        except:
+            pass
+        return None
+
+    def __init__(self, xml_dom_fragment):
+        self.xml_dom_fragment = xml_dom_fragment
+        self.track_type = xml_dom_fragment.attrs['type']
+        for el in self.xml_dom_fragment.children:
+            if not isinstance(el, NavigableString):
+                node_name = el.name.lower().strip().strip('_')
+                if node_name == 'id':
+                    node_name = 'track_id'
+                node_value = el.string
+                other_node_name = "other_%s" % node_name
+                if getattr(self, node_name) is None:
+                    setattr(self, node_name, node_value)
+                else:
+                    if getattr(self, other_node_name) is None:
+                        setattr(self, other_node_name, [node_value, ])
+                    else:
+                        getattr(self, other_node_name).append(node_value)
+
+        for o in [d for d in self.__dict__.keys() if d.startswith('other_')]:
+            try:
+                primary = o.replace('other_', '')
+                setattr(self, primary, int(getattr(self, primary)))
+            except:
+                for v in getattr(self, o):
+                    try:
+                        current = getattr(self, primary)
+                        setattr(self, primary, int(v))
+                        getattr(self, o).append(current)
+                        break
+                    except:
+                        pass
+
+    def __repr__(self):
+        return "<Track track_id='{0}', track_type='{1}'>".format(self.track_id, self.track_type)
+
+    def to_data(self):
+        data = {}
+        for k, v in six.iteritems(self.__dict__):
+            if k != 'xml_dom_fragment':
+                data[k] = v
+        return data
+
+
+class MediaInfoWrapper(object):
+
+    def __init__(self, xml):
+        self.xml_dom = xml
+        xml_types = (str,)     # no unicode type in python3
+        if isinstance(xml, xml_types):
+            self.xml_dom = MediaInfoWrapper.parse_xml_data_into_dom(xml)
+
+    @staticmethod
+    def parse_xml_data_into_dom(xml_data):
+        return BeautifulSoup(xml_data, "xml")
+
+    @staticmethod
+    def parse(filename, environment=ENV_DICT):
+        command = ["mediainfo", "-f", "--Output=XML", filename]
+        fileno_out, fname_out = mkstemp(suffix=".xml", prefix="media-")
+        fileno_err, fname_err = mkstemp(suffix=".err", prefix="media-")
+        fp_out = os.fdopen(fileno_out, 'r+b')
+        fp_err = os.fdopen(fileno_err, 'r+b')
+        p = Popen(command, stdout=fp_out, stderr=fp_err, env=environment)
+        p.wait()
+        fp_out.seek(0)
+
+        xml_dom = MediaInfoWrapper.parse_xml_data_into_dom(fp_out.read())
+        fp_out.close()
+        fp_err.close()
+        os.unlink(fname_out)
+        os.unlink(fname_err)
+        return MediaInfoWrapper(xml_dom)
+
+    def _populate_tracks(self):
+        if self.xml_dom is None:
+            return
+        for xml_track in self.xml_dom.Mediainfo.File.find_all("track"):
+            self._tracks.append(Track(xml_track))
+
+    @property
+    def tracks(self):
+        if not hasattr(self, "_tracks"):
+            self._tracks = []
+        if len(self._tracks) == 0:
+            self._populate_tracks()
+        return self._tracks
+
+    def to_data(self):
+        data = {'tracks': []}
+        for track in self.tracks:
+            data['tracks'].append(track.to_data())
+        return data
+
+    def to_json(self):
+        return json.dumps(self.to_data())
\ No newline at end of file

=== modified file 'openlp/core/ui/media/vlcplayer.py'
--- openlp/core/ui/media/vlcplayer.py	2015-12-31 22:46:06 +0000
+++ openlp/core/ui/media/vlcplayer.py	2016-04-07 18:04:52 +0000
@@ -144,6 +144,9 @@
     def setup(self, display):
         """
         Set up the media player
+
+        :param display: The display where the media is
+        :return:
         """
         vlc = get_vlc()
         display.vlc_widget = QtWidgets.QFrame(display)
@@ -186,6 +189,9 @@
     def load(self, display):
         """
         Load a video into VLC
+
+        :param display: The display where the media is
+        :return:
         """
         vlc = get_vlc()
         log.debug('load vid in Vlc Controller')
@@ -214,18 +220,16 @@
         # parse the metadata of the file
         display.vlc_media.parse()
         self.volume(display, volume)
-        # We need to set media_info.length during load because we want
-        # to avoid start and stop the video twice. Once for real playback
-        # and once to just get media length.
-        #
-        # Media plugin depends on knowing media length before playback.
-        controller.media_info.length = int(display.vlc_media_player.get_media().get_duration() / 1000)
         return True
 
     def media_state_wait(self, display, media_state):
         """
         Wait for the video to change its state
         Wait no longer than 60 seconds. (loading an iso file needs a long time)
+
+        :param media_state: The state of the playing media
+        :param display: The display where the media is
+        :return:
         """
         vlc = get_vlc()
         start = datetime.now()
@@ -240,25 +244,40 @@
     def resize(self, display):
         """
         Resize the player
+
+        :param display: The display where the media is
+        :return:
         """
         display.vlc_widget.resize(display.size())
 
     def play(self, display):
         """
         Play the current item
+
+        :param display: The display where the media is
+        :return:
         """
         vlc = get_vlc()
         controller = display.controller
         start_time = 0
         log.debug('vlc play')
-        if self.state != MediaState.Paused and controller.media_info.start_time > 0:
-            start_time = controller.media_info.start_time
+        if display.controller.is_live:
+            if self.get_live_state() != MediaState.Paused and controller.media_info.start_time > 0:
+                start_time = controller.media_info.start_time
+        else:
+            if self.get_preview_state() != MediaState.Paused and controller.media_info.start_time > 0:
+                start_time = controller.media_info.start_time
         threading.Thread(target=display.vlc_media_player.play).start()
         if not self.media_state_wait(display, vlc.State.Playing):
             return False
-        if self.state != MediaState.Paused and controller.media_info.start_time > 0:
-            log.debug('vlc play, starttime set')
-            start_time = controller.media_info.start_time
+        if display.controller.is_live:
+            if self.get_live_state() != MediaState.Paused and controller.media_info.start_time > 0:
+                log.debug('vlc play, start time set')
+                start_time = controller.media_info.start_time
+        else:
+            if self.get_preview_state() != MediaState.Paused and controller.media_info.start_time > 0:
+                log.debug('vlc play, start time set')
+                start_time = controller.media_info.start_time
         log.debug('mediatype: ' + str(controller.media_info.media_type))
         # Set tracks for the optical device
         if controller.media_info.media_type == MediaType.DVD:
@@ -279,37 +298,45 @@
                 log.debug('vlc play, starttime set: ' + str(controller.media_info.start_time))
                 start_time = controller.media_info.start_time
             controller.media_info.length = controller.media_info.end_time - controller.media_info.start_time
-        else:
-            controller.media_info.length = int(display.vlc_media_player.get_media().get_duration() / 1000)
         self.volume(display, controller.media_info.volume)
         if start_time > 0 and display.vlc_media_player.is_seekable():
-            display.vlc_media_player.set_time(int(start_time * 1000))
-        controller.seek_slider.setMaximum(controller.media_info.length * 1000)
-        self.state = MediaState.Playing
+            display.vlc_media_player.set_time(int(start_time))
+        controller.seek_slider.setMaximum(controller.media_info.length)
+        self.set_state(MediaState.Playing, display)
         display.vlc_widget.raise_()
         return True
 
     def pause(self, display):
         """
         Pause the current item
+
+        :param display: The display where the media is
+        :return:
         """
         vlc = get_vlc()
         if display.vlc_media.get_state() != vlc.State.Playing:
             return
         display.vlc_media_player.pause()
         if self.media_state_wait(display, vlc.State.Paused):
-            self.state = MediaState.Paused
+            self.set_state(MediaState.Paused, display)
 
     def stop(self, display):
         """
         Stop the current item
+
+        :param display: The display where the media is
+        :return:
         """
         threading.Thread(target=display.vlc_media_player.stop).start()
-        self.state = MediaState.Stopped
+        self.set_state(MediaState.Stopped, display)
 
     def volume(self, display, vol):
         """
         Set the volume
+
+        :param vol: The volume to be sets
+        :param display: The display where the media is
+        :return:
         """
         if display.has_audio:
             display.vlc_media_player.audio_set_volume(vol)
@@ -317,6 +344,9 @@
     def seek(self, display, seek_value):
         """
         Go to a particular position
+
+        :param seek_value: The position of where a seek goes to
+        :param display: The display where the media is
         """
         if display.controller.media_info.media_type == MediaType.CD \
                 or display.controller.media_info.media_type == MediaType.DVD:
@@ -327,14 +357,19 @@
     def reset(self, display):
         """
         Reset the player
+
+        :param display: The display where the media is
         """
         display.vlc_media_player.stop()
         display.vlc_widget.setVisible(False)
-        self.state = MediaState.Off
+        self.set_state(MediaState.Off, display)
 
     def set_visible(self, display, status):
         """
         Set the visibility
+
+        :param display: The display where the media is
+        :param status: The visibility status
         """
         if self.has_own_widget:
             display.vlc_widget.setVisible(status)
@@ -342,6 +377,8 @@
     def update_ui(self, display):
         """
         Update the UI
+
+        :param display: The display where the media is
         """
         vlc = get_vlc()
         # Stop video if playback is finished.

=== modified file 'openlp/core/ui/media/webkitplayer.py'
--- openlp/core/ui/media/webkitplayer.py	2016-01-09 16:26:14 +0000
+++ openlp/core/ui/media/webkitplayer.py	2016-04-07 18:04:52 +0000
@@ -99,74 +99,6 @@
 <video id="video" class="size" style="visibility:hidden" autobuffer preload></video>
 """
 
-FLASH_CSS = """
-#flash {
-    z-index:5;
-}
-"""
-
-FLASH_JS = """
-    function getFlashMovieObject(movieName)
-    {
-        if (window.document[movieName]){
-            return window.document[movieName];
-        }
-        if (document.embeds && document.embeds[movieName]){
-            return document.embeds[movieName];
-        }
-    }
-
-    function show_flash(state, path, volume, variable_value){
-        var text = document.getElementById('flash');
-        var flashMovie = getFlashMovieObject("OpenLPFlashMovie");
-        var src = "src = 'file:///" + path + "'";
-        var view_parm = " wmode='opaque'" + " width='100%%'" + " height='100%%'";
-        var swf_parm = " name='OpenLPFlashMovie'" + " autostart='true' loop='false' play='true'" +
-            " hidden='false' swliveconnect='true' allowscriptaccess='always'" + " volume='" + volume + "'";
-
-        switch(state){
-            case 'load':
-                text.innerHTML = "<embed " + src + view_parm + swf_parm + "/>";
-                flashMovie = getFlashMovieObject("OpenLPFlashMovie");
-                flashMovie.Play();
-                break;
-            case 'play':
-                flashMovie.Play();
-                break;
-            case 'pause':
-                flashMovie.StopPlay();
-                break;
-            case 'stop':
-                flashMovie.StopPlay();
-                tempHtml = text.innerHTML;
-                text.innerHTML = '';
-                text.innerHTML = tempHtml;
-                break;
-            case 'close':
-                flashMovie.StopPlay();
-                text.innerHTML = '';
-                break;
-            case 'length':
-                return flashMovie.TotalFrames();
-            case 'current_time':
-                return flashMovie.CurrentFrame();
-            case 'seek':
-//                flashMovie.GotoFrame(variable_value);
-                break;
-            case 'isEnded':
-                //TODO check flash end
-                return false;
-            case 'setVisible':
-                text.style.visibility = variable_value;
-                break;
-        }
-    }
-"""
-
-FLASH_HTML = """
-<div id="flash" class="size" style="visibility:hidden"></div>
-"""
-
 VIDEO_EXT = ['*.3gp', '*.3gpp', '*.3g2', '*.3gpp2', '*.aac', '*.flv', '*.f4a', '*.f4b', '*.f4p', '*.f4v', '*.mov',
              '*.m4a', '*.m4b', '*.m4p', '*.m4v', '*.mkv', '*.mp4', '*.ogv', '*.webm', '*.mpg', '*.wmv', '*.mpeg',
              '*.avi', '*.swf']
@@ -198,23 +130,25 @@
         """
         background = QtGui.QColor(Settings().value('players/background color')).name()
         css = VIDEO_CSS % {'bgcolor': background}
-        return css + FLASH_CSS
+        return css
 
     def get_media_display_javascript(self):
         """
         Add javascript functions to htmlbuilder
         """
-        return VIDEO_JS + FLASH_JS
+        return VIDEO_JS
 
     def get_media_display_html(self):
         """
         Add html code to htmlbuilder
         """
-        return VIDEO_HTML + FLASH_HTML
+        return VIDEO_HTML
 
     def setup(self, display):
         """
         Set up the player
+
+        :param display: The display to be updated.
         """
         display.web_view.resize(display.size())
         display.web_view.raise_()
@@ -235,6 +169,8 @@
     def load(self, display):
         """
         Load a video
+
+        :param display: The display to be updated.
         """
         log.debug('load vid in Webkit Controller')
         controller = display.controller
@@ -249,132 +185,120 @@
         else:
             loop = 'false'
         display.web_view.setVisible(True)
-        if controller.media_info.file_info.suffix() == 'swf':
-            controller.media_info.is_flash = True
-            js = 'show_flash("load","%s");' % (path.replace('\\', '\\\\'))
-        else:
-            js = 'show_video("load", "%s", %s, %s);' % (path.replace('\\', '\\\\'), str(vol), loop)
+        js = 'show_video("load", "%s", %s, %s);' % (path.replace('\\', '\\\\'), str(vol), loop)
         display.frame.evaluateJavaScript(js)
         return True
 
     def resize(self, display):
         """
         Resize the player
+
+        :param display: The display to be updated.
         """
         display.web_view.resize(display.size())
 
     def play(self, display):
         """
         Play a video
+
+        :param display: The display to be updated.
         """
         controller = display.controller
         display.web_loaded = True
-        length = 0
         start_time = 0
-        if self.state != MediaState.Paused and controller.media_info.start_time > 0:
-            start_time = controller.media_info.start_time
+        if display.controller.is_live:
+            if self.get_live_state() != MediaState.Paused and controller.media_info.start_time > 0:
+                start_time = controller.media_info.start_time
+        else:
+            if self.get_preview_state() != MediaState.Paused and controller.media_info.start_time > 0:
+                start_time = controller.media_info.start_time
         self.set_visible(display, True)
-        if controller.media_info.is_flash:
-            display.frame.evaluateJavaScript('show_flash("play");')
-        else:
-            display.frame.evaluateJavaScript('show_video("play");')
+        display.frame.evaluateJavaScript('show_video("play");')
         if start_time > 0:
             self.seek(display, controller.media_info.start_time * 1000)
-        # TODO add playing check and get the correct media length
-        controller.media_info.length = length
-        self.state = MediaState.Playing
+        self.set_state(MediaState.Playing, display)
         display.web_view.raise_()
         return True
 
     def pause(self, display):
         """
         Pause a video
+
+        :param display: The display to be updated.
         """
-        controller = display.controller
-        if controller.media_info.is_flash:
-            display.frame.evaluateJavaScript('show_flash("pause");')
-        else:
-            display.frame.evaluateJavaScript('show_video("pause");')
-        self.state = MediaState.Paused
+        display.frame.evaluateJavaScript('show_video("pause");')
+        self.set_state(MediaState.Paused, display)
 
     def stop(self, display):
         """
         Stop a video
+
+        :param display: The display to be updated.
         """
-        controller = display.controller
-        if controller.media_info.is_flash:
-            display.frame.evaluateJavaScript('show_flash("stop");')
-        else:
-            display.frame.evaluateJavaScript('show_video("stop");')
-        self.state = MediaState.Stopped
+        display.frame.evaluateJavaScript('show_video("stop");')
+        self.set_state(MediaState.Stopped, display)
 
     def volume(self, display, volume):
         """
         Set the volume
+
+        :param display: The display to be updated.
+        :param volume: The volume to be set.
         """
-        controller = display.controller
         # 1.0 is the highest value
         if display.has_audio:
             vol = float(volume) / float(100)
-            if not controller.media_info.is_flash:
-                display.frame.evaluateJavaScript('show_video(null, null, %s);' % str(vol))
+            display.frame.evaluateJavaScript('show_video(null, null, %s);' % str(vol))
 
     def seek(self, display, seek_value):
         """
         Go to a position in the video
+
+        :param display: The display to be updated.
+        :param seek_value: The value to be set.
         """
-        controller = display.controller
-        if controller.media_info.is_flash:
-            seek = seek_value
-            display.frame.evaluateJavaScript('show_flash("seek", null, null, "%s");' % seek)
-        else:
-            seek = float(seek_value) / 1000
-            display.frame.evaluateJavaScript('show_video("seek", null, null, null, "%f");' % seek)
+        seek = float(seek_value) / 1000
+        display.frame.evaluateJavaScript('show_video("seek", null, null, null, "%f");' % seek)
 
     def reset(self, display):
         """
         Reset the player
+
+        :param display: The display to be updated.
         """
-        controller = display.controller
-        if controller.media_info.is_flash:
-            display.frame.evaluateJavaScript('show_flash("close");')
-        else:
-            display.frame.evaluateJavaScript('show_video("close");')
-        self.state = MediaState.Off
+        display.frame.evaluateJavaScript('show_video("close");')
+        self.set_state(MediaState.Off, display)
 
-    def set_visible(self, display, status):
+    def set_visible(self, display, visibility):
         """
         Set the visibility
+
+        :param display: The display to be updated.
+        :param visibility: The visibility to be set.
         """
-        controller = display.controller
-        if status:
+        if visibility:
             is_visible = "visible"
         else:
             is_visible = "hidden"
-        if controller.media_info.is_flash:
-            display.frame.evaluateJavaScript('show_flash("setVisible", null, null, "%s");' % is_visible)
-        else:
-            display.frame.evaluateJavaScript('show_video("setVisible", null, null, null, "%s");' % is_visible)
+        display.frame.evaluateJavaScript('show_video("setVisible", null, null, null, "%s");' % is_visible)
 
     def update_ui(self, display):
         """
         Update the UI
+
+        :param display: The display to be updated.
         """
         controller = display.controller
-        if controller.media_info.is_flash:
-            current_time = display.frame.evaluateJavaScript('show_flash("current_time");')
-            length = display.frame.evaluateJavaScript('show_flash("length");')
-        else:
-            if display.frame.evaluateJavaScript('show_video("isEnded");'):
-                self.stop(display)
-            current_time = display.frame.evaluateJavaScript('show_video("current_time");')
-            # check if conversion was ok and value is not 'NaN'
-            if current_time and current_time != float('inf'):
-                current_time = int(current_time * 1000)
-            length = display.frame.evaluateJavaScript('show_video("length");')
-            # check if conversion was ok and value is not 'NaN'
-            if length and length != float('inf'):
-                length = int(length * 1000)
+        if display.frame.evaluateJavaScript('show_video("isEnded");'):
+            self.stop(display)
+        current_time = display.frame.evaluateJavaScript('show_video("current_time");')
+        # check if conversion was ok and value is not 'NaN'
+        if current_time and current_time != float('inf'):
+            current_time = int(current_time * 1000)
+        length = display.frame.evaluateJavaScript('show_video("length");')
+        # check if conversion was ok and value is not 'NaN'
+        if length and length != float('inf'):
+            length = int(length * 1000)
         if current_time and length:
             controller.media_info.length = length
             controller.seek_slider.setMaximum(length)

=== modified file 'openlp/core/ui/slidecontroller.py'
--- openlp/core/ui/slidecontroller.py	2016-03-31 16:34:22 +0000
+++ openlp/core/ui/slidecontroller.py	2016-04-07 18:04:52 +0000
@@ -84,7 +84,7 @@
         super(DisplayController, self).__init__(parent)
         self.is_live = False
         self.display = None
-        self.controller_type = DisplayControllerType.Plugin
+        self.controller_type = None
 
     def send_to_plugins(self, *args):
         """

=== modified file 'openlp/core/ui/themeform.py'
--- openlp/core/ui/themeform.py	2016-04-05 17:30:20 +0000
+++ openlp/core/ui/themeform.py	2016-04-07 18:04:52 +0000
@@ -147,6 +147,7 @@
     def update_lines_text(self, lines):
         """
         Updates the lines on a page on the wizard
+        :param lines: then number of lines to be displayed
         """
         self.main_line_count_label.setText(
             translate('OpenLP.ThemeForm', '(approximately %d lines per slide)') % int(lines))
@@ -186,6 +187,7 @@
     def on_current_id_changed(self, page_id):
         """
         Detects Page changes and updates as appropriate.
+        :param page_id: current page number
         """
         enabled = self.page(page_id) == self.area_position_page
         self.setOption(QtWidgets.QWizard.HaveCustomButton1, enabled)

=== modified file 'openlp/core/ui/wizard.py'
--- openlp/core/ui/wizard.py	2015-12-31 22:46:06 +0000
+++ openlp/core/ui/wizard.py	2016-04-07 18:04:52 +0000
@@ -111,6 +111,7 @@
     def setupUi(self, image):
         """
         Set up the wizard UI.
+        :param image: path to start up image
         """
         self.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
         self.setModal(True)
@@ -210,6 +211,7 @@
     def on_current_id_changed(self, page_id):
         """
         Perform necessary functions depending on which wizard page is active.
+        :param page_id: current page number
         """
         if self.with_progress_page and self.page(page_id) == self.progress_page:
             self.pre_wizard()
@@ -221,6 +223,7 @@
     def custom_page_changed(self, page_id):
         """
         Called when changing to a page other than the progress page
+        :param page_id: current page number
         """
         pass
 

=== modified file 'openlp/plugins/media/lib/mediaitem.py'
--- openlp/plugins/media/lib/mediaitem.py	2016-04-03 19:44:09 +0000
+++ openlp/plugins/media/lib/mediaitem.py	2016-04-07 18:04:52 +0000
@@ -29,8 +29,8 @@
     translate
 from openlp.core.lib import ItemCapabilities, MediaManagerItem, MediaType, ServiceItem, ServiceItemContext, \
     build_icon, check_item_selected
-from openlp.core.lib.ui import critical_error_message_box, create_horizontal_adjusting_combo_box
-from openlp.core.ui import DisplayController, Display, DisplayControllerType
+from openlp.core.lib.ui import create_widget_action, critical_error_message_box, create_horizontal_adjusting_combo_box
+from openlp.core.ui import DisplayControllerType
 from openlp.core.ui.media import get_media_players, set_media_players, parse_optical_path, format_milliseconds
 from openlp.core.common.languagemanager import get_locale_key
 from openlp.core.ui.media.vlcplayer import get_vlc
@@ -78,19 +78,9 @@
         self.single_service_item = False
         self.has_search = True
         self.media_object = None
-        self.display_controller = DisplayController(self.parent())
-        self.display_controller.controller_layout = QtWidgets.QVBoxLayout()
-        self.media_controller.register_controller(self.display_controller)
-        self.media_controller.set_controls_visible(self.display_controller, False)
-        self.display_controller.preview_display = Display(self.display_controller)
-        self.display_controller.preview_display.hide()
-        self.display_controller.preview_display.setGeometry(QtCore.QRect(0, 0, 300, 300))
-        self.display_controller.preview_display.screen = {'size': self.display_controller.preview_display.geometry()}
-        self.display_controller.preview_display.setup()
-        self.media_controller.setup_display(self.display_controller.preview_display, False)
+        # self.display_controller = DisplayController(self.parent())
         Registry().register_function('video_background_replaced', self.video_background_replaced)
         Registry().register_function('mediaitem_media_rebuild', self.rebuild_players)
-        Registry().register_function('config_screen_changed', self.display_setup)
         # Allow DnD from the desktop
         self.list_view.activateDnD()
 
@@ -101,12 +91,17 @@
         """
         self.on_new_prompt = translate('MediaPlugin.MediaItem', 'Select Media')
         self.replace_action.setText(UiStrings().ReplaceBG)
+        self.replace_action_context.setText(UiStrings().ReplaceBG)
         if 'webkit' in get_media_players()[0]:
             self.replace_action.setToolTip(UiStrings().ReplaceLiveBG)
+            self.replace_action_context.setToolTip(UiStrings().ReplaceLiveBG)
         else:
             self.replace_action.setToolTip(UiStrings().ReplaceLiveBGDisabled)
+            self.replace_action_context.setToolTip(UiStrings().ReplaceLiveBGDisabled)
         self.reset_action.setText(UiStrings().ResetBG)
         self.reset_action.setToolTip(UiStrings().ResetLiveBG)
+        self.reset_action_context.setText(UiStrings().ResetBG)
+        self.reset_action_context.setToolTip(UiStrings().ResetLiveBG)
         self.automatic = UiStrings().Automatic
         self.display_type_label.setText(translate('MediaPlugin.MediaItem', 'Use Player:'))
 
@@ -155,6 +150,7 @@
                                                               triggers=self.on_replace_click)
         if 'webkit' not in get_media_players()[0]:
             self.replace_action.setDisabled(True)
+            self.replace_action_context.setDisabled(True)
         self.reset_action = self.toolbar.add_toolbar_action('reset_action', icon=':/system/system_close.png',
                                                             visible=False, triggers=self.on_reset_click)
         self.media_widget = QtWidgets.QWidget(self)
@@ -173,7 +169,17 @@
         self.page_layout.addWidget(self.media_widget)
         self.display_type_combo_box.currentIndexChanged.connect(self.override_player_changed)
 
-    def override_player_changed(self, index):
+    def add_custom_context_actions(self):
+        create_widget_action(self.list_view, separator=True)
+        self.replace_action_context = create_widget_action(
+            self.list_view, text=UiStrings().ReplaceBG, icon=':/slides/slide_blank.png',
+            triggers=self.on_replace_click)
+        self.reset_action_context = create_widget_action(
+            self.list_view, text=UiStrings().ReplaceLiveBG, icon=':/system/system_close.png',
+            visible=False, triggers=self.on_reset_click)
+
+    @staticmethod
+    def override_player_changed(index):
         """
         The Player has been overridden
 
@@ -191,12 +197,14 @@
         """
         self.media_controller.media_reset(self.live_controller)
         self.reset_action.setVisible(False)
+        self.reset_action_context.setVisible(False)
 
     def video_background_replaced(self):
         """
         Triggered by main display on change of serviceitem.
         """
         self.reset_action.setVisible(False)
+        self.reset_action_context.setVisible(False)
 
     def on_replace_click(self):
         """
@@ -215,6 +223,7 @@
                 service_item.add_from_command(path, name, CLAPPERBOARD)
                 if self.media_controller.video(DisplayControllerType.Live, service_item, video_behind_text=True):
                     self.reset_action.setVisible(True)
+                    self.reset_action_context.setVisible(True)
                 else:
                     critical_error_message_box(UiStrings().LiveBGError,
                                                translate('MediaPlugin.MediaItem',
@@ -273,16 +282,14 @@
             service_item.processor = self.display_type_combo_box.currentText()
             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.media_controller.media_length(service_item):
-                    return False
+            if not self.media_controller.media_length(service_item):
+                return False
         service_item.add_capability(ItemCapabilities.CanAutoStartForLive)
         service_item.add_capability(ItemCapabilities.CanEditTitle)
         service_item.add_capability(ItemCapabilities.RequiresMedia)
         if Settings().value(self.settings_section + '/media auto start') == QtCore.Qt.Checked:
             service_item.will_auto_start = True
-            # force a non-existent theme
+        # force a non-existent theme
         service_item.theme = -1
         return True
 
@@ -305,12 +312,6 @@
             ' '.join(self.media_controller.video_extensions_list),
             ' '.join(self.media_controller.audio_extensions_list), UiStrings().AllFiles)
 
-    def display_setup(self):
-        """
-        Setup media controller display.
-        """
-        self.media_controller.setup_display(self.display_controller.preview_display, False)
-
     def populate_display_types(self):
         """
         Load the combobox with the enabled media players,  allowing user to select a specific player if settings allow.
@@ -385,16 +386,16 @@
             if item_name:
                 self.list_view.addItem(item_name)
 
-    def get_list(self, type=MediaType.Audio):
+    def get_list(self, media_type=MediaType.Audio):
         """
         Get the list of media, optional select media type.
 
-        :param type: Type to get, defaults to audio.
+        :param media_type: Type to get, defaults to audio.
         :return: The media list
         """
         media = Settings().value(self.settings_section + '/media files')
         media.sort(key=lambda filename: get_locale_key(os.path.split(str(filename))[1]))
-        if type == MediaType.Audio:
+        if media_type == MediaType.Audio:
             extension = self.media_controller.audio_extensions_list
         else:
             extension = self.media_controller.video_extensions_list

=== modified file 'openlp/plugins/media/mediaplugin.py'
--- openlp/plugins/media/mediaplugin.py	2016-01-04 00:18:01 +0000
+++ openlp/plugins/media/mediaplugin.py	2016-04-07 18:04:52 +0000
@@ -60,13 +60,6 @@
         """
         Override the inherited initialise() method in order to upgrade the media before trying to load it
         """
-        # FIXME: Remove after 2.2 release.
-        # This is needed to load the list of media from the config saved before the settings rewrite.
-        if self.media_item_class is not None:
-            loaded_list = Settings().get_files_from_config(self)
-            # Now save the list to the config using our Settings class.
-            if loaded_list:
-                Settings().setValue('%s/%s files' % (self.settings_section, self.name), loaded_list)
         super().initialise()
 
     def app_startup(self):

=== modified file 'tests/functional/openlp_core_ui_media/test_mediacontroller.py'
--- tests/functional/openlp_core_ui_media/test_mediacontroller.py	2015-12-31 22:46:06 +0000
+++ tests/functional/openlp_core_ui_media/test_mediacontroller.py	2016-04-07 18:04:52 +0000
@@ -78,10 +78,11 @@
         """
         Test that we don't try to play media when no players available
         """
-        # GIVEN: A mocked UiStrings, get_media_players, controller, display and service_item
-        with patch('openlp.core.ui.media.mediacontroller.get_media_players') as mocked_get_media_players,\
+        # GIVEN: A mocked UiStrings, get_used_players, controller, display and service_item
+        with patch('openlp.core.ui.media.mediacontroller.MediaController._get_used_players') as \
+                mocked_get_used_players,\
                 patch('openlp.core.ui.media.mediacontroller.UiStrings') as mocked_uistrings:
-            mocked_get_media_players.return_value = ([], '')
+            mocked_get_used_players.return_value = ([])
             mocked_ret_uistrings = MagicMock()
             mocked_ret_uistrings.Automatic = 1
             mocked_uistrings.return_value = mocked_ret_uistrings
@@ -97,14 +98,14 @@
             # THEN: it should return False
             self.assertFalse(ret, '_check_file_type should return False when no mediaplayers are available.')
 
-    @patch('openlp.core.ui.media.mediacontroller.get_media_players')
+    @patch('openlp.core.ui.media.mediacontroller.MediaController._get_used_players')
     @patch('openlp.core.ui.media.mediacontroller.UiStrings')
-    def check_file_type_no_processor_test(self, mocked_uistrings, mocked_get_media_players):
+    def check_file_type_no_processor_test(self, mocked_uistrings, mocked_get_used_players):
         """
         Test that we don't try to play media when the processor for the service item is None
         """
         # GIVEN: A mocked UiStrings, get_media_players, controller, display and service_item
-        mocked_get_media_players.return_value = ([], '')
+        mocked_get_used_players.return_value = ([], '')
         mocked_ret_uistrings = MagicMock()
         mocked_ret_uistrings.Automatic = 1
         mocked_uistrings.return_value = mocked_ret_uistrings
@@ -120,14 +121,14 @@
         # THEN: it should return False
         self.assertFalse(ret, '_check_file_type should return False when the processor for service_item is None.')
 
-    @patch('openlp.core.ui.media.mediacontroller.get_media_players')
+    @patch('openlp.core.ui.media.mediacontroller.MediaController._get_used_players')
     @patch('openlp.core.ui.media.mediacontroller.UiStrings')
-    def check_file_type_automatic_processor_test(self, mocked_uistrings, mocked_get_media_players):
+    def check_file_type_automatic_processor_test(self, mocked_uistrings, mocked_get_used_players):
         """
         Test that we can play media when players are available and we have a automatic processor from the service item
         """
         # GIVEN: A mocked UiStrings, get_media_players, controller, display and service_item
-        mocked_get_media_players.return_value = (['vlc', 'webkit'], '')
+        mocked_get_used_players.return_value = (['vlc', 'webkit'])
         mocked_ret_uistrings = MagicMock()
         mocked_ret_uistrings.Automatic = 1
         mocked_uistrings.return_value = mocked_ret_uistrings
@@ -150,21 +151,21 @@
         self.assertTrue(ret, '_check_file_type should return True when mediaplayers are available and '
                              'the service item has an automatic processor.')
 
-    @patch('openlp.core.ui.media.mediacontroller.get_media_players')
+    @patch('openlp.core.ui.media.mediacontroller.MediaController._get_used_players')
     @patch('openlp.core.ui.media.mediacontroller.UiStrings')
-    def check_file_type_processor_different_from_available_test(self, mocked_uistrings, mocked_get_media_players):
+    def check_file_type_processor_different_from_available_test(self, mocked_uistrings, mocked_get_used_players):
         """
         Test that we can play media when players available are different from the processor from the service item
         """
         # GIVEN: A mocked UiStrings, get_media_players, controller, display and service_item
-        mocked_get_media_players.return_value = (['phonon'], '')
+        mocked_get_used_players.return_value = (['system'])
         mocked_ret_uistrings = MagicMock()
         mocked_ret_uistrings.Automatic = 'automatic'
         mocked_uistrings.return_value = mocked_ret_uistrings
         media_controller = MediaController()
         mocked_phonon = MagicMock()
         mocked_phonon.video_extensions_list = ['*.mp4']
-        media_controller.media_players = {'phonon': mocked_phonon}
+        media_controller.media_players = {'system': mocked_phonon}
         mocked_controller = MagicMock()
         mocked_suffix = MagicMock()
         mocked_suffix.return_value = 'mp4'

=== modified file 'tests/functional/openlp_core_ui_media/test_vlcplayer.py'
--- tests/functional/openlp_core_ui_media/test_vlcplayer.py	2015-12-31 22:46:06 +0000
+++ tests/functional/openlp_core_ui_media/test_vlcplayer.py	2016-04-07 18:04:52 +0000
@@ -380,7 +380,6 @@
         mocked_display.vlc_media_player.set_media.assert_called_with(mocked_vlc_media)
         mocked_vlc_media.parse.assert_called_with()
         mocked_volume.assert_called_with(mocked_display, 100)
-        self.assertEqual(10, mocked_controller.media_info.length)
         self.assertTrue(result)
 
     @patch('openlp.core.ui.media.vlcplayer.is_win')
@@ -426,7 +425,6 @@
         mocked_display.vlc_media_player.set_media.assert_called_with(mocked_vlc_media)
         mocked_vlc_media.parse.assert_called_with()
         mocked_volume.assert_called_with(mocked_display, 100)
-        self.assertEqual(10, mocked_controller.media_info.length)
         self.assertTrue(result)
 
     @patch('openlp.core.ui.media.vlcplayer.is_win')
@@ -472,7 +470,7 @@
         mocked_display.vlc_media_player.set_media.assert_called_with(mocked_vlc_media)
         mocked_vlc_media.parse.assert_called_with()
         mocked_volume.assert_called_with(mocked_display, 100)
-        self.assertEqual(10, mocked_controller.media_info.length)
+        # self.assertEqual(10, mocked_controller.media_info.length)
         self.assertTrue(result)
 
     @patch('openlp.core.ui.media.vlcplayer.is_win')
@@ -628,7 +626,7 @@
         mocked_display.controller = mocked_controller
         mocked_display.vlc_media_player.get_media.return_value = mocked_media
         vlc_player = VlcPlayer(None)
-        vlc_player.state = MediaState.Paused
+        vlc_player.set_state(MediaState.Paused, mocked_display)
 
         # WHEN: play() is called
         with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \
@@ -638,10 +636,9 @@
 
         # THEN: A bunch of things should happen to play the media
         mocked_thread.start.assert_called_with()
-        self.assertEqual(50, mocked_controller.media_info.length)
         mocked_volume.assert_called_with(mocked_display, 100)
         mocked_controller.seek_slider.setMaximum.assert_called_with(50000)
-        self.assertEqual(MediaState.Playing, vlc_player.state)
+        self.assertEqual(MediaState.Playing, vlc_player.get_live_state())
         mocked_display.vlc_widget.raise_.assert_called_with()
         self.assertTrue(result, 'The value returned from play() should be True')
 
@@ -661,7 +658,7 @@
         mocked_display = MagicMock()
         mocked_display.controller = mocked_controller
         vlc_player = VlcPlayer(None)
-        vlc_player.state = MediaState.Paused
+        vlc_player.set_state(MediaState.Paused, mocked_display)
 
         # WHEN: play() is called
         with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \
@@ -695,7 +692,7 @@
         mocked_display = MagicMock()
         mocked_display.controller = mocked_controller
         vlc_player = VlcPlayer(None)
-        vlc_player.state = MediaState.Paused
+        vlc_player.set_state(MediaState.Paused, mocked_display)
 
         # WHEN: play() is called
         with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \
@@ -709,10 +706,9 @@
         mocked_display.vlc_media_player.play.assert_called_with()
         mocked_display.vlc_media_player.audio_set_track.assert_called_with(1)
         mocked_display.vlc_media_player.video_set_spu.assert_called_with(1)
-        self.assertEqual(50, mocked_controller.media_info.length)
         mocked_volume.assert_called_with(mocked_display, 100)
         mocked_controller.seek_slider.setMaximum.assert_called_with(50000)
-        self.assertEqual(MediaState.Playing, vlc_player.state)
+        self.assertEqual(MediaState.Playing, vlc_player.get_live_state())
         mocked_display.vlc_widget.raise_.assert_called_with()
         self.assertTrue(result, 'The value returned from play() should be True')
 
@@ -739,7 +735,7 @@
         mocked_display.vlc_media.get_state.assert_called_with()
         mocked_display.vlc_media_player.pause.assert_called_with()
         mocked_media_state_wait.assert_called_with(mocked_display, 2)
-        self.assertEqual(MediaState.Paused, vlc_player.state)
+        self.assertEqual(MediaState.Paused, vlc_player.get_live_state())
 
     @patch('openlp.core.ui.media.vlcplayer.get_vlc')
     def pause_not_playing_test(self, mocked_get_vlc):
@@ -805,7 +801,7 @@
         # THEN: A thread should have been started to stop VLC
         mocked_threading.Thread.assert_called_with(target=mocked_stop)
         mocked_thread.start.assert_called_with()
-        self.assertEqual(MediaState.Stopped, vlc_player.state)
+        self.assertEqual(MediaState.Stopped, vlc_player.get_live_state())
 
     def volume_test(self):
         """
@@ -900,10 +896,10 @@
         # WHEN: reset() is called
         vlc_player.reset(mocked_display)
 
-        # THEN: The media should be stopped and invsibile
+        # THEN: The media should be stopped and invisible
         mocked_display.vlc_media_player.stop.assert_called_with()
         mocked_display.vlc_widget.setVisible.assert_called_with(False)
-        self.assertEqual(MediaState.Off, vlc_player.state)
+        self.assertEqual(MediaState.Off, vlc_player.get_live_state())
 
     def set_visible_has_own_widget_test(self):
         """

=== added directory 'tests/interfaces/openlp_core_ul_media_vendor'
=== added file 'tests/interfaces/openlp_core_ul_media_vendor/__init__.py'
--- tests/interfaces/openlp_core_ul_media_vendor/__init__.py	1970-01-01 00:00:00 +0000
+++ tests/interfaces/openlp_core_ul_media_vendor/__init__.py	2016-04-07 18:04:52 +0000
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2016 OpenLP Developers                                   #
+# --------------------------------------------------------------------------- #
+# This program is free software; you can redistribute it and/or modify it     #
+# under the terms of the GNU General Public License as published by the Free  #
+# Software Foundation; 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                          #
+###############################################################################

=== added file 'tests/interfaces/openlp_core_ul_media_vendor/test_mediainfoWrapper.py'
--- tests/interfaces/openlp_core_ul_media_vendor/test_mediainfoWrapper.py	1970-01-01 00:00:00 +0000
+++ tests/interfaces/openlp_core_ul_media_vendor/test_mediainfoWrapper.py	2016-04-07 18:04:52 +0000
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2016 OpenLP Developers                                   #
+# --------------------------------------------------------------------------- #
+# This program is free software; you can redistribute it and/or modify it     #
+# under the terms of the GNU General Public License as published by the Free  #
+# Software Foundation; 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                          #
+###############################################################################
+"""
+Package to test the openlp.core.ui.media package.
+"""
+
+import os
+from unittest import TestCase
+
+from openlp.core.ui.media.vendor.mediainfoWrapper import MediaInfoWrapper
+
+TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources', 'media'))
+
+TEST_MEDIA = [['avi_file.avi', 61495], ['mp3_file.mp3', 134426], ['mpg_file.mpg', 9404], ['mp4_file.mp4', 188336]]
+
+
+class TestMediainfoWrapper(TestCase):
+
+    def media_length_test(self):
+        """
+        Test the Media Info basic functionality
+        """
+        for test_data in TEST_MEDIA:
+            # GIVEN: a media file
+            full_path = os.path.normpath(os.path.join(TEST_PATH, test_data[0]))
+
+            # WHEN the media data is retrieved
+            results = MediaInfoWrapper.parse(full_path)
+
+            # THEN you can determine the run time
+            self.assertEqual(results.tracks[0].duration, test_data[1], 'The correct duration is returned for ' +
+                                                                       test_data[0])

=== added directory 'tests/resources/media'
=== added file 'tests/resources/media/avi_file.avi'
Binary files tests/resources/media/avi_file.avi	1970-01-01 00:00:00 +0000 and tests/resources/media/avi_file.avi	2016-04-07 18:04:52 +0000 differ
=== added file 'tests/resources/media/mp3_file.mp3'
Binary files tests/resources/media/mp3_file.mp3	1970-01-01 00:00:00 +0000 and tests/resources/media/mp3_file.mp3	2016-04-07 18:04:52 +0000 differ
=== added file 'tests/resources/media/mp4_file.mp4'
Binary files tests/resources/media/mp4_file.mp4	1970-01-01 00:00:00 +0000 and tests/resources/media/mp4_file.mp4	2016-04-07 18:04:52 +0000 differ
=== added file 'tests/resources/media/mpg_file.mpg'
Binary files tests/resources/media/mpg_file.mpg	1970-01-01 00:00:00 +0000 and tests/resources/media/mpg_file.mpg	2016-04-07 18:04:52 +0000 differ

Follow ups