Insert timeout on acquiring lock in slidecontroller to avoid hang. Fixes bug 1413324.

Run XInitThreads when using VLC to make it work correctly. Fixes bug 1433245.
Disable the stop button in the slidecontroller mediaplayer buttons instead of hiding it to stop the position and audio slider resizing. Fixes bug 1431478.
Fixed a webkit player exception. Fixes bug 1431476.
Fix crash if presentation file in the mediamanger was removed bewteen sessions. Fixes bug 1432418.
Updated the list of extensions supported by VLC.

bzr-revno: 2524
Fixes: https://launchpad.net/bugs/1413324, https://launchpad.net/bugs/1433245, https://launchpad.net/bugs/1431478, https://launchpad.net/bugs/1431476, https://launchpad.net/bugs/1432418
This commit is contained in:
Tomas Groth 2015-03-22 22:27:36 +02:00 committed by Raoul Snyman
commit 41c695bbec
6 changed files with 63 additions and 38 deletions

View File

@ -589,7 +589,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
else: else:
controller.mediabar.actions['playbackPlay'].setVisible(False) controller.mediabar.actions['playbackPlay'].setVisible(False)
controller.mediabar.actions['playbackPause'].setVisible(True) controller.mediabar.actions['playbackPause'].setVisible(True)
controller.mediabar.actions['playbackStop'].setVisible(True) controller.mediabar.actions['playbackStop'].setDisabled(False)
if controller.is_live: if controller.is_live:
if controller.hide_menu.defaultAction().isChecked() and not controller.media_info.is_background: if controller.hide_menu.defaultAction().isChecked() and not controller.media_info.is_background:
controller.hide_menu.defaultAction().trigger() controller.hide_menu.defaultAction().trigger()
@ -619,7 +619,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
display = self._define_display(controller) display = self._define_display(controller)
self.current_media_players[controller.controller_type].pause(display) self.current_media_players[controller.controller_type].pause(display)
controller.mediabar.actions['playbackPlay'].setVisible(True) controller.mediabar.actions['playbackPlay'].setVisible(True)
controller.mediabar.actions['playbackStop'].setVisible(True) controller.mediabar.actions['playbackStop'].setDisabled(False)
controller.mediabar.actions['playbackPause'].setVisible(False) controller.mediabar.actions['playbackPause'].setVisible(False)
def media_stop_msg(self, msg): def media_stop_msg(self, msg):
@ -645,7 +645,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
self.current_media_players[controller.controller_type].set_visible(display, False) self.current_media_players[controller.controller_type].set_visible(display, False)
controller.seek_slider.setSliderPosition(0) controller.seek_slider.setSliderPosition(0)
controller.mediabar.actions['playbackPlay'].setVisible(True) controller.mediabar.actions['playbackPlay'].setVisible(True)
controller.mediabar.actions['playbackStop'].setVisible(False) controller.mediabar.actions['playbackStop'].setDisabled(True)
controller.mediabar.actions['playbackPause'].setVisible(False) controller.mediabar.actions['playbackPause'].setVisible(False)
def media_volume_msg(self, msg): def media_volume_msg(self, msg):

View File

@ -27,10 +27,11 @@ from distutils.version import LooseVersion
import logging import logging
import os import os
import threading import threading
import sys
from PyQt4 import QtGui from PyQt4 import QtGui
from openlp.core.common import Settings, is_win, is_macosx from openlp.core.common import Settings, is_win, is_macosx, is_linux
from openlp.core.lib import translate from openlp.core.lib import translate
from openlp.core.ui.media import MediaState, MediaType from openlp.core.ui.media import MediaState, MediaType
from openlp.core.ui.media.mediaplayer import MediaPlayer from openlp.core.ui.media.mediaplayer import MediaPlayer
@ -62,36 +63,30 @@ if VLC_AVAILABLE:
if LooseVersion(VERSION.split()[0]) < LooseVersion('1.1.0'): if LooseVersion(VERSION.split()[0]) < LooseVersion('1.1.0'):
VLC_AVAILABLE = False VLC_AVAILABLE = False
log.debug('VLC could not be loaded, because the vlc version is too old: %s' % VERSION) log.debug('VLC could not be loaded, because the vlc version is too old: %s' % VERSION)
# On linux we need to initialise X threads, but not when running tests.
if VLC_AVAILABLE and is_linux() and 'nose' not in sys.argv[0]:
import ctypes
try:
x11 = ctypes.cdll.LoadLibrary('libX11.so')
x11.XInitThreads()
except:
log.exception('Failed to run XInitThreads(), VLC might not work properly!')
AUDIO_EXT = ['*.mp3', '*.wav', '*.wma', '*.ogg'] # Audio and video extensions copied from 'include/vlc_interface.h' from vlc 2.2.0 source
AUDIO_EXT = ['*.3ga', '*.669', '*.a52', '*.aac', '*.ac3', '*.adt', '*.adts', '*.aif', '*.aifc', '*.aiff', '*.amr',
'*.aob', '*.ape', '*.awb', '*.caf', '*.dts', '*.flac', '*.it', '*.kar', '*.m4a', '*.m4b', '*.m4p', '*.m5p',
'*.mid', '*.mka', '*.mlp', '*.mod', '*.mpa', '*.mp1', '*.mp2', '*.mp3', '*.mpc', '*.mpga', '*.mus',
'*.oga', '*.ogg', '*.oma', '*.opus', '*.qcp', '*.ra', '*.rmi', '*.s3m', '*.sid', '*.spx', '*.thd', '*.tta',
'*.voc', '*.vqf', '*.w64', '*.wav', '*.wma', '*.wv', '*.xa', '*.xm']
VIDEO_EXT = [ VIDEO_EXT = ['*.3g2', '*.3gp', '*.3gp2', '*.3gpp', '*.amv', '*.asf', '*.avi', '*.bik', '*.divx', '*.drc', '*.dv',
'*.3gp', '*.f4v', '*.flv', '*.gvi', '*.gxf', '*.iso', '*.m1v', '*.m2v', '*.m2t', '*.m2ts', '*.m4v', '*.mkv',
'*.asf', '*.wmv', '*.mov', '*.mp2', '*.mp2v', '*.mp4', '*.mp4v', '*.mpe', '*.mpeg', '*.mpeg1', '*.mpeg2', '*.mpeg4', '*.mpg',
'*.au', '*.mpv2', '*.mts', '*.mtv', '*.mxf', '*.mxg', '*.nsv', '*.nuv', '*.ogg', '*.ogm', '*.ogv', '*.ogx', '*.ps',
'*.avi', '*.rec', '*.rm', '*.rmvb', '*.rpl', '*.thp', '*.tod', '*.ts', '*.tts', '*.txd', '*.vob', '*.vro', '*.webm',
'*.divx', '*.wm', '*.wmv', '*.wtv', '*.xesc',
'*.flv', # These extensions was not in the official list, added manually.
'*.mov', '*.nut', '*.rv', '*.xvid']
'*.mp4', '*.m4v',
'*.ogm', '*.ogv',
'*.mkv', '*.mka',
'*.ts', '*.mpg',
'*.mpg', '*.mp2',
'*.nsc',
'*.nsv',
'*.nut',
'*.ra', '*.ram', '*.rm', '*.rv', '*.rmbv',
'*.a52', '*.dts', '*.aac', '*.flac', '*.dv', '*.vid',
'*.tta', '*.tac',
'*.ty',
'*.dts',
'*.xa',
'*.iso',
'*.vob',
'*.webm',
'*.xvid'
]
class VlcPlayer(MediaPlayer): class VlcPlayer(MediaPlayer):

View File

@ -375,7 +375,7 @@ class WebkitPlayer(MediaPlayer):
# check if conversion was ok and value is not 'NaN' # check if conversion was ok and value is not 'NaN'
if length and length != float('inf'): if length and length != float('inf'):
length = int(length * 1000) length = int(length * 1000)
if current_time: if current_time and length:
controller.media_info.length = length controller.media_info.length = length
controller.seek_slider.setMaximum(length) controller.seek_slider.setMaximum(length)
if not controller.seek_slider.isSliderDown(): if not controller.seek_slider.isSliderDown():

View File

@ -1069,8 +1069,13 @@ class SlideController(DisplayController, RegistryProperties):
:param start: :param start:
""" """
# Only one thread should be in here at the time. If already locked just skip, since the update will be # Only one thread should be in here at the time. If already locked just skip, since the update will be
# done by the thread holding the lock. If it is a "start" slide, we must wait for the lock. # done by the thread holding the lock. If it is a "start" slide, we must wait for the lock, but only for 0.2
if not self.slide_selected_lock.acquire(start): # seconds, since we don't want to cause a deadlock
timeout = 0.2 if start else -1
if not self.slide_selected_lock.acquire(start, timeout):
if start:
self.log_debug('Could not get lock in slide_selected after waiting %f, skip to avoid deadlock.'
% timeout)
return return
row = self.preview_widget.current_slide_number() row = self.preview_widget.current_slide_number()
self.selected_row = 0 self.selected_row = 0

View File

@ -245,7 +245,8 @@ class PresentationMediaItem(MediaManagerItem):
doc = self.controllers[cidx].add_document(filepath) doc = self.controllers[cidx].add_document(filepath)
if clean_for_update: if clean_for_update:
thumb_path = doc.get_thumbnail_path(1, True) thumb_path = doc.get_thumbnail_path(1, True)
if not thumb_path or os.path.getmtime(thumb_path) < os.path.getmtime(filepath): if not thumb_path or not os.path.exists(filepath) or os.path.getmtime(
thumb_path) < os.path.getmtime(filepath):
doc.presentation_deleted() doc.presentation_deleted()
else: else:
doc.presentation_deleted() doc.presentation_deleted()

View File

@ -87,7 +87,7 @@ class TestMediaItem(TestCase, TestMixin):
def clean_up_thumbnails_test(self): def clean_up_thumbnails_test(self):
""" """
Test that the clean_up_thumbnails method works as expected. Test that the clean_up_thumbnails method works as expected when files exists.
""" """
# GIVEN: A mocked controller, and mocked os.path.getmtime # GIVEN: A mocked controller, and mocked os.path.getmtime
mocked_controller = MagicMock() mocked_controller = MagicMock()
@ -98,8 +98,10 @@ class TestMediaItem(TestCase, TestMixin):
'Mocked': mocked_controller 'Mocked': mocked_controller
} }
presentation_file = 'file.tmp' presentation_file = 'file.tmp'
with patch('openlp.plugins.presentations.lib.mediaitem.os.path.getmtime') as mocked_getmtime: with patch('openlp.plugins.presentations.lib.mediaitem.os.path.getmtime') as mocked_getmtime, \
patch('openlp.plugins.presentations.lib.mediaitem.os.path.exists') as mocked_exists:
mocked_getmtime.side_effect = [100, 200] mocked_getmtime.side_effect = [100, 200]
mocked_exists.return_value = True
# WHEN: calling clean_up_thumbnails # WHEN: calling clean_up_thumbnails
self.media_item.clean_up_thumbnails(presentation_file, True) self.media_item.clean_up_thumbnails(presentation_file, True)
@ -107,3 +109,25 @@ class TestMediaItem(TestCase, TestMixin):
# THEN: doc.presentation_deleted should have been called since the thumbnails mtime will be greater than # THEN: doc.presentation_deleted should have been called since the thumbnails mtime will be greater than
# the presentation_file's mtime. # the presentation_file's mtime.
mocked_doc.assert_has_calls([call.get_thumbnail_path(1, True), call.presentation_deleted()], True) mocked_doc.assert_has_calls([call.get_thumbnail_path(1, True), call.presentation_deleted()], True)
def clean_up_thumbnails_missing_file_test(self):
"""
Test that the clean_up_thumbnails method works as expected when file is missing.
"""
# GIVEN: A mocked controller, and mocked os.path.exists
mocked_controller = MagicMock()
mocked_doc = MagicMock()
mocked_controller.add_document.return_value = mocked_doc
mocked_controller.supports = ['tmp']
self.media_item.controllers = {
'Mocked': mocked_controller
}
presentation_file = 'file.tmp'
with patch('openlp.plugins.presentations.lib.mediaitem.os.path.exists') as mocked_exists:
mocked_exists.return_value = False
# WHEN: calling clean_up_thumbnails
self.media_item.clean_up_thumbnails(presentation_file, True)
# THEN: doc.presentation_deleted should have been called since the presentation file did not exists.
mocked_doc.assert_has_calls([call.get_thumbnail_path(1, True), call.presentation_deleted()], True)