From d74f2c05ffe3cb3d47ab20c45d7a4b8d8bff0726 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Sun, 15 Mar 2015 21:53:46 +0000 Subject: [PATCH 1/7] Fix crash if presentation file in the mediamanger was removed bewteen sessions. Fixes bug 1432418. Fixes: https://launchpad.net/bugs/1432418 --- openlp/plugins/presentations/lib/mediaitem.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index 00a529b63..fa6b3d377 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -245,7 +245,8 @@ class PresentationMediaItem(MediaManagerItem): doc = self.controllers[cidx].add_document(filepath) if clean_for_update: 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 (os.path.exists(filepath) + and os.path.getmtime(thumb_path) < os.path.getmtime(filepath)): doc.presentation_deleted() else: doc.presentation_deleted() From e34179795bed71ca2a33c69cacc5e1fdb9b1479b Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Sun, 15 Mar 2015 22:34:12 +0000 Subject: [PATCH 2/7] Attempt to fix a webkit player exception --- openlp/core/ui/media/webkitplayer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/media/webkitplayer.py b/openlp/core/ui/media/webkitplayer.py index 338a1eae5..bff9cf940 100644 --- a/openlp/core/ui/media/webkitplayer.py +++ b/openlp/core/ui/media/webkitplayer.py @@ -375,7 +375,7 @@ class WebkitPlayer(MediaPlayer): # check if conversion was ok and value is not 'NaN' if length and length != float('inf'): length = int(length * 1000) - if current_time: + if current_time and length: controller.media_info.length = length controller.seek_slider.setMaximum(length) if not controller.seek_slider.isSliderDown(): From c3d21ca742f1b5c5c2124a089fe8343f174a3d08 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Mon, 16 Mar 2015 21:32:56 +0000 Subject: [PATCH 3/7] Disable the stop button in the slidecontroller mediaplayer buttons instead of hiding it to stop the position and audio slider resizing. Fixes bug 1431478. --- openlp/core/ui/media/mediacontroller.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py index f91c46a7d..494bd54c3 100644 --- a/openlp/core/ui/media/mediacontroller.py +++ b/openlp/core/ui/media/mediacontroller.py @@ -589,7 +589,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties): else: controller.mediabar.actions['playbackPlay'].setVisible(False) controller.mediabar.actions['playbackPause'].setVisible(True) - controller.mediabar.actions['playbackStop'].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() @@ -619,7 +619,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties): display = self._define_display(controller) self.current_media_players[controller.controller_type].pause(display) controller.mediabar.actions['playbackPlay'].setVisible(True) - controller.mediabar.actions['playbackStop'].setVisible(True) + controller.mediabar.actions['playbackStop'].setDisabled(False) controller.mediabar.actions['playbackPause'].setVisible(False) 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) controller.seek_slider.setSliderPosition(0) controller.mediabar.actions['playbackPlay'].setVisible(True) - controller.mediabar.actions['playbackStop'].setVisible(False) + controller.mediabar.actions['playbackStop'].setDisabled(True) controller.mediabar.actions['playbackPause'].setVisible(False) def media_volume_msg(self, msg): From 27332f87e1a4c3c325ba19a4f5009fdff41312ae Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Tue, 17 Mar 2015 22:06:21 +0000 Subject: [PATCH 4/7] Run XInitThreads when using VLC to make it work correctly. Fixes bug 1433245. Fixes: https://launchpad.net/bugs/1433245 --- openlp/core/ui/media/vlcplayer.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/openlp/core/ui/media/vlcplayer.py b/openlp/core/ui/media/vlcplayer.py index 46057fcf9..f7f7589ea 100644 --- a/openlp/core/ui/media/vlcplayer.py +++ b/openlp/core/ui/media/vlcplayer.py @@ -30,7 +30,7 @@ import threading 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.ui.media import MediaState, MediaType from openlp.core.ui.media.mediaplayer import MediaPlayer @@ -62,6 +62,13 @@ if VLC_AVAILABLE: if LooseVersion(VERSION.split()[0]) < LooseVersion('1.1.0'): VLC_AVAILABLE = False log.debug('VLC could not be loaded, because the vlc version is too old: %s' % VERSION) + if is_linux(): + import ctypes + try: + x11 = ctypes.cdll.LoadLibrary('libX11.so') + x11.XInitThreads() + except: + log.exception('Failed to XInitThreads(), VLC might not work properly!') AUDIO_EXT = ['*.mp3', '*.wav', '*.wma', '*.ogg'] From b3fd23da59427b5896040317d018e8d3bc17fa1c Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Wed, 18 Mar 2015 20:44:35 +0000 Subject: [PATCH 5/7] Updated the list of extensions supported by VLC. --- openlp/core/ui/media/vlcplayer.py | 46 +++++++++++-------------------- 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/openlp/core/ui/media/vlcplayer.py b/openlp/core/ui/media/vlcplayer.py index f7f7589ea..9932d3901 100644 --- a/openlp/core/ui/media/vlcplayer.py +++ b/openlp/core/ui/media/vlcplayer.py @@ -62,43 +62,29 @@ if VLC_AVAILABLE: if LooseVersion(VERSION.split()[0]) < LooseVersion('1.1.0'): VLC_AVAILABLE = False log.debug('VLC could not be loaded, because the vlc version is too old: %s' % VERSION) - if is_linux(): + if VLC_AVAILABLE and is_linux(): import ctypes try: x11 = ctypes.cdll.LoadLibrary('libX11.so') x11.XInitThreads() except: - log.exception('Failed to XInitThreads(), VLC might not work properly!') + 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 = [ - '*.3gp', - '*.asf', '*.wmv', - '*.au', - '*.avi', - '*.divx', - '*.flv', - '*.mov', - '*.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' -] +VIDEO_EXT = ['*.3g2', '*.3gp', '*.3gp2', '*.3gpp', '*.amv', '*.asf', '*.avi', '*.bik', '*.divx', '*.drc', '*.dv', + '*.f4v', '*.flv', '*.gvi', '*.gxf', '*.iso', '*.m1v', '*.m2v', '*.m2t', '*.m2ts', '*.m4v', '*.mkv', + '*.mov', '*.mp2', '*.mp2v', '*.mp4', '*.mp4v', '*.mpe', '*.mpeg', '*.mpeg1', '*.mpeg2', '*.mpeg4', '*.mpg', + '*.mpv2', '*.mts', '*.mtv', '*.mxf', '*.mxg', '*.nsv', '*.nuv', '*.ogg', '*.ogm', '*.ogv', '*.ogx', '*.ps', + '*.rec', '*.rm', '*.rmvb', '*.rpl', '*.thp', '*.tod', '*.ts', '*.tts', '*.txd', '*.vob', '*.vro', '*.webm', + '*.wm', '*.wmv', '*.wtv', '*.xesc', + # These extensions was not in the official list, added manually. + '*.nut', '*.rv', '*.xvid'] class VlcPlayer(MediaPlayer): From 7e406e700570d019aeff1907b1c8d2c05688af2b Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Wed, 18 Mar 2015 22:02:51 +0000 Subject: [PATCH 6/7] Insert timeout on acquiring lock in slidecontroller to avoid hang. Fixes bug 1413324. Fixes: https://launchpad.net/bugs/1413324 --- openlp/core/ui/slidecontroller.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 645940fe6..22fd0f4b3 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -1069,8 +1069,13 @@ class SlideController(DisplayController, RegistryProperties): :param start: """ # 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. - if not self.slide_selected_lock.acquire(start): + # done by the thread holding the lock. If it is a "start" slide, we must wait for the lock, but only for 0.2 + # 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 row = self.preview_widget.current_slide_number() self.selected_row = 0 From f8084059ba6429db1a5f7af618adc2626a9737c1 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Wed, 18 Mar 2015 22:04:30 +0000 Subject: [PATCH 7/7] Added test + small fixes. --- openlp/core/ui/media/vlcplayer.py | 4 ++- openlp/plugins/presentations/lib/mediaitem.py | 4 +-- .../presentations/test_mediaitem.py | 28 +++++++++++++++++-- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/openlp/core/ui/media/vlcplayer.py b/openlp/core/ui/media/vlcplayer.py index 9932d3901..535f23e8b 100644 --- a/openlp/core/ui/media/vlcplayer.py +++ b/openlp/core/ui/media/vlcplayer.py @@ -27,6 +27,7 @@ from distutils.version import LooseVersion import logging import os import threading +import sys from PyQt4 import QtGui @@ -62,7 +63,8 @@ if VLC_AVAILABLE: if LooseVersion(VERSION.split()[0]) < LooseVersion('1.1.0'): VLC_AVAILABLE = False log.debug('VLC could not be loaded, because the vlc version is too old: %s' % VERSION) - if VLC_AVAILABLE and is_linux(): + # 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') diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index fa6b3d377..63fd20ba5 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -245,8 +245,8 @@ class PresentationMediaItem(MediaManagerItem): doc = self.controllers[cidx].add_document(filepath) if clean_for_update: thumb_path = doc.get_thumbnail_path(1, True) - if not thumb_path or (os.path.exists(filepath) - and 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() else: doc.presentation_deleted() diff --git a/tests/functional/openlp_plugins/presentations/test_mediaitem.py b/tests/functional/openlp_plugins/presentations/test_mediaitem.py index 8de65e0fe..72584e571 100644 --- a/tests/functional/openlp_plugins/presentations/test_mediaitem.py +++ b/tests/functional/openlp_plugins/presentations/test_mediaitem.py @@ -87,7 +87,7 @@ class TestMediaItem(TestCase, TestMixin): 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 mocked_controller = MagicMock() @@ -98,8 +98,10 @@ class TestMediaItem(TestCase, TestMixin): 'Mocked': mocked_controller } 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_exists.return_value = True # WHEN: calling clean_up_thumbnails 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 # the presentation_file's mtime. 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)