diff --git a/openlp/plugins/media/forms/mediaclipselectorform.py b/openlp/plugins/media/forms/mediaclipselectorform.py index 28d37f32e..d63e8a8bb 100644 --- a/openlp/plugins/media/forms/mediaclipselectorform.py +++ b/openlp/plugins/media/forms/mediaclipselectorform.py @@ -28,31 +28,29 @@ ############################################################################### import os -if os.name == 'nt': - from win32com.client import Dispatch - import string -import sys - -if sys.platform.startswith('linux'): - import dbus import logging import re from time import sleep from datetime import datetime - from PyQt4 import QtCore, QtGui -from openlp.core.common import translate +from openlp.core.common import translate, is_win, is_linux, is_macosx, RegistryProperties from openlp.plugins.media.forms.mediaclipselectordialog import Ui_MediaClipSelector from openlp.core.lib.ui import critical_error_message_box -from openlp.core.ui.media import format_milliseconds + +if is_win(): + from win32com.client import Dispatch + +if is_linux(): + import dbus + try: from openlp.core.ui.media.vendor import vlc except (ImportError, NameError, NotImplementedError): pass except OSError as e: - if sys.platform.startswith('win'): + if is_win(): if not isinstance(e, WindowsError) and e.winerror != 126: raise else: @@ -61,7 +59,7 @@ except OSError as e: log = logging.getLogger(__name__) -class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): +class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector, RegistryProperties): """ Class to manage the clip selection """ @@ -144,9 +142,9 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): # You have to give the id of the QFrame (or similar object) # to vlc, different platforms have different functions for this. win_id = int(self.preview_frame.winId()) - if sys.platform == "win32": + if is_win(): self.vlc_media_player.set_hwnd(win_id) - elif sys.platform == "darwin": + elif is_macosx(): # We have to use 'set_nsobject' since Qt4 on OSX uses Cocoa # framework and not the old Carbon. self.vlc_media_player.set_nsobject(win_id) @@ -190,7 +188,7 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): self.audio_cd = True self.titles_combo_box.setDisabled(False) self.titles_combo_box.setCurrentIndex(0) - self.on_title_combo_box_currentIndexChanged(0) + self.on_titles_combo_box_currentIndexChanged(0) return True @@ -203,18 +201,21 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): """ log.debug('on_load_disc_button_clicked') self.disable_all() + self.application.set_busy_cursor() path = self.media_path_combobox.currentText() # Check if given path is non-empty and exists before starting VLC if not path: log.debug('no given path') critical_error_message_box(message=translate('MediaPlugin.MediaClipSelectorForm', 'No path was given')) self.toggle_disable_load_media(False) + self.application.set_normal_cursor() return if not os.path.exists(path): log.debug('Given path does not exists') critical_error_message_box(message=translate('MediaPlugin.MediaClipSelectorForm', 'Given path does not exists')) self.toggle_disable_load_media(False) + self.application.set_normal_cursor() return # VLC behaves a bit differently on windows and linux when loading, which creates problems when trying to # detect if we're dealing with a DVD or CD, so we use different loading approaches depending on the OS. @@ -231,6 +232,7 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): critical_error_message_box(message=translate('MediaPlugin.MediaClipSelectorForm', 'An error happened during initialization of VLC player')) self.toggle_disable_load_media(False) + self.application.set_normal_cursor() return # put the media in the media player self.vlc_media_player.set_media(self.vlc_media) @@ -241,6 +243,8 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): critical_error_message_box(message=translate('MediaPlugin.MediaClipSelectorForm', 'VLC player failed playing the media')) self.toggle_disable_load_media(False) + self.application.set_normal_cursor() + self.vlc_media_player.audio_set_mute(False) return self.vlc_media_player.audio_set_mute(True) if not self.media_state_wait(vlc.State.Playing): @@ -249,23 +253,32 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): critical_error_message_box(message=translate('MediaPlugin.MediaClipSelectorForm', 'VLC player failed playing the media')) self.toggle_disable_load_media(False) + self.application.set_normal_cursor() + self.vlc_media_player.audio_set_mute(False) return - self.vlc_media_player.audio_set_mute(True) + # pause + self.vlc_media_player.set_time(0) + self.vlc_media_player.set_pause(1) + self.media_state_wait(vlc.State.Paused) + self.toggle_disable_load_media(False) + self.application.set_normal_cursor() + self.vlc_media_player.audio_set_mute(False) if not self.audio_cd: + # Temporarily disable signals + self.blockSignals(True) # Get titles, insert in combobox titles = self.vlc_media_player.video_get_title_description() self.titles_combo_box.clear() for title in titles: self.titles_combo_box.addItem(title[1].decode(), title[0]) + # Re-enable signals + self.blockSignals(False) # Main title is usually title #1 if len(titles) > 1: self.titles_combo_box.setCurrentIndex(1) - else: - self.titles_combo_box.setCurrentIndex(0) # Enable audio track combobox if anything is in it if len(titles) > 0: self.titles_combo_box.setDisabled(False) - self.toggle_disable_load_media(False) log.debug('load_disc_button end - vlc_media_player state: %s' % self.vlc_media_player.get_state()) @QtCore.pyqtSlot(bool) @@ -378,6 +391,7 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): if not self.vlc_media_player: log.error('vlc_media_player was None') return + self.application.set_busy_cursor() if self.audio_cd: self.vlc_media = self.audio_cd_tracks.item_at_index(index) self.vlc_media_player.set_media(self.vlc_media) @@ -385,14 +399,14 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): self.vlc_media_player.play() if not self.media_state_wait(vlc.State.Playing): log.error('Could not start playing audio cd, needed to get track info') + self.application.set_normal_cursor() return self.vlc_media_player.audio_set_mute(True) - # Sleep 1 second to make sure VLC has the needed metadata - sleep(1) # pause self.vlc_media_player.set_time(0) self.vlc_media_player.set_pause(1) self.vlc_media_player.audio_set_mute(False) + self.application.set_normal_cursor() self.toggle_disable_player(False) else: self.vlc_media_player.set_title(index) @@ -400,13 +414,13 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): self.vlc_media_player.play() if not self.media_state_wait(vlc.State.Playing): log.error('Could not start playing dvd, needed to get track info') + self.application.set_normal_cursor() return self.vlc_media_player.audio_set_mute(True) - # Sleep 1 second to make sure VLC has the needed metadata - sleep(1) - self.vlc_media_player.set_time(0) - # Get audio tracks, insert in combobox + # Get audio tracks audio_tracks = self.vlc_media_player.audio_get_track_description() + log.debug('number of audio tracks: %d' % len(audio_tracks)) + # Clear the audio track combobox, insert new tracks self.audio_tracks_combobox.clear() for audio_track in audio_tracks: self.audio_tracks_combobox.addItem(audio_track[1].decode(), audio_track[0]) @@ -447,6 +461,7 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): self.vlc_media_player.set_pause(1) loop_count += 1 log.debug('titles_combo_box end - vlc_media_player state: %s' % self.vlc_media_player.get_state()) + self.application.set_normal_cursor() @QtCore.pyqtSlot(int) def on_audio_tracks_combobox_currentIndexChanged(self, index): @@ -535,7 +550,7 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): """ Saves the current media and trackinfo as a clip to the mediamanager """ - log.debug('in on_save_button_clicked') + log.debug('in MediaClipSelectorForm.accept') start_time = self.start_position_edit.time() start_time_ms = start_time.hour() * 60 * 60 * 1000 + \ start_time.minute() * 60 * 1000 + \ @@ -550,10 +565,23 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): path = self.media_path_combobox.currentText() optical = '' if self.audio_cd: + # Check for load problems + if start_time_ms is None or end_time_ms is None or title is None: + critical_error_message_box(translate('MediaPlugin.MediaClipSelectorForm', 'CD not loaded correctly'), + translate('MediaPlugin.MediaClipSelectorForm', + 'The CD was not loaded correctly, please re-load and try again.')) + return optical = 'optical:%d:-1:-1:%d:%d:' % (title, start_time_ms, end_time_ms) else: audio_track = self.audio_tracks_combobox.itemData(self.audio_tracks_combobox.currentIndex()) subtitle_track = self.subtitle_tracks_combobox.itemData(self.subtitle_tracks_combobox.currentIndex()) + # Check for load problems + if start_time_ms is None or end_time_ms is None or title is None or audio_track is None\ + or subtitle_track is None: + critical_error_message_box(translate('MediaPlugin.MediaClipSelectorForm', 'DVD not loaded correctly'), + translate('MediaPlugin.MediaClipSelectorForm', + 'The DVD was not loaded correctly, please re-load and try again.')) + return optical = 'optical:%d:%d:%d:%d:%d:' % (title, audio_track, subtitle_track, start_time_ms, end_time_ms) # Ask for an alternative name for the mediaclip while True: @@ -595,7 +623,7 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): while media_state != self.vlc_media_player.get_state(): if self.vlc_media_player.get_state() == vlc.State.Error: return False - if (datetime.now() - start).seconds > 30: + if (datetime.now() - start).seconds > 15: return False return True @@ -606,7 +634,7 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): """ # Clear list first self.media_path_combobox.clear() - if os.name == 'nt': + if is_win(): # use win api to find optical drives fso = Dispatch('scripting.filesystemobject') for drive in fso.Drives: @@ -614,7 +642,7 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): # if type is 4, it is a cd-rom drive if drive.DriveType == 4: self.media_path_combobox.addItem('%s:\\' % drive.DriveLetter) - elif sys.platform.startswith('linux'): + elif is_linux(): # Get disc devices from dbus and find the ones that are optical bus = dbus.SystemBus() try: @@ -646,7 +674,7 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector): if chr(c) != '\x00': block_file += chr(c) self.media_path_combobox.addItem(block_file) - elif sys.platform.startswith('darwin'): + elif is_macosx(): # Look for DVD folders in devices to find optical devices volumes = os.listdir('/Volumes') candidates = list() diff --git a/tests/interfaces/openlp_plugins/media/__init__.py b/tests/interfaces/openlp_plugins/media/__init__.py index e69de29bb..6b241e7fc 100644 --- a/tests/interfaces/openlp_plugins/media/__init__.py +++ b/tests/interfaces/openlp_plugins/media/__init__.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# 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 # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### diff --git a/tests/interfaces/openlp_plugins/media/forms/__init__.py b/tests/interfaces/openlp_plugins/media/forms/__init__.py new file mode 100644 index 000000000..6b241e7fc --- /dev/null +++ b/tests/interfaces/openlp_plugins/media/forms/__init__.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# 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 # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### diff --git a/tests/interfaces/openlp_plugins/media/forms/test_mediaclipselectorform.py b/tests/interfaces/openlp_plugins/media/forms/test_mediaclipselectorform.py index e97a06238..89810aa50 100644 --- a/tests/interfaces/openlp_plugins/media/forms/test_mediaclipselectorform.py +++ b/tests/interfaces/openlp_plugins/media/forms/test_mediaclipselectorform.py @@ -60,6 +60,7 @@ class TestMediaClipSelectorForm(TestCase, TestMixin): # Mock VLC so we don't actually use it self.vlc_patcher = patch('openlp.plugins.media.forms.mediaclipselectorform.vlc') self.vlc_patcher.start() + Registry().register('application', self.app) # Mock the media item self.mock_media_item = MagicMock() # create form to test @@ -67,6 +68,9 @@ class TestMediaClipSelectorForm(TestCase, TestMixin): mock_media_state_wait = MagicMock() mock_media_state_wait.return_value = True self.form.media_state_wait = mock_media_state_wait + self.form.application.set_busy_cursor = MagicMock() + self.form.application.set_normal_cursor = MagicMock() + self.form.find_optical_devices = MagicMock() def tearDown(self): """ @@ -155,3 +159,21 @@ class TestMediaClipSelectorForm(TestCase, TestMixin): self.form.audio_tracks_combobox.itemData.assert_any_call(0) self.form.audio_tracks_combobox.itemData.assert_any_call(1) self.form.subtitle_tracks_combobox.itemData.assert_any_call(0) + + def click_save_button_test(self): + """ + Test that the correct function is called when save is clicked, and that it behaves as expected. + """ + # GIVEN: Mocked methods. + with patch('openlp.plugins.media.forms.mediaclipselectorform.critical_error_message_box') as \ + mocked_critical_error_message_box,\ + patch('PyQt4.QtGui.QDialog.exec_') as mocked_exec: + self.form.exec_() + + # WHEN: The save button is clicked with a NoneType in start_time_ms or end_time_ms + self.form.accept() + + # THEN: we should get an error message + mocked_critical_error_message_box.assert_called_with('DVD not loaded correctly', + 'The DVD was not loaded correctly, ' + 'please re-load and try again.')