Merged from trunk

This commit is contained in:
Ian Knight 2016-04-16 16:32:36 +09:30
commit c8172d882f
27 changed files with 707 additions and 359 deletions

View File

@ -610,7 +610,7 @@ class ServiceItem(RegistryProperties):
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:

View File

@ -68,7 +68,6 @@ class DisplayControllerType(object):
"""
Live = 0
Preview = 1
Plugin = 2
class SingleColumnTableWidget(QtWidgets.QTableWidget):

View File

@ -60,12 +60,14 @@ class MediaInfo(object):
"""
file_info = None
volume = 100
is_flash = False
is_background = False
can_loop_playback = False
length = 0
start_time = 0
end_time = 0
title_track = 0
is_playing = False
timer = 1000
audio_track = 0
subtitle_track = 0
media_type = MediaType()
@ -104,15 +106,15 @@ def set_media_players(players_list, overridden_player='auto'):
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])

View File

@ -33,12 +33,15 @@ from openlp.core.lib import OpenLPToolbar, ItemCapabilities
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 @@ class MediaSlider(QtWidgets.QSlider):
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 @@ class MediaSlider(QtWidgets.QSlider):
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 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
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 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
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 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
self._generate_extensions_lists()
return True
def media_state(self):
def media_state_live(self):
"""
Check if there is a running media Player and do updating stuff (e.g. update the UI)
Check if there is a running Live media Player and do updating stuff (e.g. update the UI)
"""
if not list(self.current_media_players.keys()):
self.timer.stop()
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:
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()
self.live_timer.stop()
self.media_stop(self.display_controllers[DisplayControllerType.Live])
if self.display_controllers[DisplayControllerType.Live].media_info.can_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.can_loop_playback:
self.media_play(self.display_controllers[DisplayControllerType.Preview], True)
def get_media_display_css(self):
"""
@ -274,6 +295,15 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
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 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
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 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
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 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
: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 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
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.can_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 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
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 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
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 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
: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'))
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
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])
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 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
: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 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
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 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
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 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
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 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
: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 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
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.is_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.is_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.can_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 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
: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.is_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.can_loop_playback = not controller.media_info.can_loop_playback
controller.mediabar.actions['playbackLoop'].setChecked(controller.media_info.can_loop_playback)
def media_stop_msg(self, msg):
"""
@ -645,25 +729,28 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
: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.is_playing = False
controller.media_info.timer = 1000
controller.media_timer = 0
def media_volume_msg(self, msg):
"""
@ -694,7 +781,6 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
: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 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
: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 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
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 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
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 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
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

View File

@ -41,7 +41,7 @@ class MediaPlayer(RegistryProperties):
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 @@ class MediaPlayer(RegistryProperties):
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 @@ class MediaPlayer(RegistryProperties):
"""
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,45 @@ class MediaPlayer(RegistryProperties):
Returns Information about the player
"""
return ''
def get_live_state(self):
"""
Get the state of the live player
:return: Live state
"""
return self.state[0]
def set_live_state(self, state):
"""
Set the State of the Live player
:param state: State to be set
:return: None
"""
self.state[0] = state
def get_preview_state(self):
"""
Get the state of the preview player
:return: Preview State
"""
return self.state[1]
def set_preview_state(self, state):
"""
Set the state of the Preview Player
:param state: State to be set
:return: None
"""
self.state[1] = state
def set_state(self, state, display):
"""
Set the State based on the display being processed
:param state: State to be set
:param display: Identify the Display type
:return: None
"""
if display.controller.is_live:
self.set_live_state(state)
else:
self.set_preview_state(state)

View File

@ -133,12 +133,16 @@ class PlayerTab(SettingsTab):
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:

View File

@ -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 @@ class SystemPlayer(MediaPlayer):
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 @@ class SystemPlayer(MediaPlayer):
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():

View File

@ -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())

View File

@ -144,6 +144,9 @@ class VlcPlayer(MediaPlayer):
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 @@ class VlcPlayer(MediaPlayer):
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 @@ class VlcPlayer(MediaPlayer):
# 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 @@ class VlcPlayer(MediaPlayer):
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 @@ class VlcPlayer(MediaPlayer):
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 @@ class VlcPlayer(MediaPlayer):
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 @@ class VlcPlayer(MediaPlayer):
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 @@ class VlcPlayer(MediaPlayer):
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.

View File

@ -99,74 +99,6 @@ VIDEO_HTML = """
<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 @@ class WebkitPlayer(MediaPlayer):
"""
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 @@ class WebkitPlayer(MediaPlayer):
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 @@ class WebkitPlayer(MediaPlayer):
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
self.set_visible(display, True)
if controller.media_info.is_flash:
display.frame.evaluateJavaScript('show_flash("play");')
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:
display.frame.evaluateJavaScript('show_video("play");')
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)
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
"""
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
def set_visible(self, display, status):
:param display: The display to be updated.
"""
display.frame.evaluateJavaScript('show_video("close");')
self.set_state(MediaState.Off, display)
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)

View File

@ -84,7 +84,7 @@ class DisplayController(QtWidgets.QWidget):
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):
"""

View File

@ -147,6 +147,7 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
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 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
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)

View File

@ -111,6 +111,7 @@ class OpenLPWizard(QtWidgets.QWizard, RegistryProperties):
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 @@ class OpenLPWizard(QtWidgets.QWizard, RegistryProperties):
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 @@ class OpenLPWizard(QtWidgets.QWizard, RegistryProperties):
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

View File

@ -195,7 +195,7 @@ class ImageMediaItem(MediaManagerItem):
Add custom buttons to the end of the toolbar
"""
self.replace_action = self.toolbar.add_toolbar_action('replace_action',
icon=':/slides/slide_blank.png',
icon=':/slides/slide_theme.png',
triggers=self.on_replace_click)
self.reset_action = self.toolbar.add_toolbar_action('reset_action',
icon=':/system/system_close.png',

View File

@ -29,8 +29,8 @@ from openlp.core.common import Registry, RegistryProperties, AppLocation, Settin
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 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
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 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
"""
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:'))
@ -151,10 +146,11 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
Adds buttons to the end of the header bar.
"""
# Replace backgrounds do not work at present so remove functionality.
self.replace_action = self.toolbar.add_toolbar_action('replace_action', icon=':/slides/slide_blank.png',
self.replace_action = self.toolbar.add_toolbar_action('replace_action', icon=':/slides/slide_theme.png',
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 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
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 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
"""
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 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
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 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
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 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
' '.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 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
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

View File

@ -60,13 +60,6 @@ class MediaPlugin(Plugin):
"""
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):

Binary file not shown.

Before

Width:  |  Height:  |  Size: 815 B

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -148,7 +148,7 @@ class TestMainWindow(TestCase, TestMixin):
# THEN: the following registry functions should have been registered
self.assertEqual(len(self.registry.service_list), 6, 'The registry should have 6 services.')
self.assertEqual(len(self.registry.functions_list), 16, 'The registry should have 16 functions')
self.assertEqual(len(self.registry.functions_list), 17, 'The registry should have 17 functions')
self.assertTrue('application' in self.registry.service_list, 'The application should have been registered.')
self.assertTrue('main_window' in self.registry.service_list, 'The main_window should have been registered.')
self.assertTrue('media_controller' in self.registry.service_list, 'The media_controller should have been '

View File

@ -78,10 +78,11 @@ class TestMediaController(TestCase, TestMixin):
"""
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 @@ class TestMediaController(TestCase, TestMixin):
# 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 @@ class TestMediaController(TestCase, TestMixin):
# 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 @@ class TestMediaController(TestCase, TestMixin):
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'

View File

@ -380,7 +380,6 @@ class TestVLCPlayer(TestCase, TestMixin):
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 @@ class TestVLCPlayer(TestCase, TestMixin):
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,6 @@ class TestVLCPlayer(TestCase, TestMixin):
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')
@ -628,7 +625,7 @@ class TestVLCPlayer(TestCase, TestMixin):
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 +635,8 @@ class TestVLCPlayer(TestCase, TestMixin):
# 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 +656,7 @@ class TestVLCPlayer(TestCase, TestMixin):
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 +690,7 @@ class TestVLCPlayer(TestCase, TestMixin):
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 +704,8 @@ class TestVLCPlayer(TestCase, TestMixin):
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 +732,7 @@ class TestVLCPlayer(TestCase, TestMixin):
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 +798,7 @@ class TestVLCPlayer(TestCase, TestMixin):
# 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 +893,10 @@ class TestVLCPlayer(TestCase, TestMixin):
# 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):
"""

View File

@ -54,8 +54,6 @@ class MediaPluginTest(TestCase, TestMixin):
media_plugin.initialise()
# THEN: The settings should be upgraded and the base initialise() method should be called
mocked_settings.get_files_from_config.assert_called_with(media_plugin)
mocked_settings.setValue.assert_called_with('media/media files', True)
mocked_initialise.assert_called_with()
def test_about_text(self):

View File

@ -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 #
###############################################################################

View File

@ -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])

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.