diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 45d6f3097..0e94be368 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -37,6 +37,14 @@ from PyQt4 import QtCore, QtGui, Qt log = logging.getLogger(__name__) +class ServiceItemContext(object): + """ + The context in which a Service Item is being generated + """ + Preview = 0 + Live = 1 + Service = 2 + class ImageSource(object): """ @@ -395,4 +403,4 @@ from dockwidget import OpenLPDockWidget from imagemanager import ImageManager from renderer import Renderer from mediamanageritem import MediaManagerItem -from openlp.core.utils.actions import ActionList + diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 46d3f5a0f..9b06b0ac5 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -36,7 +36,8 @@ import re from PyQt4 import QtCore, QtGui from openlp.core.lib import SettingsManager, OpenLPToolbar, ServiceItem, \ - StringContent, build_icon, translate, Receiver, ListWidgetWithDnD + StringContent, build_icon, translate, Receiver, ListWidgetWithDnD, \ + ServiceItemContext from openlp.core.lib.searchedit import SearchEdit from openlp.core.lib.ui import UiStrings, create_widget_action, \ critical_error_message_box @@ -459,7 +460,7 @@ class MediaManagerItem(QtGui.QWidget): pass def generateSlideData(self, serviceItem, item=None, xmlVersion=False, - remote=False): + remote=False, context=ServiceItemContext.Live): raise NotImplementedError(u'MediaManagerItem.generateSlideData needs ' u'to be defined by the plugin') @@ -521,6 +522,8 @@ class MediaManagerItem(QtGui.QWidget): if serviceItem: if not item_id: serviceItem.from_plugin = True + if remote: + serviceItem.will_auto_start = True self.plugin.liveController.addServiceItem(serviceItem) def createItemFromId(self, item_id): @@ -548,7 +551,8 @@ class MediaManagerItem(QtGui.QWidget): self.addToService(item) def addToService(self, item=None, replace=None, remote=False): - serviceItem = self.buildServiceItem(item, True, remote=remote) + serviceItem = self.buildServiceItem(item, True, remote=remote, + context=ServiceItemContext.Service) if serviceItem: serviceItem.from_plugin = False self.plugin.serviceManager.addServiceItem(serviceItem, @@ -581,13 +585,15 @@ class MediaManagerItem(QtGui.QWidget): unicode(translate('OpenLP.MediaManagerItem', 'You must select a %s service item.')) % self.title) - def buildServiceItem(self, item=None, xmlVersion=False, remote=False): + def buildServiceItem(self, item=None, xmlVersion=False, remote=False, + context=ServiceItemContext.Live): """ Common method for generating a service item """ serviceItem = ServiceItem(self.plugin) serviceItem.add_icon(self.plugin.iconPath) - if self.generateSlideData(serviceItem, item, xmlVersion, remote): + if self.generateSlideData(serviceItem, item, xmlVersion, remote, + context): return serviceItem else: return None diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index 850346aaf..2b0bfa582 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -53,7 +53,65 @@ class ServiceItemType(object): class ItemCapabilities(object): """ - Provides an enumeration of a serviceitem's capabilities + Provides an enumeration of a service item's capabilities + + ``CanPreview`` + The capability to allow the ServiceManager to add to the preview + tab when making the previous item live. + + ``CanEdit`` + The capability to allow the ServiceManager to allow the item to be + edited + + ``CanMaintain`` + The capability to allow the ServiceManager to allow the item to be + reordered. + + ``RequiresMedia`` + Determines is the serviceItem needs a Media Player + + ``CanLoop`` + The capability to allow the SlideController to allow the loop + processing. + + ``CanAppend`` + The capability to allow the ServiceManager to add leaves to the + item + + ``NoLineBreaks`` + The capability to remove lines breaks in the renderer + + ``OnLoadUpdate`` + The capability to update MediaManager when a service Item is + loaded. + + ``AddIfNewItem`` + Not Used + + ``ProvidesOwnDisplay`` + The capability to tell the SlideController the service Item has a + different display. + + ``HasDetailedTitleDisplay`` + ServiceItem provides a title + + ``HasVariableStartTime`` + The capability to tell the ServiceManager that a change to start + time is possible. + + ``CanSoftBreak`` + The capability to tell the renderer that Soft Break is allowed + + ``CanWordSplit`` + The capability to tell the renderer that it can split words is + allowed + + ``HasBackgroundAudio`` + That a audio file is present with the text. + + ``CanAutoStartForLive`` + The capability to ignore the do not play if display blank flag. + """ CanPreview = 1 CanEdit = 2 @@ -70,6 +128,7 @@ class ItemCapabilities(object): CanSoftBreak = 13 CanWordSplit = 14 HasBackgroundAudio = 15 + CanAutoStartForLive = 16 class ServiceItem(object): @@ -123,6 +182,7 @@ class ServiceItem(object): self.background_audio = [] self.theme_overwritten = False self.temporary_edit = False + self.will_auto_start = False self._new_item() def _new_item(self): @@ -267,7 +327,7 @@ class ServiceItem(object): {u'title': file_name, u'image': image, u'path': path}) self._new_item() - def get_service_repr(self): + def get_service_repr(self, lite_save): """ This method returns some text which can be saved into the service file to represent this item. @@ -291,17 +351,24 @@ class ServiceItem(object): u'end_time': self.end_time, u'media_length': self.media_length, u'background_audio': self.background_audio, - u'theme_overwritten': self.theme_overwritten + u'theme_overwritten': self.theme_overwritten, + u'will_auto_start': self.will_auto_start } service_data = [] if self.service_item_type == ServiceItemType.Text: service_data = [slide for slide in self._raw_frames] elif self.service_item_type == ServiceItemType.Image: - service_data = [slide[u'title'] for slide in self._raw_frames] + if lite_save: + for slide in self._raw_frames: + service_data.append( + {u'title': slide[u'title'], u'path': slide[u'path']}) + else: + service_data = [slide[u'title'] for slide in self._raw_frames] elif self.service_item_type == ServiceItemType.Command: for slide in self._raw_frames: service_data.append( - {u'title': slide[u'title'], u'image': slide[u'image']}) + {u'title': slide[u'title'], u'image': slide[u'image'], + u'path': slide[u'path']}) return {u'header': service_header, u'data': service_data} def set_from_service(self, serviceitem, path=None): @@ -313,7 +380,9 @@ class ServiceItem(object): The item to extract data from. ``path`` - Defaults to *None*. Any path data, usually for images. + Defaults to *None*. This is the service manager path for things + which have their files saved with them or None when the saved + service is lite and the original file paths need to be preserved.. """ log.debug(u'set_from_service called with path %s' % path) header = serviceitem[u'serviceitem'][u'header'] @@ -335,6 +404,7 @@ class ServiceItem(object): self.start_time = header.get(u'start_time', 0) self.end_time = header.get(u'end_time', 0) self.media_length = header.get(u'media_length', 0) + self.will_auto_start = header.get(u'will_auto_start', False) if u'background_audio' in header: self.background_audio = [] for filename in header[u'background_audio']: @@ -345,14 +415,24 @@ class ServiceItem(object): for slide in serviceitem[u'serviceitem'][u'data']: self._raw_frames.append(slide) elif self.service_item_type == ServiceItemType.Image: - for text_image in serviceitem[u'serviceitem'][u'data']: - filename = os.path.join(path, text_image) - self.add_from_image(filename, text_image) + if path: + for text_image in serviceitem[u'serviceitem'][u'data']: + filename = os.path.join(path, text_image) + self.add_from_image(filename, text_image) + else: + for text_image in serviceitem[u'serviceitem'][u'data']: + self.add_from_image(text_image[u'path'], + text_image[u'title']) elif self.service_item_type == ServiceItemType.Command: for text_image in serviceitem[u'serviceitem'][u'data']: - filename = os.path.join(path, text_image[u'title']) - self.add_from_command( - path, text_image[u'title'], text_image[u'image']) + if path: + self.add_from_command( + path, text_image[u'title'], text_image[u'image']) + else: + self.add_from_command( + text_image[u'path'], text_image[u'title'], + text_image[u'image']) + self._new_item() def get_display_title(self): @@ -434,6 +514,17 @@ class ServiceItem(object): """ return self.service_item_type == ServiceItemType.Text + def set_media_length(self, length): + """ + Stores the media length of the item + + ``length`` + The length of the media item + """ + self.media_length = length + if length > 0: + self.add_capability(ItemCapabilities.HasVariableStartTime) + def get_frames(self): """ Returns the frames for the ServiceItem @@ -446,6 +537,8 @@ class ServiceItem(object): def get_rendered_frame(self, row): """ Returns the correct frame for a given list and renders it if required. + ``row`` + The service item slide to be returned """ if self.service_item_type == ServiceItemType.Text: return self._display_frames[row][u'html'].split(u'\n')[0] diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 943c4a78e..eb099d92c 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -61,6 +61,8 @@ class UiStrings(object): self.Add = translate('OpenLP.Ui', '&Add') self.Advanced = translate('OpenLP.Ui', 'Advanced') self.AllFiles = translate('OpenLP.Ui', 'All Files') + self.Automatic = translate('OpenLP.Ui', 'Automatic') + self.BackgroundColor = translate('OpenLP.Ui', 'Background Color') self.Bottom = translate('OpenLP.Ui', 'Bottom') self.Browse = translate('OpenLP.Ui', 'Browse...') self.Cancel = translate('OpenLP.Ui', 'Cancel') @@ -69,6 +71,7 @@ class UiStrings(object): self.ConfirmDelete = translate('OpenLP.Ui', 'Confirm Delete') self.Continuous = translate('OpenLP.Ui', 'Continuous') self.Default = unicode(translate('OpenLP.Ui', 'Default')) + self.DefaultColor = translate('OpenLP.Ui', 'Default Color:') self.Delete = translate('OpenLP.Ui', '&Delete') self.DisplayStyle = translate('OpenLP.Ui', 'Display style:') self.Duplicate = translate('OpenLP.Ui', 'Duplicate Error') @@ -106,7 +109,8 @@ class UiStrings(object): self.NISs = translate('OpenLP.Ui', 'No Item Selected', 'Singular') self.NISp = translate('OpenLP.Ui', 'No Items Selected', 'Plural') self.OLPV1 = translate('OpenLP.Ui', 'openlp.org 1.x') - self.OLPV2 = translate('OpenLP.Ui', 'OpenLP 2.0') + self.OLPV2 = translate('OpenLP.Ui', 'OpenLP 2') + self.OLPV2x = translate('OpenLP.Ui', 'OpenLP 2.1') self.OpenLPStart = translate('OpenLP.Ui', 'OpenLP is already running. ' 'Do you wish to continue?') self.OpenService = translate('OpenLP.Ui', 'Open service.') diff --git a/openlp/core/ui/__init__.py b/openlp/core/ui/__init__.py index e34a0ec5c..325b1d450 100644 --- a/openlp/core/ui/__init__.py +++ b/openlp/core/ui/__init__.py @@ -72,6 +72,16 @@ class AlertLocation(object): Middle = 1 Bottom = 2 +class DisplayControllerType(object): + """ + This is an enumeration class which says where a display controller + originated from. + """ + Live = 0 + Preview = 1 + Plugin = 2 + + from firsttimeform import FirstTimeForm from firsttimelanguageform import FirstTimeLanguageForm from themelayoutform import ThemeLayoutForm @@ -82,7 +92,7 @@ from screen import ScreenList from maindisplay import MainDisplay, Display from servicenoteform import ServiceNoteForm from serviceitemeditform import ServiceItemEditForm -from slidecontroller import SlideController, Controller +from slidecontroller import SlideController, DisplayController from splashscreen import SplashScreen from generaltab import GeneralTab from themestab import ThemesTab @@ -98,4 +108,4 @@ from thememanager import ThemeManager __all__ = ['SplashScreen', 'AboutForm', 'SettingsForm', 'MainDisplay', 'SlideController', 'ServiceManager', 'ThemeManager', 'MediaDockManager', - 'ServiceItemEditForm', u'FirstTimeForm'] + 'ServiceItemEditForm', 'FirstTimeForm'] diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index 64411ac70..16ee19161 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -53,8 +53,8 @@ log = logging.getLogger(__name__) class Display(QtGui.QGraphicsView): """ This is a general display screen class. Here the general display settings - will done. It will be used as specialized classes by Main Display and - Preview display. + will done. It will be used as specialized classes by Main Display and + Preview display. """ def __init__(self, parent, live, controller): if live: @@ -327,7 +327,7 @@ class MainDisplay(Display): """ log.debug(u'image to display') image = self.imageManager.getImageBytes(path, ImageSource.ImagePlugin) - self.controller.mediaController.video_reset(self.controller) + self.controller.mediaController.media_reset(self.controller) self.displayImage(image) def displayImage(self, image): diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 8c36de7bf..23e467f1a 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -395,7 +395,7 @@ class Ui_MainWindow(object): """ Set up the translation system """ - mainWindow.mainTitle = UiStrings().OLPV2 + mainWindow.mainTitle = UiStrings().OLPV2x mainWindow.setWindowTitle(mainWindow.mainTitle) self.fileMenu.setTitle(translate('OpenLP.MainWindow', '&File')) self.fileImportMenu.setTitle(translate('OpenLP.MainWindow', '&Import')) @@ -576,6 +576,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): self.headerSection = u'SettingsImport' self.serviceNotSaved = False self.aboutForm = AboutForm(self) + self.mediaController = MediaController(self) self.settingsForm = SettingsForm(self, self) self.formattingTagForm = FormattingTagForm(self) self.shortcutForm = ShortcutListForm(self) @@ -585,9 +586,10 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): self.pluginManager = PluginManager(plugin_path) self.pluginHelpers = {} self.imageManager = ImageManager() - self.mediaController = MediaController(self) # Set up the interface self.setupUi(self) + # Register the active media players and suffixes + self.mediaController.check_available_media_players() # Load settings after setupUi so default UI sizes are overwritten self.loadSettings() # Once settings are loaded update the menu with the recent files. @@ -1169,7 +1171,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): if self.serviceManagerContents.isModified(): ret = self.serviceManagerContents.saveModifiedService() if ret == QtGui.QMessageBox.Save: - if self.serviceManagerContents.saveFile(): + if self.serviceManagerContents.decideSaveMethod(): self.cleanUp() event.accept() else: diff --git a/openlp/core/ui/media/__init__.py b/openlp/core/ui/media/__init__.py index 11e94bc6f..1de494cf0 100644 --- a/openlp/core/ui/media/__init__.py +++ b/openlp/core/ui/media/__init__.py @@ -47,7 +47,7 @@ class MediaState(object): class MediaType(object): """ - An enumeration of possibible Media Types + An enumeration of possible Media Types """ Unused = 0 Audio = 1 @@ -59,7 +59,7 @@ class MediaType(object): class MediaInfo(object): """ - This class hold the media related infos + This class hold the media related info """ file_info = None volume = 100 @@ -72,30 +72,26 @@ class MediaInfo(object): def get_media_players(): """ - This method extract the configured media players and overridden player from - the settings. - - ``players_list`` - A list with all active media players. - - ``overridden_player`` - Here an special media player is chosen for all media actions. + This method extracts the configured media players and overridden player + from the settings. """ log.debug(u'get_media_players') - players = unicode(Settings().value(u'media/players').toString()) - if not players: - players = u'webkit' + saved_players = unicode(Settings().value(u'media/players').toString()) + if not saved_players: + # we must always have a player and Webkit is the core one. + saved_players = u'webkit' reg_ex = QtCore.QRegExp(".*\[(.*)\].*") if Settings().value(u'media/override player', QtCore.QVariant(QtCore.Qt.Unchecked)).toInt()[0] == QtCore.Qt.Checked: - if reg_ex.exactMatch(players): + if reg_ex.exactMatch(saved_players): overridden_player = u'%s' % reg_ex.cap(1) else: overridden_player = u'auto' else: overridden_player = u'' - players_list = players.replace(u'[', u'').replace(u']', u'').split(u',') - return players_list, overridden_player + saved_players_list = saved_players.replace(u'[', u'').\ + replace(u']',u'').split(u',') + return saved_players_list, overridden_player def set_media_players(players_list, overridden_player=u'auto'): @@ -118,3 +114,4 @@ def set_media_players(players_list, overridden_player=u'auto'): Settings().setValue(u'media/players', QtCore.QVariant(players)) from mediacontroller import MediaController +from playertab import PlayerTab diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py index 05498278b..fc2b8635a 100644 --- a/openlp/core/ui/media/mediacontroller.py +++ b/openlp/core/ui/media/mediacontroller.py @@ -34,11 +34,12 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import OpenLPToolbar, Receiver, translate from openlp.core.lib.settings import Settings -from openlp.core.lib.mediaplayer import MediaPlayer -from openlp.core.lib.ui import critical_error_message_box +from openlp.core.lib.ui import UiStrings, critical_error_message_box from openlp.core.ui.media import MediaState, MediaInfo, MediaType, \ get_media_players, set_media_players +from openlp.core.ui.media.mediaplayer import MediaPlayer from openlp.core.utils import AppLocation +from openlp.core.ui import DisplayControllerType log = logging.getLogger(__name__) @@ -46,68 +47,104 @@ class MediaController(object): """ The implementation of the Media Controller. The Media Controller adds an own class for every Player. Currently these are QtWebkit, Phonon and Vlc. - """ + displayControllers are an array of controllers keyed on the + slidecontroller or plugin which built them. ControllerType is the class + containing the key values. + + mediaPlayers are an array of media players keyed on player name. + + currentMediaPlayer is an array of player instances keyed on ControllerType. + + """ def __init__(self, parent): - self.parent = parent + self.mainWindow = parent self.mediaPlayers = {} - self.controller = [] - self.curDisplayMediaPlayer = {} + self.displayControllers = {} + self.currentMediaPlayer = {} # Timer for video state self.timer = QtCore.QTimer() self.timer.setInterval(200) - self.withLivePreview = False - self.check_available_media_players() # Signals QtCore.QObject.connect(self.timer, - QtCore.SIGNAL("timeout()"), self.video_state) + QtCore.SIGNAL("timeout()"), self.media_state) QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'playbackPlay'), self.video_play) + QtCore.SIGNAL(u'playbackPlay'), self.media_play_msg) QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'playbackPause'), self.video_pause) + QtCore.SIGNAL(u'playbackPause'), self.media_pause_msg) QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'playbackStop'), self.video_stop) + QtCore.SIGNAL(u'playbackStop'), self.media_stop_msg) QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'seekSlider'), self.video_seek) + QtCore.SIGNAL(u'seekSlider'), self.media_seek) QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'volumeSlider'), self.video_volume) + QtCore.SIGNAL(u'volumeSlider'), self.media_volume) QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'media_hide'), self.video_hide) + QtCore.SIGNAL(u'media_hide'), self.media_hide) QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'media_blank'), self.video_blank) + QtCore.SIGNAL(u'media_blank'), self.media_blank) QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'media_unblank'), self.video_unblank) + QtCore.SIGNAL(u'media_unblank'), self.media_unblank) # Signals for background video QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'songs_hide'), self.video_hide) + QtCore.SIGNAL(u'songs_hide'), self.media_hide) QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'songs_unblank'), self.video_unblank) + QtCore.SIGNAL(u'songs_unblank'), self.media_unblank) QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'mediaitem_media_rebuild'), self.set_active_players) + QtCore.SIGNAL(u'mediaitem_media_rebuild'), self._set_active_players) + QtCore.QObject.connect(Receiver.get_receiver(), + QtCore.SIGNAL(u'mediaitem_suffixes'), + self._generate_extensions_lists) - def set_active_players(self): + def _set_active_players(self): + """ + Set the active players and available media files + """ savedPlayers = get_media_players()[0] for player in self.mediaPlayers.keys(): self.mediaPlayers[player].isActive = player in savedPlayers - def register_controllers(self, controller): + def _generate_extensions_lists(self): """ - Register each media Player controller (Webkit, Phonon, etc) and store + Set the active players and available media files + """ + self.audio_extensions_list = [] + for player in self.mediaPlayers.values(): + if player.isActive: + for item in player.audio_extensions_list: + if not item in self.audio_extensions_list: + self.audio_extensions_list.append(item) + self.mainWindow.serviceManagerContents. \ + supportedSuffixes(item[2:]) + self.video_extensions_list = [] + for player in self.mediaPlayers.values(): + if player.isActive: + for item in player.video_extensions_list: + if item not in self.video_extensions_list: + self.video_extensions_list.extend(item) + self.mainWindow.serviceManagerContents. \ + supportedSuffixes(item[2:]) + + def register_players(self, player): + """ + Register each media Player (Webkit, Phonon, etc) and store for later use + + ``player`` + Individual player class which has been enabled """ - self.mediaPlayers[controller.name] = controller + self.mediaPlayers[player.name] = player def check_available_media_players(self): """ - Check to see if we have any media Player's available. If Not do not - install the plugin. + Check to see if we have any media Player's available. """ - log.debug(u'check_available_media_players') + log.debug(u'_check_available_media_players') controller_dir = os.path.join( AppLocation.get_directory(AppLocation.AppDir), u'core', u'ui', u'media') for filename in os.listdir(controller_dir): - if filename.endswith(u'player.py'): + if filename.endswith(u'player.py') and \ + not filename == 'mediaplayer.py': path = os.path.join(controller_dir, filename) if os.path.isfile(path): modulename = u'openlp.core.ui.media.' + \ @@ -120,10 +157,10 @@ class MediaController(object): except (ImportError, OSError): log.warn(u'Failed to import %s on path %s', modulename, path) - controller_classes = MediaPlayer.__subclasses__() - for controller_class in controller_classes: - controller = controller_class(self) - self.register_controllers(controller) + player_classes = MediaPlayer.__subclasses__() + for player_class in player_classes: + player = player_class(self) + self.register_players(player) if not self.mediaPlayers: return False savedPlayers, overriddenPlayer = get_media_players() @@ -134,31 +171,33 @@ class MediaController(object): for invalidPlayer in invalidMediaPlayers: savedPlayers.remove(invalidPlayer) set_media_players(savedPlayers, overriddenPlayer) - self.set_active_players() + self._set_active_players() + self._generate_extensions_lists() return True - def video_state(self): + def media_state(self): """ Check if there is a running media Player and do updating stuff (e.g. update the UI) """ - if not self.curDisplayMediaPlayer.keys(): + if not self.currentMediaPlayer.keys(): self.timer.stop() else: any_active = False - for display in self.curDisplayMediaPlayer.keys(): - self.curDisplayMediaPlayer[display].resize(display) - self.curDisplayMediaPlayer[display].update_ui(display) - if self.curDisplayMediaPlayer[display].state == \ - MediaState.Playing: + for source in self.currentMediaPlayer.keys(): + display = self._define_display(self.displayControllers[source]) + self.currentMediaPlayer[source].resize(display) + self.currentMediaPlayer[source].update_ui(display) + if self.currentMediaPlayer[source].state == \ + MediaState.Playing: any_active = True - # There are still any active players - no need to stop timer. + # There are still any active players - no need to stop timer. if any_active: return - - # No players are active anymore. - for display in self.curDisplayMediaPlayer.keys(): - if self.curDisplayMediaPlayer[display].state != MediaState.Paused: + # no players are active anymore + for source in self.currentMediaPlayer.keys(): + if self.currentMediaPlayer[source].state != MediaState.Paused: + display = self._define_display(self.displayControllers[source]) display.controller.seekSlider.setSliderPosition(0) self.timer.stop() @@ -192,14 +231,22 @@ class MediaController(object): html += player.get_media_display_html() return html - def add_controller_items(self, controller, control_panel): - self.controller.append(controller) - self.setup_generic_controls(controller, control_panel) - self.setup_special_controls(controller, control_panel) - - def setup_generic_controls(self, controller, control_panel): + def register_controller(self, controller): """ - Add generic media control items (valid for all types of medias) + Registers media controls where the players will be placed to run. + + ``controller`` + The controller where a player will be placed + """ + self.displayControllers[controller.controllerType] = controller + self.setup_generic_controls(controller) + + def setup_generic_controls(self, controller): + """ + Set up controls on the control_panel for a given controller + + ``controller`` + First element is the controller which should be used """ controller.media_info = MediaInfo() # Build a Media ToolBar @@ -212,7 +259,8 @@ class MediaController(object): controller.mediabar.addToolbarAction(u'playbackPause', text=u'media_playback_pause', icon=u':/slides/media_playback_pause.png', - tooltip=translate('OpenLP.SlideController', 'Pause playing media.'), + tooltip=translate('OpenLP.SlideController', + 'Pause playing media.'), triggers=controller.sendToPlugins) controller.mediabar.addToolbarAction(u'playbackStop', text=u'media_playback_stop', @@ -241,7 +289,7 @@ class MediaController(object): controller.volumeSlider.setGeometry(QtCore.QRect(90, 160, 221, 24)) controller.volumeSlider.setObjectName(u'volumeSlider') controller.mediabar.addToolbarWidget(controller.volumeSlider) - control_panel.addWidget(controller.mediabar) + controller.controllerLayout.addWidget(controller.mediabar) controller.mediabar.setVisible(False) # Signals QtCore.QObject.connect(controller.seekSlider, @@ -249,75 +297,96 @@ class MediaController(object): QtCore.QObject.connect(controller.volumeSlider, QtCore.SIGNAL(u'valueChanged(int)'), controller.sendToPlugins) - def setup_special_controls(self, controller, control_panel): - """ - Special media Toolbars will be created here (e.g. for DVD Playback) - """ - controller.media_info = MediaInfo() - # TODO: add Toolbar for DVD, ... - def setup_display(self, display): + def setup_display(self, display, preview): """ After a new display is configured, all media related widget will be created too + + ``display`` + Display on which the output is to be played + + ``preview`` + Whether the display is a main or preview display """ # clean up possible running old media files self.finalise() # update player status - self.set_active_players() + self._set_active_players() display.hasAudio = True - if not self.withLivePreview and \ - display == self.parent.liveController.previewDisplay: + if display.isLive and preview: return - if display == self.parent.previewController.previewDisplay or \ - display == self.parent.liveController.previewDisplay: + if preview: display.hasAudio = False for player in self.mediaPlayers.values(): if player.isActive: player.setup(display) def set_controls_visible(self, controller, value): + """ + After a new display is configured, all media related widget will be + created too + + ``controller`` + The controller on which controls act. + + ``value`` + control name to be changed. + """ # Generic controls controller.mediabar.setVisible(value) if controller.isLive and controller.display: - if self.curDisplayMediaPlayer and value: - if self.curDisplayMediaPlayer[controller.display] != \ + if self.currentMediaPlayer and value: + if self.currentMediaPlayer[controller.controllerType] != \ self.mediaPlayers[u'webkit']: controller.display.setTransparency(False) - # Special controls: Here media type specific Controls will be enabled - # (e.g. for DVD control, ...) - # TODO - def resize(self, controller, display, player): + def resize(self, display, player): """ After Mainwindow changes or Splitter moved all related media widgets have to be resized + + ``display`` + The display on which output is playing. + + ``player`` + The player which is doing the playing. """ player.resize(display) - def video(self, controller, file, muted, isBackground, hidden=False, - isInfo=False, controlsVisible=True): + def video(self, source, serviceItem, hidden=False, videoBehindText=False): """ Loads and starts a video to run with the option of sound + + ``source`` + Where the call originated form + + ``serviceItem`` + The player which is doing the playing + + ``hidden`` + The player which is doing the playing + + ``videoBehindText`` + Is the video to be played behind text. """ log.debug(u'video') isValid = False + controller = self.displayControllers[source] # stop running videos - self.video_reset(controller) + self.media_reset(controller) controller.media_info = MediaInfo() - if muted: + if videoBehindText: controller.media_info.volume = 0 + controller.media_info.is_background = True else: controller.media_info.volume = controller.volumeSlider.value() - controller.media_info.file_info = QtCore.QFileInfo(file) - controller.media_info.is_background = isBackground - display = None + controller.media_info.is_background = False + controller.media_info.file_info = \ + QtCore.QFileInfo(serviceItem.get_frame_path()) + display = self._define_display(controller) if controller.isLive: - if self.withLivePreview and controller.previewDisplay: - display = controller.previewDisplay - isValid = self.check_file_type(controller, display) - display = controller.display - isValid = self.check_file_type(controller, display) + isValid = self._check_file_type(controller, display, serviceItem) display.override[u'theme'] = u'' display.override[u'video'] = True if controller.media_info.is_background: @@ -327,10 +396,9 @@ class MediaController(object): else: controller.media_info.start_time = \ display.serviceItem.start_time - controller.media_info.end_time = display.serviceItem.end_time + controller.media_info.end_time = serviceItem.end_time elif controller.previewDisplay: - display = controller.previewDisplay - isValid = self.check_file_type(controller, display) + isValid = self._check_file_type(controller, display, serviceItem) if not isValid: # Media could not be loaded correctly critical_error_message_box( @@ -347,33 +415,74 @@ class MediaController(object): # Preview requested if not controller.isLive: autoplay = True - # Visible or background requested - elif not hidden or controller.media_info.is_background: + # Visible or background requested or Service Item wants to autostart + elif not hidden or controller.media_info.is_background or \ + serviceItem.will_auto_start: autoplay = True # Unblank on load set elif Settings().value(u'general/auto unblank', QtCore.QVariant(False)).toBool(): autoplay = True - # Start playback only for visible widgets. If we need just load a video - # and get video information, do not start playback. - if autoplay and not isInfo: - if not self.video_play([controller]): + if autoplay: + if not self.media_play(controller): critical_error_message_box( translate('MediaPlugin.MediaItem', 'Unsupported File'), unicode(translate('MediaPlugin.MediaItem', 'Unsupported File'))) return False - self.set_controls_visible(controller, controlsVisible) - log.debug(u'use %s controller' % self.curDisplayMediaPlayer[display]) + self.set_controls_visible(controller, True) + log.debug(u'use %s controller' % + self.currentMediaPlayer[controller.controllerType]) return True - def check_file_type(self, controller, display): + def media_length(self, serviceItem): + """ + Loads and starts a media item to obtain the media length + + ``serviceItem`` + The ServiceItem containing the details to be played. + """ + controller = self.displayControllers[DisplayControllerType.Plugin] + log.debug(u'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(serviceItem + .get_frame_path()) + display = controller.previewDisplay + if not self._check_file_type(controller, display, serviceItem): + # Media could not be loaded correctly + critical_error_message_box( + translate('MediaPlugin.MediaItem', 'Unsupported File'), + unicode(translate('MediaPlugin.MediaItem', + 'Unsupported File'))) + return False + if not self.media_play(controller): + critical_error_message_box( + translate('MediaPlugin.MediaItem', 'Unsupported File'), + unicode(translate('MediaPlugin.MediaItem', + 'Unsupported File'))) + return False + serviceItem.set_media_length(controller.media_info.length) + self.media_stop(controller) + log.debug(u'use %s controller' % + self.currentMediaPlayer[controller.controllerType]) + return True + + def _check_file_type(self, controller, display, serviceItem): """ Select the correct media Player type from the prioritized Player list + + ``controller`` + First element is the controller which should be used + + ``serviceItem`` + The ServiceItem containing the details to be played. """ - usedPlayers, overriddenPlayer = get_media_players() - if overriddenPlayer and overriddenPlayer != u'auto': - usedPlayers = [overriddenPlayer] + usedPlayers = get_media_players()[0] + if serviceItem.title != UiStrings().Automatic: + usedPlayers = [serviceItem.title.lower()] if controller.media_info.file_info.isFile(): suffix = u'*.%s' % \ controller.media_info.file_info.suffix().toLower() @@ -383,83 +492,127 @@ class MediaController(object): if not controller.media_info.is_background or \ controller.media_info.is_background and \ player.canBackground: - self.resize(controller, display, player) + self.resize(display, player) if player.load(display): - self.curDisplayMediaPlayer[display] = player + self.currentMediaPlayer[controller.controllerType] \ + = player controller.media_info.media_type = MediaType.Video return True if suffix in player.audio_extensions_list: if player.load(display): - self.curDisplayMediaPlayer[display] = player + self.currentMediaPlayer[controller.controllerType] \ + = player controller.media_info.media_type = MediaType.Audio return True else: for title in usedPlayers: player = self.mediaPlayers[title] if player.canFolder: - self.resize(controller, display, player) + self.resize(display, player) if player.load(display): - self.curDisplayMediaPlayer[display] = player + self.currentMediaPlayer[controller.controllerType] \ + = player controller.media_info.media_type = MediaType.Video return True # no valid player found return False - def video_play(self, msg, status=True): + def media_play_msg(self, msg, status=True): """ Responds to the request to play a loaded video ``msg`` First element is the controller which should be used """ - log.debug(u'video_play') - controller = msg[0] - for display in self.curDisplayMediaPlayer.keys(): - if display.controller == controller: - if not self.curDisplayMediaPlayer[display].play(display): - return False - if status: - display.frame.evaluateJavaScript(u'show_blank("desktop");') - self.curDisplayMediaPlayer[display].set_visible(display, - True) - if controller.isLive: - if controller.hideMenu.defaultAction().isChecked(): - controller.hideMenu.defaultAction().trigger() + log.debug(u'media_play_msg') + self.media_play(msg[0],status) + + + def media_play(self, controller, status=True): + """ + Responds to the request to play a loaded video + + ``controller`` + The controller to be played + """ + log.debug(u'media_play') + display = self._define_display(controller) + if not self.currentMediaPlayer[controller.controllerType].play(display): + return False + if status: + display.frame.evaluateJavaScript(u'show_blank("desktop");') + self.currentMediaPlayer[controller.controllerType]\ + .set_visible(display, True) + # Flash needs to be played and will not AutoPlay + if controller.media_info.is_flash: + controller.mediabar.actions[u'playbackPlay'].setVisible(True) + controller.mediabar.actions[u'playbackPause'].setVisible(False) + else: + controller.mediabar.actions[u'playbackPlay'].setVisible(False) + controller.mediabar.actions[u'playbackPause'].setVisible(True) + controller.mediabar.actions[u'playbackStop'].setVisible(True) + if controller.isLive: + if controller.hideMenu.defaultAction().isChecked(): + controller.hideMenu.defaultAction().trigger() # Start Timer for ui updates if not self.timer.isActive(): self.timer.start() return True - def video_pause(self, msg): + def media_pause_msg(self, msg): """ Responds to the request to pause a loaded video ``msg`` First element is the controller which should be used """ - log.debug(u'video_pause') - controller = msg[0] - for display in self.curDisplayMediaPlayer.keys(): - if display.controller == controller: - self.curDisplayMediaPlayer[display].pause(display) + log.debug(u'media_pause_msg') + self.media_pause( msg[0]) - def video_stop(self, msg): + def media_pause(self, controller): + """ + Responds to the request to pause a loaded video + + ``controller`` + The Controller to be paused + """ + log.debug(u'media_pause') + display = self._define_display(controller) + self.currentMediaPlayer[controller.controllerType].pause(display) + controller.mediabar.actions[u'playbackPlay'].setVisible(True) + controller.mediabar.actions[u'playbackStop'].setVisible(True) + controller.mediabar.actions[u'playbackPause'].setVisible(False) + + def media_stop_msg(self, msg): """ Responds to the request to stop a loaded video ``msg`` First element is the controller which should be used """ - log.debug(u'video_stop') - controller = msg[0] - for display in self.curDisplayMediaPlayer.keys(): - if display.controller == controller: - display.frame.evaluateJavaScript(u'show_blank("black");') - self.curDisplayMediaPlayer[display].stop(display) - self.curDisplayMediaPlayer[display].set_visible(display, False) - controller.seekSlider.setSliderPosition(0) + log.debug(u'media_stop_msg') + self.media_stop(msg[0]) - def video_volume(self, msg): + def media_stop(self, controller): + """ + Responds to the request to stop a loaded video + + ``controller`` + The controller that needs to be stopped + """ + log.debug(u'media_stop') + display = self._define_display(controller) + if controller.controllerType in self.currentMediaPlayer: + display.frame.evaluateJavaScript(u'show_blank("black");') + self.currentMediaPlayer[controller.controllerType].stop(display) + self.currentMediaPlayer[controller.controllerType] \ + .set_visible(display, False) + controller.seekSlider.setSliderPosition(0) + controller.mediabar.actions[u'playbackPlay'].setVisible(True) + controller.mediabar.actions[u'playbackStop'].setVisible(False) + controller.mediabar.actions[u'playbackPause'].setVisible(False) + + def media_volume(self, msg): """ Changes the volume of a running video @@ -468,12 +621,11 @@ class MediaController(object): """ controller = msg[0] vol = msg[1][0] - log.debug(u'video_volume %d' % vol) - for display in self.curDisplayMediaPlayer.keys(): - if display.controller == controller: - self.curDisplayMediaPlayer[display].volume(display, vol) + log.debug(u'media_volume %d' % vol) + display = self._define_display(controller) + self.currentMediaPlayer[controller.controllerType].volume(display, vol) - def video_seek(self, msg): + def media_seek(self, msg): """ Responds to the request to change the seek Slider of a loaded video @@ -481,29 +633,30 @@ class MediaController(object): First element is the controller which should be used Second element is a list with the seek Value as first element """ - log.debug(u'video_seek') + log.debug(u'media_seek') controller = msg[0] seekVal = msg[1][0] - for display in self.curDisplayMediaPlayer.keys(): - if display.controller == controller: - self.curDisplayMediaPlayer[display].seek(display, seekVal) + display = self._define_display(controller) + self.currentMediaPlayer[controller.controllerType] \ + .seek(display, seekVal) - def video_reset(self, controller): + def media_reset(self, controller): """ Responds to the request to reset a loaded video """ - log.debug(u'video_reset') + log.debug(u'media_reset') self.set_controls_visible(controller, False) - for display in self.curDisplayMediaPlayer.keys(): - if display.controller == controller: - display.override = {} - self.curDisplayMediaPlayer[display].reset(display) - self.curDisplayMediaPlayer[display].set_visible(display, False) - display.frame.evaluateJavaScript(u'show_video( \ + display = self._define_display(controller) + if controller.controllerType in self.currentMediaPlayer: + display.override = {} + self.currentMediaPlayer[controller.controllerType].reset(display) + self.currentMediaPlayer[controller.controllerType] \ + .set_visible(display, False) + display.frame.evaluateJavaScript(u'show_video( \ "setBackBoard", null, null, null,"hidden");') - del self.curDisplayMediaPlayer[display] + del self.currentMediaPlayer[controller.controllerType] - def video_hide(self, msg): + def media_hide(self, msg): """ Hide the related video Widget @@ -513,15 +666,15 @@ class MediaController(object): isLive = msg[1] if not isLive: return - controller = self.parent.liveController - for display in self.curDisplayMediaPlayer.keys(): - if display.controller != controller or \ - self.curDisplayMediaPlayer[display].state != MediaState.Playing: - continue - self.curDisplayMediaPlayer[display].pause(display) - self.curDisplayMediaPlayer[display].set_visible(display, False) + controller = self.mainWindow.liveController + display = self._define_display(controller) + if self.currentMediaPlayer[controller.controllerType].state \ + == MediaState.Playing: + self.currentMediaPlayer[controller.controllerType].pause(display) + self.currentMediaPlayer[controller.controllerType] \ + .set_visible(display, False) - def video_blank(self, msg): + def media_blank(self, msg): """ Blank the related video Widget @@ -534,15 +687,15 @@ class MediaController(object): if not isLive: return Receiver.send_message(u'live_display_hide', hide_mode) - controller = self.parent.liveController - for display in self.curDisplayMediaPlayer.keys(): - if display.controller != controller or \ - self.curDisplayMediaPlayer[display].state != MediaState.Playing: - continue - self.curDisplayMediaPlayer[display].pause(display) - self.curDisplayMediaPlayer[display].set_visible(display, False) + controller = self.mainWindow.liveController + display = self._define_display(controller) + if self.currentMediaPlayer[controller.controllerType].state \ + == MediaState.Playing: + self.currentMediaPlayer[controller.controllerType].pause(display) + self.currentMediaPlayer[controller.controllerType] \ + .set_visible(display, False) - def video_unblank(self, msg): + def media_unblank(self, msg): """ Unblank the related video Widget @@ -554,35 +707,28 @@ class MediaController(object): isLive = msg[1] if not isLive: return - controller = self.parent.liveController - for display in self.curDisplayMediaPlayer.keys(): - if display.controller != controller or \ - self.curDisplayMediaPlayer[display].state != MediaState.Paused: - continue - if self.curDisplayMediaPlayer[display].play(display): - self.curDisplayMediaPlayer[display].set_visible(display, True) + controller = self.mainWindow.liveController + display = self._define_display(controller) + if controller.controllerType in self.currentMediaPlayer and \ + self.currentMediaPlayer[controller.controllerType].state != MediaState.Playing: + if self.currentMediaPlayer[controller.controllerType].play(display): + self.currentMediaPlayer[controller.controllerType].set_visible(display, True) # Start Timer for ui updates if not self.timer.isActive(): self.timer.start() - def get_audio_extensions_list(self): - audio_list = [] - for player in self.mediaPlayers.values(): - if player.isActive: - for item in player.audio_extensions_list: - if not item in audio_list: - audio_list.append(item) - return audio_list - - def get_video_extensions_list(self): - video_list = [] - for player in self.mediaPlayers.values(): - if player.isActive: - video_list.extend([item for item in player.video_extensions_list - if item not in video_list]) - return video_list - def finalise(self): self.timer.stop() - for controller in self.controller: - self.video_reset(controller) + for controller in self.displayControllers: + self.media_reset(self.displayControllers[controller]) + + def _define_display(self, controller): + """ + Extract the correct display for a given controller + + ``controller`` + Controller to be used + """ + if controller.isLive: + return controller.display + return controller.previewDisplay diff --git a/openlp/core/lib/mediaplayer.py b/openlp/core/ui/media/mediaplayer.py similarity index 96% rename from openlp/core/lib/mediaplayer.py rename to openlp/core/ui/media/mediaplayer.py index f4388a3ae..a2435e35a 100644 --- a/openlp/core/lib/mediaplayer.py +++ b/openlp/core/ui/media/mediaplayer.py @@ -31,8 +31,8 @@ from openlp.core.ui.media import MediaState class MediaPlayer(object): """ - This is the base class media Player class to provide OpenLP with a pluggable media display - framework. + This is the base class media Player class to provide OpenLP with a + pluggable media display framework. """ def __init__(self, parent, name=u'media_player'): @@ -137,3 +137,9 @@ class MediaPlayer(object): Add html code to htmlbuilder """ return u'' + + def get_info(self): + """ + Returns Information about the player + """ + return u'' diff --git a/openlp/core/ui/media/phononplayer.py b/openlp/core/ui/media/phononplayer.py index 561b6bad3..c2935f0fb 100644 --- a/openlp/core/ui/media/phononplayer.py +++ b/openlp/core/ui/media/phononplayer.py @@ -31,11 +31,15 @@ import logging import mimetypes from datetime import datetime +from PyQt4 import QtCore, QtGui from PyQt4.phonon import Phonon -from openlp.core.lib import Receiver -from openlp.core.lib.mediaplayer import MediaPlayer +from openlp.core.lib import Receiver, translate +from openlp.core.lib.settings import Settings + from openlp.core.ui.media import MediaState +from openlp.core.ui.media.mediaplayer import MediaPlayer + log = logging.getLogger(__name__) @@ -56,6 +60,20 @@ ADDITIONAL_EXT = { u'video/mpeg' : [u'.mp4', u'.mts', u'.mov'], u'video/x-ms-wmv': [u'.wmv']} +VIDEO_CSS = u""" +#videobackboard { + z-index:3; + background-color: %s; +} +#video1 { + background-color: %s; + z-index:4; +} +#video2 { + background-color: %s; + z-index:4; +} +""" class PhononPlayer(MediaPlayer): """ @@ -205,3 +223,20 @@ class PhononPlayer(MediaPlayer): if not controller.seekSlider.isSliderDown(): controller.seekSlider.setSliderPosition( display.mediaObject.currentTime()) + + def get_media_display_css(self): + """ + Add css style sheets to htmlbuilder + """ + background = unicode(QtGui.QColor(Settings().value( + u'players/background color', QtCore.QVariant(u'#000000'))).name()) + return VIDEO_CSS % (background,background,background) + + def get_info(self): + return(translate('Media.player', 'Phonon is a media player which ' + 'interacts with the operating system to provide media capabilities' + '.') + + u'
' + translate('Media.player', 'Audio') + + u'
' + unicode(self.audio_extensions_list) + + u'
' + translate('Media.player', 'Video') + + u'
' + unicode(self.video_extensions_list) + u'
') \ No newline at end of file diff --git a/openlp/core/ui/media/playertab.py b/openlp/core/ui/media/playertab.py new file mode 100644 index 000000000..2bf2d4458 --- /dev/null +++ b/openlp/core/ui/media/playertab.py @@ -0,0 +1,255 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2012 Raoul Snyman # +# Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Eric Ludin, 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, # +# Erode Woldsund, Martin Zibricky # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### + +from PyQt4 import QtCore, QtGui + +from openlp.core.lib import SettingsTab, translate, Receiver +from openlp.core.lib.ui import UiStrings, create_button +from openlp.core.lib.settings import Settings +from openlp.core.ui.media import get_media_players, set_media_players + +class MediaQCheckBox(QtGui.QCheckBox): + """ + MediaQCheckBox adds an extra property, playerName to the QCheckBox class. + """ + def setPlayerName(self, name): + self.playerName = name + + +class PlayerTab(SettingsTab): + """ + MediaTab is the Media settings tab in the settings dialog. + """ + def __init__(self, parent, mainWindow): + self.parent = parent + self.mainWindow = mainWindow + self.mediaPlayers = mainWindow.mediaController.mediaPlayers + self.savedUsedPlayers = None + self.iconPath = u':/system/system_settings.png' + player_translated = translate('OpenLP.PlayerTab', 'Players') + SettingsTab.__init__(self, parent, u'Players', player_translated) + + def setupUi(self): + self.setObjectName(u'MediaTab') + SettingsTab.setupUi(self) + self.bgColorGroupBox = QtGui.QGroupBox(self.leftColumn) + self.bgColorGroupBox.setObjectName(u'FontGroupBox') + self.formLayout = QtGui.QFormLayout(self.bgColorGroupBox) + self.formLayout.setObjectName(u'FormLayout') + self.colorLayout = QtGui.QHBoxLayout() + self.backgroundColorLabel = QtGui.QLabel(self.bgColorGroupBox) + self.backgroundColorLabel.setObjectName(u'BackgroundColorLabel') + self.colorLayout.addWidget(self.backgroundColorLabel) + self.backgroundColorButton = QtGui.QPushButton(self.bgColorGroupBox) + self.backgroundColorButton.setObjectName(u'BackgroundColorButton') + self.colorLayout.addWidget(self.backgroundColorButton) + self.formLayout.addRow(self.colorLayout) + self.informationLabel = QtGui.QLabel(self.bgColorGroupBox) + self.informationLabel.setObjectName(u'InformationLabel') + self.informationLabel.setWordWrap(True) + self.formLayout.addRow(self.informationLabel) + self.leftLayout.addWidget(self.bgColorGroupBox) + self.leftLayout.addStretch() + self.rightColumn.setSizePolicy( + QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred) + self.rightLayout.addStretch() + self.mediaPlayerGroupBox = QtGui.QGroupBox(self.leftColumn) + self.mediaPlayerGroupBox.setObjectName(u'mediaPlayerGroupBox') + self.mediaPlayerLayout = QtGui.QVBoxLayout(self.mediaPlayerGroupBox) + self.mediaPlayerLayout.setObjectName(u'mediaPlayerLayout') + self.playerCheckBoxes = {} + self.leftLayout.addWidget(self.mediaPlayerGroupBox) + self.playerOrderGroupBox = QtGui.QGroupBox(self.leftColumn) + self.playerOrderGroupBox.setObjectName(u'playerOrderGroupBox') + self.playerOrderLayout = QtGui.QHBoxLayout(self.playerOrderGroupBox) + self.playerOrderLayout.setObjectName(u'playerOrderLayout') + self.playerOrderlistWidget = QtGui.QListWidget( + self.playerOrderGroupBox) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, + QtGui.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.playerOrderlistWidget.\ + sizePolicy().hasHeightForWidth()) + self.playerOrderlistWidget.setSizePolicy(sizePolicy) + self.playerOrderlistWidget.setVerticalScrollBarPolicy( + QtCore.Qt.ScrollBarAsNeeded) + self.playerOrderlistWidget.setHorizontalScrollBarPolicy( + QtCore.Qt.ScrollBarAlwaysOff) + self.playerOrderlistWidget.setEditTriggers( + QtGui.QAbstractItemView.NoEditTriggers) + self.playerOrderlistWidget.setObjectName(u'playerOrderlistWidget') + self.playerOrderLayout.addWidget(self.playerOrderlistWidget) + self.orderingButtonLayout = QtGui.QVBoxLayout() + self.orderingButtonLayout.setObjectName(u'orderingButtonLayout') + self.orderingButtonLayout.addStretch(1) + self.orderingUpButton = create_button(self, u'orderingUpButton', + role=u'up', click=self.onUpButtonClicked) + self.orderingDownButton = create_button(self, u'orderingDownButton', + role=u'down', click=self.onDownButtonClicked) + self.orderingButtonLayout.addWidget(self.orderingUpButton) + self.orderingButtonLayout.addWidget(self.orderingDownButton) + self.orderingButtonLayout.addStretch(1) + self.playerOrderLayout.addLayout(self.orderingButtonLayout) + self.leftLayout.addWidget(self.playerOrderGroupBox) + self.leftLayout.addStretch() + self.rightLayout.addStretch() + # Signals and slots + QtCore.QObject.connect(self.backgroundColorButton, + QtCore.SIGNAL(u'clicked()'), self.onbackgroundColorButtonClicked) + + def retranslateUi(self): + self.mediaPlayerGroupBox.setTitle( + translate('OpenLP.PlayerTab', 'Available Media Players')) + self.playerOrderGroupBox.setTitle( + translate('OpenLP.PlayerTab', 'Player Search Order')) + self.bgColorGroupBox.setTitle(UiStrings().BackgroundColor) + self.backgroundColorLabel.setText(UiStrings().DefaultColor) + self.informationLabel.setText( + translate('OpenLP.PlayerTab', 'Visible background for videos ' + 'with aspect ratio different to screen.')) + self.retranslatePlayers() + + def onbackgroundColorButtonClicked(self): + new_color = QtGui.QColorDialog.getColor( + QtGui.QColor(self.bg_color), self) + if new_color.isValid(): + self.bg_color = new_color.name() + self.backgroundColorButton.setStyleSheet( + u'background-color: %s' % self.bg_color) + + def onPlayerCheckBoxChanged(self, check_state): + player = self.sender().playerName + if check_state == QtCore.Qt.Checked: + if player not in self.usedPlayers: + self.usedPlayers.append(player) + else: + if player in self.usedPlayers: + self.usedPlayers.remove(player) + self.updatePlayerList() + + def updatePlayerList(self): + self.playerOrderlistWidget.clear() + for player in self.usedPlayers: + if player in self.playerCheckBoxes.keys(): + if len(self.usedPlayers) == 1: + # At least one media player has to stay active + self.playerCheckBoxes[u'%s' % player].setEnabled(False) + else: + self.playerCheckBoxes[u'%s' % player].setEnabled(True) + self.playerOrderlistWidget.addItem( + self.mediaPlayers[unicode(player)].original_name) + + def onUpButtonClicked(self): + row = self.playerOrderlistWidget.currentRow() + if row <= 0: + return + item = self.playerOrderlistWidget.takeItem(row) + self.playerOrderlistWidget.insertItem(row - 1, item) + self.playerOrderlistWidget.setCurrentRow(row - 1) + self.usedPlayers.insert(row - 1, self.usedPlayers.pop(row)) + + def onDownButtonClicked(self): + row = self.playerOrderlistWidget.currentRow() + if row == -1 or row > self.playerOrderlistWidget.count() - 1: + return + item = self.playerOrderlistWidget.takeItem(row) + self.playerOrderlistWidget.insertItem(row + 1, item) + self.playerOrderlistWidget.setCurrentRow(row + 1) + self.usedPlayers.insert(row + 1, self.usedPlayers.pop(row)) + + def load(self): + if self.savedUsedPlayers: + self.usedPlayers = self.savedUsedPlayers + self.usedPlayers = get_media_players()[0] + self.savedUsedPlayers = self.usedPlayers + + settings = Settings() + settings.beginGroup(self.settingsSection) + self.updatePlayerList() + self.bg_color = unicode(settings.value( + u'background color', QtCore.QVariant(u'#000000')).toString()) + self.initial_color = self.bg_color + settings.endGroup() + self.backgroundColorButton.setStyleSheet( + u'background-color: %s' % self.bg_color) + + def save(self): + player_string_changed = False + settings = Settings() + settings.beginGroup(self.settingsSection) + settings.setValue(u'background color', QtCore.QVariant(self.bg_color)) + settings.endGroup() + old_players, override_player = get_media_players() + if self.usedPlayers != old_players: + # clean old Media stuff + set_media_players(self.usedPlayers, override_player) + player_string_changed = True + if player_string_changed: + self.parent.resetSupportedSuffixes() + Receiver.send_message(u'mediaitem_media_rebuild') + Receiver.send_message(u'config_screen_changed') + + def postSetUp(self, postUpdate=False): + """ + Late setup for players as the MediaController has to be initialised + first. + """ + for key, player in self.mediaPlayers.iteritems(): + player = self.mediaPlayers[key] + checkbox = MediaQCheckBox(self.mediaPlayerGroupBox) + checkbox.setEnabled(player.available) + checkbox.setObjectName(player.name + u'CheckBox') + checkbox.setToolTip(player.get_info()) + checkbox.setPlayerName(player.name) + self.playerCheckBoxes[player.name] = checkbox + QtCore.QObject.connect(checkbox,QtCore.SIGNAL(u'stateChanged(int)'), + self.onPlayerCheckBoxChanged) + self.mediaPlayerLayout.addWidget(checkbox) + if player.available and player.name in self.usedPlayers: + checkbox.setChecked(True) + else: + checkbox.setChecked(False) + self.updatePlayerList() + self.retranslatePlayers() + + def retranslatePlayers(self): + """ + Translations for players is dependent on their setup as well + """ + for key in self.mediaPlayers: + player = self.mediaPlayers[key] + checkbox = self.playerCheckBoxes[player.name] + checkbox.setPlayerName(player.name) + if player.available: + checkbox.setText(player.display_name) + else: + checkbox.setText( + unicode(translate('OpenLP.PlayerTab', + '%s (unavailable)')) % player.display_name) \ No newline at end of file diff --git a/openlp/core/ui/media/vendor/__init__.py b/openlp/core/ui/media/vendor/__init__.py new file mode 100644 index 000000000..005d9543f --- /dev/null +++ b/openlp/core/ui/media/vendor/__init__.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2012 Raoul Snyman # +# Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Eric Ludin, 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, # +# Erode Woldsund, Martin Zibricky # +# --------------------------------------------------------------------------- # +# 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/openlp/core/ui/media/vlc.py b/openlp/core/ui/media/vendor/vlc.py similarity index 100% rename from openlp/core/ui/media/vlc.py rename to openlp/core/ui/media/vendor/vlc.py diff --git a/openlp/core/ui/media/vlcplayer.py b/openlp/core/ui/media/vlcplayer.py index d1eef4176..65e829763 100644 --- a/openlp/core/ui/media/vlcplayer.py +++ b/openlp/core/ui/media/vlcplayer.py @@ -35,16 +35,16 @@ import sys from PyQt4 import QtCore, QtGui -from openlp.core.lib import Receiver +from openlp.core.lib import Receiver, translate from openlp.core.lib.settings import Settings -from openlp.core.lib.mediaplayer import MediaPlayer from openlp.core.ui.media import MediaState +from openlp.core.ui.media.mediaplayer import MediaPlayer log = logging.getLogger(__name__) VLC_AVAILABLE = False try: - import vlc + from openlp.core.ui.media.vendor import vlc VLC_AVAILABLE = bool(vlc.get_default_instance()) except (ImportError, NameError, NotImplementedError): pass @@ -187,7 +187,8 @@ class VlcPlayer(MediaPlayer): def play(self, display): controller = display.controller start_time = 0 - if controller.media_info.start_time > 0: + if self.state != MediaState.Paused and \ + controller.media_info.start_time > 0: start_time = controller.media_info.start_time display.vlcMediaPlayer.play() if not self.media_state_wait(display, vlc.State.Playing): @@ -243,3 +244,10 @@ class VlcPlayer(MediaPlayer): controller.seekSlider.setSliderPosition( \ display.vlcMediaPlayer.get_time()) + def get_info(self): + return(translate('Media.player', 'VLC is an external player which ' + 'supports a number of different formats.') + + u'
' + translate('Media.player', 'Audio') + + u'
' + unicode(AUDIO_EXT) + u'
' + + translate('Media.player', 'Video') + u'
' + + unicode(VIDEO_EXT) + u'
') \ No newline at end of file diff --git a/openlp/core/ui/media/webkitplayer.py b/openlp/core/ui/media/webkitplayer.py index b2e3a0c3b..b9108b0b6 100644 --- a/openlp/core/ui/media/webkitplayer.py +++ b/openlp/core/ui/media/webkitplayer.py @@ -27,22 +27,28 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### +from PyQt4 import QtCore, QtGui + import logging -from openlp.core.lib.mediaplayer import MediaPlayer +from openlp.core.lib import translate from openlp.core.ui.media import MediaState +from openlp.core.ui.media.mediaplayer import MediaPlayer +from openlp.core.lib.settings import Settings log = logging.getLogger(__name__) VIDEO_CSS = u""" #videobackboard { z-index:3; - background-color: black; + background-color: %s; } #video1 { + background-color: %s; z-index:4; } #video2 { + background-color: %s; z-index:4; } """ @@ -277,7 +283,10 @@ class WebkitPlayer(MediaPlayer): """ Add css style sheets to htmlbuilder """ - return VIDEO_CSS + FLASH_CSS + background = unicode(QtGui.QColor(Settings().value( + u'players/background color', QtCore.QVariant(u'#000000'))).name()) + css = VIDEO_CSS % (background,background,background) + return css + FLASH_CSS def get_media_display_javascript(self): """ @@ -324,7 +333,6 @@ class WebkitPlayer(MediaPlayer): return True def resize(self, display): - controller = display.controller display.webView.resize(display.size()) def play(self, display): @@ -431,3 +439,12 @@ class WebkitPlayer(MediaPlayer): controller.seekSlider.setMaximum(length) if not controller.seekSlider.isSliderDown(): controller.seekSlider.setSliderPosition(currentTime) + + def get_info(self): + return(translate('Media.player', 'Webkit is a media player which runs ' + 'inside a web browser. This player allows text over video to be ' + 'rendered.') + + u'
' + translate('Media.player', 'Audio') + + u'
' + unicode(AUDIO_EXT) + u'
' + + translate('Media.player', 'Video') + u'
' + + unicode(VIDEO_EXT) + u'
') \ No newline at end of file diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index ea0cc94ba..76fa3b5cb 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -99,8 +99,8 @@ class ServiceManager(QtGui.QWidget): """ Manages the services. This involves taking text strings from plugins and adding them to the service. This service can then be zipped up with all - the resources used into one OSZ file for use on any OpenLP v2 installation. - Also handles the UI tasks of moving things up and down etc. + the resources used into one OSZ or oszl file for use on any OpenLP v2 + installation. Also handles the UI tasks of moving things up and down etc. """ def __init__(self, mainwindow, parent=None): """ @@ -136,7 +136,7 @@ class ServiceManager(QtGui.QWidget): self.toolbar.addToolbarAction(u'saveService', text=UiStrings().SaveService, icon=u':/general/general_save.png', tooltip=translate('OpenLP.ServiceManager', 'Save this service.'), - triggers=self.saveFile) + triggers=self.decideSaveMethod) self.toolbar.addSeparator() self.themeLabel = QtGui.QLabel(u'%s:' % UiStrings().Theme, self) self.themeLabel.setMargin(3) @@ -307,6 +307,9 @@ class ServiceManager(QtGui.QWidget): self.timeAction = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Start Time'), icon=u':/media/media_time.png', triggers=self.onStartTimeForm) + self.autoStartAction = create_widget_action(self.menu, + text=u'', + icon=u':/media/media_time.png', triggers=self.onAutoStart) # Add already existing delete action to the menu. self.menu.addAction(self.serviceManagerList.delete) self.menu.addSeparator() @@ -359,6 +362,7 @@ class ServiceManager(QtGui.QWidget): self.shortFileName()) Settings(). \ setValue(u'servicemanager/last file',QtCore.QVariant(fileName)) + self._saveLite = True if self._fileName.endswith(u'.oszl') else False def fileName(self): """ @@ -380,6 +384,13 @@ class ServiceManager(QtGui.QWidget): u'advanced/expand service item', QtCore.QVariant(u'False')).toBool() + def resetSupportedSuffixes(self): + """ + Resets the Suffixes list. + + """ + self.suffixes = [] + def supportedSuffixes(self, suffix): """ Adds Suffixes supported to the master list. Called from Plugins. @@ -387,7 +398,8 @@ class ServiceManager(QtGui.QWidget): ``suffix`` New Suffix to be supported """ - self.suffixes.append(suffix) + if not suffix in self.suffixes: + self.suffixes.append(suffix) def onNewServiceClicked(self): """ @@ -398,7 +410,7 @@ class ServiceManager(QtGui.QWidget): if result == QtGui.QMessageBox.Cancel: return False elif result == QtGui.QMessageBox.Save: - if not self.saveFile(): + if not self.decideSaveMethod(): return False self.newFile() @@ -416,7 +428,7 @@ class ServiceManager(QtGui.QWidget): if result == QtGui.QMessageBox.Cancel: return False elif result == QtGui.QMessageBox.Save: - self.saveFile() + self.decideSaveMethod() if not loadFile: fileName = unicode(QtGui.QFileDialog.getOpenFileName( self.mainwindow, @@ -424,7 +436,7 @@ class ServiceManager(QtGui.QWidget): SettingsManager.get_last_dir( self.mainwindow.serviceManagerSettingsSection), translate('OpenLP.ServiceManager', - 'OpenLP Service Files (*.osz)'))) + 'OpenLP Service Files (*.osz *.oszl)'))) if not fileName: return False else: @@ -525,7 +537,7 @@ class ServiceManager(QtGui.QWidget): if not item[u'service_item'].validate(): self.serviceItems.remove(item) else: - service_item = item[u'service_item'].get_service_repr() + service_item = item[u'service_item'].get_service_repr(self._saveLite) if service_item[u'header'][u'background_audio']: for i, filename in enumerate( service_item[u'header'][u'background_audio']): @@ -596,6 +608,73 @@ class ServiceManager(QtGui.QWidget): delete_file(temp_file_name) return success + def saveLocalFile(self): + """ + Save the current service file. + + A temporary file is created so that we don't overwrite the existing one + and leave a mangled service file should there be an error when saving. + No files are added to this version of the service as it is deisgned + to only work on the machine it was save on if there are files. + """ + if not self.fileName(): + return self.saveFileAs() + temp_file, temp_file_name = mkstemp(u'.oszl', u'openlp_') + # We don't need the file handle. + os.close(temp_file) + log.debug(temp_file_name) + path_file_name = unicode(self.fileName()) + path, file_name = os.path.split(path_file_name) + basename = os.path.splitext(file_name)[0] + service_file_name = '%s.osd' % basename + log.debug(u'ServiceManager.saveFile - %s', path_file_name) + SettingsManager.set_last_dir( + self.mainwindow.serviceManagerSettingsSection, + path) + service = [] + Receiver.send_message(u'cursor_busy') + # Number of items + 1 to zip it + self.mainwindow.displayProgressBar(len(self.serviceItems) + 1) + for item in self.serviceItems: + self.mainwindow.incrementProgressBar() + service_item = item[u'service_item']. \ + get_service_repr(self._saveLite) + #@todo check for file item on save. + service.append({u'serviceitem': service_item}) + self.mainwindow.incrementProgressBar() + service_content = cPickle.dumps(service) + zip = None + success = True + self.mainwindow.incrementProgressBar() + try: + zip = zipfile.ZipFile(temp_file_name, 'w', zipfile.ZIP_STORED, + True) + # First we add service contents. + zip.writestr(service_file_name.encode(u'utf-8'), service_content) + except IOError: + log.exception(u'Failed to save service to disk: %s', temp_file_name) + Receiver.send_message(u'openlp_error_message', { + u'title': translate(u'OpenLP.ServiceManager', + u'Error Saving File'), + u'message': translate(u'OpenLP.ServiceManager', + u'There was an error saving your file.') + }) + success = False + finally: + if zip: + zip.close() + self.mainwindow.finishedProgressBar() + Receiver.send_message(u'cursor_normal') + if success: + try: + shutil.copy(temp_file_name, path_file_name) + except: + return self.saveFileAs() + self.mainwindow.addRecentFile(path_file_name) + self.setModified(False) + delete_file(temp_file_name) + return success + def saveFileAs(self): """ Get a file name and then call :func:`ServiceManager.saveFile` to @@ -632,9 +711,19 @@ class ServiceManager(QtGui.QWidget): directory = unicode(SettingsManager.get_last_dir( self.mainwindow.serviceManagerSettingsSection)) path = os.path.join(directory, default_filename) - fileName = unicode(QtGui.QFileDialog.getSaveFileName(self.mainwindow, - UiStrings().SaveService, path, - translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz)'))) + # SaveAs from osz to oszl is not valid as the files will be deleted + # on exit which is not sensible or usable in the long term. + if self._fileName.endswith(u'oszl') or not self._fileName: + fileName = unicode(QtGui.QFileDialog.getSaveFileName( + self.mainwindow, UiStrings().SaveService, path, + translate('OpenLP.ServiceManager', + 'OpenLP Service Files (*.osz);;' + 'OpenLP Service Files - lite (*.oszl)'))) + else: + fileName = unicode(QtGui.QFileDialog.getSaveFileName( + self.mainwindow, UiStrings().SaveService, path, + translate('OpenLP.ServiceManager', + 'OpenLP Service Files (*.osz);;'))) if not fileName: return False if os.path.splitext(fileName)[1] == u'': @@ -643,9 +732,23 @@ class ServiceManager(QtGui.QWidget): ext = os.path.splitext(fileName)[1] fileName.replace(ext, u'.osz') self.setFileName(fileName) - return self.saveFile() + self.decideSaveMethod() + + def decideSaveMethod(self): + """ + Determine which type of save method to use. + """ + if not self.fileName(): + return self.saveFileAs() + if self._saveLite: + return self.saveLocalFile() + else: + return self.saveFile() def loadFile(self, fileName): + """ + Load an existing service file + """ if not fileName: return False fileName = unicode(fileName) @@ -680,12 +783,16 @@ class ServiceManager(QtGui.QWidget): items = cPickle.load(fileTo) fileTo.close() self.newFile() + self.setFileName(fileName) self.mainwindow.displayProgressBar(len(items)) for item in items: self.mainwindow.incrementProgressBar() serviceItem = ServiceItem() serviceItem.renderer = self.mainwindow.renderer - serviceItem.set_from_service(item, self.servicePath) + if self._saveLite: + serviceItem.set_from_service(item) + else: + serviceItem.set_from_service(item, self.servicePath) self.validateItem(serviceItem) self.load_item_uuid = 0 if serviceItem.is_capable(ItemCapabilities.OnLoadUpdate): @@ -697,7 +804,6 @@ class ServiceManager(QtGui.QWidget): serviceItem.temporary_edit = self.load_item_temporary self.addServiceItem(serviceItem, repaint=False) delete_file(p_file) - self.setFileName(fileName) self.mainwindow.addRecentFile(fileName) self.setModified(False) Settings().setValue( @@ -760,6 +866,7 @@ class ServiceManager(QtGui.QWidget): self.maintainAction.setVisible(False) self.notesAction.setVisible(False) self.timeAction.setVisible(False) + self.autoStartAction.setVisible(False) if serviceItem[u'service_item'].is_capable(ItemCapabilities.CanEdit)\ and serviceItem[u'service_item'].edit_id: self.editAction.setVisible(True) @@ -771,6 +878,14 @@ class ServiceManager(QtGui.QWidget): if serviceItem[u'service_item']\ .is_capable(ItemCapabilities.HasVariableStartTime): self.timeAction.setVisible(True) + if serviceItem[u'service_item']\ + .is_capable(ItemCapabilities.CanAutoStartForLive): + self.autoStartAction.setVisible(True) + self.autoStartAction.setText(translate('OpenLP.ServiceManager', + '&Auto Start - inactive')) + if serviceItem[u'service_item'].will_auto_start: + self.autoStartAction.setText(translate('OpenLP.ServiceManager', + '&Auto Start - active')) self.themeMenu.menuAction().setVisible(False) # Set up the theme menu. if serviceItem[u'service_item'].is_text() and \ @@ -805,6 +920,14 @@ class ServiceManager(QtGui.QWidget): if self.startTimeForm.exec_(): self.repaintServiceList(item, -1) + def onAutoStart(self): + """ + Toggles to Auto Start Setting. + """ + item = self.findServiceItem()[0] + self.serviceItems[item][u'service_item'].will_auto_start = \ + not self.serviceItems[item][u'service_item'].will_auto_start + def onServiceItemEditForm(self): """ Opens a dialog to edit the service item and update the service @@ -1095,10 +1218,12 @@ class ServiceManager(QtGui.QWidget): Validates the service item and if the suffix matches an accepted one it allows the item to be displayed. """ + #@todo check file items exist if serviceItem.is_command(): type = serviceItem._raw_frames[0][u'title'].split(u'.')[-1] if type.lower() not in self.suffixes: serviceItem.is_valid = False + #@todo check file items exist def cleanUp(self): """ @@ -1379,6 +1504,9 @@ class ServiceManager(QtGui.QWidget): filename = unicode(url.toLocalFile()) if filename.endswith(u'.osz'): self.onLoadServiceClicked(filename) + elif filename.endswith(u'.oszl'): + # todo correct + self.onLoadServiceClicked(filename) elif link.hasText(): plugin = unicode(link.text()) item = self.serviceManagerList.itemAt(event.pos()) diff --git a/openlp/core/ui/settingsform.py b/openlp/core/ui/settingsform.py index 25a2afac2..570adce42 100644 --- a/openlp/core/ui/settingsform.py +++ b/openlp/core/ui/settingsform.py @@ -35,6 +35,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import Receiver, build_icon, PluginStatus from openlp.core.ui import AdvancedTab, GeneralTab, ThemesTab +from openlp.core.ui.media import PlayerTab from settingsdialog import Ui_SettingsDialog log = logging.getLogger(__name__) @@ -47,6 +48,7 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): """ Initialise the settings form """ + self.mainWindow = mainWindow QtGui.QDialog.__init__(self, parent) self.setupUi(self) # General tab @@ -55,6 +57,8 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): self.themesTab = ThemesTab(self, mainWindow) # Advanced tab self.advancedTab = AdvancedTab(self) + # Advanced tab + self.playerTab = PlayerTab(self, mainWindow) def exec_(self): # load all the settings @@ -65,7 +69,8 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): self.insertTab(self.generalTab, 0, PluginStatus.Active) self.insertTab(self.themesTab, 1, PluginStatus.Active) self.insertTab(self.advancedTab, 2, PluginStatus.Active) - count = 3 + self.insertTab(self.playerTab, 3, PluginStatus.Active) + count = 4 for plugin in self.plugins: if plugin.settingsTab: self.insertTab(plugin.settingsTab, count, plugin.status) @@ -94,6 +99,7 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): """ Process the form saving the settings """ + self.resetSuffixes = True for tabIndex in range(self.stackedLayout.count()): self.stackedLayout.widget(tabIndex).save() # Must go after all settings are save @@ -115,6 +121,7 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): self.generalTab.postSetUp() self.themesTab.postSetUp() self.advancedTab.postSetUp() + self.playerTab.postSetUp() for plugin in self.plugins: if plugin.settingsTab: plugin.settingsTab.postSetUp() @@ -125,3 +132,13 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): """ self.stackedLayout.setCurrentIndex(tabIndex) self.stackedLayout.currentWidget().tabVisible() + + def resetSupportedSuffixes(self): + """ + Control the resetting of the serviceManager suffix list as can be + called by a number of settings tab and only needs to be called once + per save. + """ + if self.resetSuffixes: + self.mainWindow.serviceManagerContents.resetSupportedSuffixes() + self.resetSuffixes = False \ No newline at end of file diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index b7e9aea34..23754eae1 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -39,15 +39,16 @@ from openlp.core.lib import OpenLPToolbar, Receiver, ItemCapabilities, \ from openlp.core.lib.ui import UiStrings, create_action from openlp.core.lib.settings import Settings from openlp.core.lib import SlideLimits, ServiceItemAction -from openlp.core.ui import HideMode, MainDisplay, Display, ScreenList +from openlp.core.ui import HideMode, MainDisplay, Display, ScreenList, \ + DisplayControllerType from openlp.core.utils.actions import ActionList, CategoryOrder log = logging.getLogger(__name__) -class Controller(QtGui.QWidget): +class DisplayController(QtGui.QWidget): """ - Controller is a general controller widget. + Controller is a general display controller widget. """ def __init__(self, parent, isLive=False): """ @@ -56,6 +57,7 @@ class Controller(QtGui.QWidget): QtGui.QWidget.__init__(self, parent) self.isLive = isLive self.display = None + self.controllerType = DisplayControllerType.Plugin def sendToPlugins(self, *args): """ @@ -69,7 +71,7 @@ class Controller(QtGui.QWidget): Receiver.send_message('%s' % sender, [controller, args]) -class SlideController(Controller): +class SlideController(DisplayController): """ SlideController is the slide controller widget. This widget is what the user uses to control the displaying of verses/slides/etc on the screen. @@ -78,7 +80,7 @@ class SlideController(Controller): """ Set up the Slide Controller. """ - Controller.__init__(self, parent, isLive) + DisplayController.__init__(self, parent, isLive) self.screens = ScreenList() try: self.ratio = float(self.screens.current[u'size'].width()) / \ @@ -185,7 +187,9 @@ class SlideController(Controller): category=self.category, triggers=self.onSlideSelectedNextAction) self.toolbar.addAction(self.nextItem) self.toolbar.addSeparator() + self.controllerType = DisplayControllerType.Preview if self.isLive: + self.controllerType = DisplayControllerType.Live # Hide Menu self.hideMenu = QtGui.QToolButton(self.toolbar) self.hideMenu.setObjectName(u'hideMenu') @@ -263,7 +267,7 @@ class SlideController(Controller): 'Edit and reload song preview.'), triggers=self.onEditSong) self.controllerLayout.addWidget(self.toolbar) # Build the Media Toolbar - self.mediaController.add_controller_items(self, self.controllerLayout) + self.mediaController.register_controller(self) if self.isLive: # Build the Song Toolbar self.songMenu = QtGui.QToolButton(self.toolbar) @@ -516,7 +520,7 @@ class SlideController(Controller): def liveEscape(self): self.display.setVisible(False) - self.mediaController.video_stop([self]) + self.mediaController.media_stop(self) def toggleDisplay(self, action): """ @@ -586,14 +590,14 @@ class SlideController(Controller): float(self.screens.current[u'size'].height()) except ZeroDivisionError: self.ratio = 1 - self.mediaController.setup_display(self.display) + self.mediaController.setup_display(self.display, False) self.previewSizeChanged() self.previewDisplay.setup() serviceItem = ServiceItem() self.previewDisplay.webView.setHtml(build_html(serviceItem, self.previewDisplay.screen, None, self.isLive, plugins=PluginManager.get_instance().plugins)) - self.mediaController.setup_display(self.previewDisplay) + self.mediaController.setup_display(self.previewDisplay,True) if self.serviceItem: self.refreshServiceItem() @@ -1343,9 +1347,8 @@ class SlideController(Controller): Respond to the arrival of a media service item """ log.debug(u'SlideController onMediaStart') - file = item.get_frame_path() - self.mediaController.video(self, file, False, False, self.hideMode()) - if not self.isLive or self.mediaController.withLivePreview: + self.mediaController.video(self.controllerType, item, self.hideMode()) + if not self.isLive: self.previewDisplay.show() self.slidePreview.hide() @@ -1354,7 +1357,7 @@ class SlideController(Controller): Respond to a request to close the Video """ log.debug(u'SlideController onMediaClose') - self.mediaController.video_reset(self) + self.mediaController.media_reset(self) self.previewDisplay.hide() self.slidePreview.show() diff --git a/openlp/plugins/alerts/lib/alertstab.py b/openlp/plugins/alerts/lib/alertstab.py index 53662d3ad..e97fbce3f 100644 --- a/openlp/plugins/alerts/lib/alertstab.py +++ b/openlp/plugins/alerts/lib/alertstab.py @@ -123,7 +123,7 @@ class AlertsTab(SettingsTab): translate('AlertsPlugin.AlertsTab', 'Alert timeout:')) self.timeoutSpinBox.setSuffix(UiStrings().Seconds) self.previewGroupBox.setTitle(UiStrings().Preview) - self.fontPreview.setText(UiStrings().OLPV2) + self.fontPreview.setText(UiStrings().OLPV2x) def onBackgroundColorButtonClicked(self): new_color = QtGui.QColorDialog.getColor( diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 3ac7c3e2b..77ac67aac 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -32,7 +32,7 @@ import logging from PyQt4 import QtCore, QtGui from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \ - translate, create_separated_list + translate, create_separated_list, ServiceItemContext from openlp.core.lib.searchedit import SearchEdit from openlp.core.lib.settings import Settings from openlp.core.lib.ui import UiStrings, set_case_insensitive_completer, \ @@ -894,7 +894,7 @@ class BibleMediaItem(MediaManagerItem): return items def generateSlideData(self, service_item, item=None, xmlVersion=False, - remote=False): + remote=False, context=ServiceItemContext.Service): """ Generates and formats the slides for the service item as well as the service item's title. diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py index b9fa9c5cc..77b1d8c0b 100644 --- a/openlp/plugins/custom/lib/mediaitem.py +++ b/openlp/plugins/custom/lib/mediaitem.py @@ -33,7 +33,7 @@ from PyQt4 import QtCore, QtGui from sqlalchemy.sql import or_, func from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \ - check_item_selected, translate + check_item_selected, translate, ServiceItemContext from openlp.core.lib.ui import UiStrings from openlp.core.lib.settings import Settings from openlp.plugins.custom.forms import EditCustomForm @@ -195,7 +195,7 @@ class CustomMediaItem(MediaManagerItem): self.searchTextEdit.setFocus() def generateSlideData(self, service_item, item=None, xmlVersion=False, - remote=False): + remote=False, context=ServiceItemContext.Service): item_id = self._getIdOfItemToGenerate(item, self.remoteCustom) service_item.add_capability(ItemCapabilities.CanEdit) service_item.add_capability(ItemCapabilities.CanPreview) diff --git a/openlp/plugins/images/lib/imagetab.py b/openlp/plugins/images/lib/imagetab.py index 184be7eea..241a2a05d 100644 --- a/openlp/plugins/images/lib/imagetab.py +++ b/openlp/plugins/images/lib/imagetab.py @@ -31,6 +31,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import SettingsTab, translate, Receiver from openlp.core.lib.settings import Settings +from openlp.core.lib.ui import UiStrings class ImageTab(SettingsTab): """ @@ -68,10 +69,8 @@ class ImageTab(SettingsTab): QtCore.SIGNAL(u'clicked()'), self.onbackgroundColorButtonClicked) def retranslateUi(self): - self.bgColorGroupBox.setTitle( - translate('ImagesPlugin.ImageTab', 'Background Color')) - self.backgroundColorLabel.setText( - translate('ImagesPlugin.ImageTab', 'Default Color:')) + self.bgColorGroupBox.setTitle(UiStrings().BackgroundColor) + self.backgroundColorLabel.setText(UiStrings().DefaultColor) self.informationLabel.setText( translate('ImagesPlugin.ImageTab', 'Visible background for images ' 'with aspect ratio different to screen.')) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 4190e9401..43ad97248 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -34,7 +34,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import MediaManagerItem, build_icon, ItemCapabilities, \ SettingsManager, translate, check_item_selected, check_directory_exists, \ - Receiver, create_thumb, validate_thumb + Receiver, create_thumb, validate_thumb, ServiceItemContext from openlp.core.lib.ui import UiStrings, critical_error_message_box from openlp.core.lib.settings import Settings from openlp.core.utils import AppLocation, delete_file, locale_compare, \ @@ -153,7 +153,7 @@ class ImageMediaItem(MediaManagerItem): Receiver.send_message(u'cursor_normal') def generateSlideData(self, service_item, item=None, xmlVersion=False, - remote=False): + remote=False, context=ServiceItemContext.Service): background = QtGui.QColor(Settings().value(self.settingsSection + u'/background color', QtCore.QVariant(u'#000000'))) if item: diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 7e208c49a..1ab36ff45 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -34,20 +34,21 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import MediaManagerItem, build_icon, ItemCapabilities, \ SettingsManager, translate, check_item_selected, Receiver, MediaType, \ - ServiceItem, build_html + ServiceItem, build_html, ServiceItemContext +from openlp.core.lib.settings import Settings from openlp.core.lib.ui import UiStrings, critical_error_message_box, \ create_horizontal_adjusting_combo_box -from openlp.core.ui import Controller, Display +from openlp.core.ui import DisplayController, Display, DisplayControllerType from openlp.core.ui.media import get_media_players, set_media_players from openlp.core.utils import locale_compare log = logging.getLogger(__name__) CLAPPERBOARD = u':/media/slidecontroller_multimedia.png' -VIDEO = QtGui.QImage(u':/media/media_video.png') -AUDIO = QtGui.QImage(u':/media/media_audio.png') -DVD_ICON = QtGui.QImage(u':/media/media_video.png') -ERROR = QtGui.QImage(u':/general/general_delete.png') +VIDEO = build_icon(QtGui.QImage(u':/media/media_video.png')) +AUDIO = build_icon(QtGui.QImage(u':/media/media_audio.png')) +DVDICON = build_icon(QtGui.QImage(u':/media/media_video.png')) +ERROR = build_icon(QtGui.QImage(u':/general/general_delete.png')) class MediaMediaItem(MediaManagerItem): """ @@ -58,39 +59,31 @@ class MediaMediaItem(MediaManagerItem): def __init__(self, parent, plugin, icon): self.iconPath = u'images/image' self.background = False - self.previewFunction = CLAPPERBOARD self.automatic = u'' MediaManagerItem.__init__(self, parent, plugin, icon) self.singleServiceItem = False self.hasSearch = True self.mediaObject = None - self.mediaController = Controller(parent) - self.mediaController.controllerLayout = QtGui.QVBoxLayout() - self.plugin.mediaController.add_controller_items(self.mediaController, \ - self.mediaController.controllerLayout) - self.plugin.mediaController.set_controls_visible(self.mediaController, \ + self.displayController = DisplayController(parent) + self.displayController.controllerLayout = QtGui.QVBoxLayout() + self.plugin.mediaController.register_controller(self.displayController) + self.plugin.mediaController.set_controls_visible(self.displayController, False) - self.mediaController.previewDisplay = Display(self.mediaController, \ - False, self.mediaController) - self.mediaController.previewDisplay.setGeometry( + self.displayController.previewDisplay = Display(self.displayController, + False, self.displayController) + self.displayController.previewDisplay.hide() + self.displayController.previewDisplay.setGeometry( QtCore.QRect(0, 0, 300, 300)) - self.mediaController.previewDisplay.screen = \ - {u'size':self.mediaController.previewDisplay.geometry()} - self.mediaController.previewDisplay.setup() - serviceItem = ServiceItem() - self.mediaController.previewDisplay.webView.setHtml(build_html( \ - serviceItem, self.mediaController.previewDisplay.screen, None, \ - False, None)) - self.mediaController.previewDisplay.setup() - self.plugin.mediaController.setup_display( \ - self.mediaController.previewDisplay) - self.mediaController.previewDisplay.hide() - + self.displayController.previewDisplay.screen = \ + {u'size':self.displayController.previewDisplay.geometry()} + self.displayController.previewDisplay.setup() + self.plugin.mediaController.setup_display( + self.displayController.previewDisplay, False) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'video_background_replaced'), self.videobackgroundReplaced) QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'mediaitem_media_rebuild'), self.rebuild) + QtCore.SIGNAL(u'mediaitem_media_rebuild'), self.rebuild_players) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'config_screen_changed'), self.displaySetup) # Allow DnD from the desktop @@ -98,18 +91,14 @@ class MediaMediaItem(MediaManagerItem): def retranslateUi(self): self.onNewPrompt = translate('MediaPlugin.MediaItem', 'Select Media') - self.onNewFileMasks = unicode(translate('MediaPlugin.MediaItem', - 'Videos (%s);;Audio (%s);;%s (*)')) % ( - u' '.join(self.plugin.video_extensions_list), - u' '.join(self.plugin.audio_extensions_list), UiStrings().AllFiles) self.replaceAction.setText(UiStrings().ReplaceBG) self.replaceAction.setToolTip(UiStrings().ReplaceLiveBG) self.resetAction.setText(UiStrings().ResetBG) self.resetAction.setToolTip(UiStrings().ResetLiveBG) - self.automatic = translate('MediaPlugin.MediaItem', - 'Automatic') + self.automatic = UiStrings().Automatic self.displayTypeLabel.setText( translate('MediaPlugin.MediaItem', 'Use Player:')) + self.rebuild_players() def requiredIcons(self): MediaManagerItem.requiredIcons(self) @@ -157,7 +146,7 @@ class MediaMediaItem(MediaManagerItem): """ Called to reset the Live background with the media selected, """ - self.plugin.liveController.mediaController.video_reset( \ + self.plugin.liveController.mediaController.media_reset( self.plugin.liveController) self.resetAction.setVisible(False) @@ -177,8 +166,14 @@ class MediaMediaItem(MediaManagerItem): item = self.listView.currentItem() filename = unicode(item.data(QtCore.Qt.UserRole).toString()) if os.path.exists(filename): - if self.plugin.liveController.mediaController.video( \ - self.plugin.liveController, filename, True, True): + service_item = ServiceItem() + service_item.title = u'webkit' + service_item.shortname = service_item.title + (path, name) = os.path.split(filename) + service_item.add_from_command(path, name,CLAPPERBOARD) + if self.plugin.liveController.mediaController.video( + DisplayControllerType.Live, service_item, + videoBehindText=True): self.resetAction.setVisible(True) else: critical_error_message_box(UiStrings().LiveBGError, @@ -191,7 +186,7 @@ class MediaMediaItem(MediaManagerItem): 'the media file "%s" no longer exists.')) % filename) def generateSlideData(self, service_item, item=None, xmlVersion=False, - remote=False): + remote=False, context=ServiceItemContext.Live): if item is None: item = self.listView.currentItem() if item is None: @@ -202,37 +197,27 @@ class MediaMediaItem(MediaManagerItem): # File is no longer present critical_error_message_box( translate('MediaPlugin.MediaItem', 'Missing Media File'), - unicode(translate('MediaPlugin.MediaItem', - 'The file %s no longer exists.')) % filename) + unicode(translate('MediaPlugin.MediaItem', + 'The file %s no longer exists.')) % filename) return False - self.mediaLength = 0 - # Get media information and its length. - # - # This code (mediaController.video()) starts playback but we - # need only media information not video to start. Otherwise - # video is played twice. Find another way to get media info - # without loading and starting video playback. - # - # TODO Test getting media length with other media backends - # Phonon/Webkit. - if self.plugin.mediaController.video(self.mediaController, - filename, muted=False, isBackground=False, isInfo=True, - controlsVisible=False): - self.mediaLength = self.mediaController.media_info.length - service_item.media_length = self.mediaLength - if self.mediaLength > 0: - service_item.add_capability( - ItemCapabilities.HasVariableStartTime) - else: - return False - service_item.media_length = self.mediaLength - service_item.title = unicode(self.plugin.nameStrings[u'singular']) - service_item.add_capability(ItemCapabilities.RequiresMedia) - # force a non-existent theme - service_item.theme = -1 - frame = CLAPPERBOARD + service_item.title = unicode(self.displayTypeComboBox.currentText()) + service_item.shortname = service_item.title (path, name) = os.path.split(filename) - service_item.add_from_command(path, name, frame) + 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.plugin.mediaController.media_length(service_item): + return False + service_item.add_capability(ItemCapabilities.CanAutoStartForLive) + service_item.add_capability(ItemCapabilities.RequiresMedia) + service_item.add_capability(ItemCapabilities.HasDetailedTitleDisplay) + if Settings().value(self.settingsSection + u'/media auto start', + QtCore.QVariant(QtCore.Qt.Unchecked)).toInt()[0]\ + == QtCore.Qt.Checked: + service_item.will_auto_start = True + # force a non-existent theme + service_item.theme = -1 return True def initialise(self): @@ -241,7 +226,7 @@ class MediaMediaItem(MediaManagerItem): self.loadList(SettingsManager.load_list(self.settingsSection, u'media')) self.populateDisplayTypes() - def rebuild(self): + def rebuild_players(self): """ Rebuild the tab in the media manager when changes are made in the settings @@ -249,12 +234,13 @@ class MediaMediaItem(MediaManagerItem): self.populateDisplayTypes() self.onNewFileMasks = unicode(translate('MediaPlugin.MediaItem', 'Videos (%s);;Audio (%s);;%s (*)')) % ( - u' '.join(self.plugin.video_extensions_list), - u' '.join(self.plugin.audio_extensions_list), UiStrings().AllFiles) + u' '.join(self.plugin.mediaController.video_extensions_list), + u' '.join(self.plugin.mediaController.audio_extensions_list), + UiStrings().AllFiles) def displaySetup(self): - self.plugin.mediaController.setup_display( \ - self.mediaController.previewDisplay) + self.plugin.mediaController.setup_display( + self.displayController.previewDisplay, False) def populateDisplayTypes(self): """ @@ -305,18 +291,21 @@ class MediaMediaItem(MediaManagerItem): if not os.path.exists(track): filename = os.path.split(unicode(track))[1] item_name = QtGui.QListWidgetItem(filename) - item_name.setIcon(build_icon(ERROR)) + item_name.setIcon(ERROR) item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(track)) elif track_info.isFile(): filename = os.path.split(unicode(track))[1] item_name = QtGui.QListWidgetItem(filename) - item_name.setIcon(build_icon(VIDEO)) + if u'*.%s' % (filename.split(u'.')[-1].lower()) in \ + self.plugin.mediaController.audio_extensions_list: + item_name.setIcon(AUDIO) + else: + item_name.setIcon(VIDEO) item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(track)) else: filename = os.path.split(unicode(track))[1] item_name = QtGui.QListWidgetItem(filename) - #TODO: add the appropriate Icon - #item_name.setIcon(build_icon(DVD_ICON)) + item_name.setIcon(build_icon(DVDICON)) item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(track)) item_name.setToolTip(track) self.listView.addItem(item_name) @@ -327,9 +316,9 @@ class MediaMediaItem(MediaManagerItem): key=lambda filename: os.path.split(unicode(filename))[1]) ext = [] if type == MediaType.Audio: - ext = self.plugin.audio_extensions_list + ext = self.plugin.mediaController.audio_extensions_list else: - ext = self.plugin.video_extensions_list + ext = self.plugin.mediaController.video_extensions_list ext = map(lambda x: x[1:], ext) media = filter(lambda x: os.path.splitext(x)[1] in ext, media) return media diff --git a/openlp/plugins/media/lib/mediatab.py b/openlp/plugins/media/lib/mediatab.py index 83b1c74c0..8de5171e7 100644 --- a/openlp/plugins/media/lib/mediatab.py +++ b/openlp/plugins/media/lib/mediatab.py @@ -33,6 +33,7 @@ from openlp.core.lib import SettingsTab, translate, Receiver from openlp.core.lib.ui import UiStrings, create_button from openlp.core.lib.settings import Settings from openlp.core.ui.media import get_media_players, set_media_players + class MediaQCheckBox(QtGui.QCheckBox): """ MediaQCheckBox adds an extra property, playerName to the QCheckBox class. @@ -45,60 +46,13 @@ class MediaTab(SettingsTab): """ MediaTab is the Media settings tab in the settings dialog. """ - def __init__(self, parent, title, visible_title, media_players, icon_path): - self.mediaPlayers = media_players - self.savedUsedPlayers = None + def __init__(self, parent, title, visible_title, icon_path): + self.parent = parent SettingsTab.__init__(self, parent, title, visible_title, icon_path) def setupUi(self): self.setObjectName(u'MediaTab') SettingsTab.setupUi(self) - self.mediaPlayerGroupBox = QtGui.QGroupBox(self.leftColumn) - self.mediaPlayerGroupBox.setObjectName(u'mediaPlayerGroupBox') - self.mediaPlayerLayout = QtGui.QVBoxLayout(self.mediaPlayerGroupBox) - self.mediaPlayerLayout.setObjectName(u'mediaPlayerLayout') - self.playerCheckBoxes = {} - for key, player in self.mediaPlayers.iteritems(): - player = self.mediaPlayers[key] - checkbox = MediaQCheckBox(self.mediaPlayerGroupBox) - checkbox.setEnabled(player.available) - checkbox.setObjectName(player.name + u'CheckBox') - self.playerCheckBoxes[player.name] = checkbox - self.mediaPlayerLayout.addWidget(checkbox) - self.leftLayout.addWidget(self.mediaPlayerGroupBox) - self.playerOrderGroupBox = QtGui.QGroupBox(self.leftColumn) - self.playerOrderGroupBox.setObjectName(u'playerOrderGroupBox') - self.playerOrderLayout = QtGui.QHBoxLayout(self.playerOrderGroupBox) - self.playerOrderLayout.setObjectName(u'playerOrderLayout') - self.playerOrderlistWidget = QtGui.QListWidget( \ - self.playerOrderGroupBox) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, - QtGui.QSizePolicy.Expanding) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.playerOrderlistWidget. \ - sizePolicy().hasHeightForWidth()) - self.playerOrderlistWidget.setSizePolicy(sizePolicy) - self.playerOrderlistWidget.setVerticalScrollBarPolicy( \ - QtCore.Qt.ScrollBarAsNeeded) - self.playerOrderlistWidget.setHorizontalScrollBarPolicy( \ - QtCore.Qt.ScrollBarAlwaysOff) - self.playerOrderlistWidget.setEditTriggers( \ - QtGui.QAbstractItemView.NoEditTriggers) - self.playerOrderlistWidget.setObjectName(u'playerOrderlistWidget') - self.playerOrderLayout.addWidget(self.playerOrderlistWidget) - self.orderingButtonLayout = QtGui.QVBoxLayout() - self.orderingButtonLayout.setObjectName(u'orderingButtonLayout') - self.orderingButtonLayout.addStretch(1) - self.orderingUpButton = create_button(self, u'orderingUpButton', - role=u'up', click=self.onUpButtonClicked) - self.orderingDownButton = create_button(self, u'orderingDownButton', - role=u'down', click=self.onDownButtonClicked) - self.orderingButtonLayout.addWidget(self.orderingUpButton) - self.orderingButtonLayout.addWidget(self.orderingDownButton) - self.orderingButtonLayout.addStretch(1) - self.playerOrderLayout.addLayout(self.orderingButtonLayout) - self.leftLayout.addWidget(self.playerOrderGroupBox) self.advancedGroupBox = QtGui.QGroupBox(self.leftColumn) self.advancedGroupBox.setObjectName(u'advancedGroupBox') self.advancedLayout = QtGui.QVBoxLayout(self.advancedGroupBox) @@ -106,110 +60,44 @@ class MediaTab(SettingsTab): self.overridePlayerCheckBox = QtGui.QCheckBox(self.advancedGroupBox) self.overridePlayerCheckBox.setObjectName(u'overridePlayerCheckBox') self.advancedLayout.addWidget(self.overridePlayerCheckBox) + self.autoStartCheckBox = QtGui.QCheckBox(self.advancedGroupBox) + self.autoStartCheckBox.setObjectName(u'autoStartCheckBox') + self.advancedLayout.addWidget(self.autoStartCheckBox) self.leftLayout.addWidget(self.advancedGroupBox) self.leftLayout.addStretch() self.rightLayout.addStretch() - for key in self.mediaPlayers: - player = self.mediaPlayers[key] - checkbox = self.playerCheckBoxes[player.name] - QtCore.QObject.connect(checkbox, - QtCore.SIGNAL(u'stateChanged(int)'), - self.onPlayerCheckBoxChanged) def retranslateUi(self): - self.mediaPlayerGroupBox.setTitle( - translate('MediaPlugin.MediaTab', 'Available Media Players')) - for key in self.mediaPlayers: - player = self.mediaPlayers[key] - checkbox = self.playerCheckBoxes[player.name] - checkbox.setPlayerName(player.name) - if player.available: - checkbox.setText(player.display_name) - else: - checkbox.setText( - unicode(translate('MediaPlugin.MediaTab', - '%s (unavailable)')) % player.display_name) - self.playerOrderGroupBox.setTitle( - translate('MediaPlugin.MediaTab', 'Player Order')) self.advancedGroupBox.setTitle(UiStrings().Advanced) self.overridePlayerCheckBox.setText( translate('MediaPlugin.MediaTab', 'Allow media player to be overridden')) - - def onPlayerCheckBoxChanged(self, check_state): - player = self.sender().playerName - if check_state == QtCore.Qt.Checked: - if player not in self.usedPlayers: - self.usedPlayers.append(player) - else: - if player in self.usedPlayers: - self.usedPlayers.remove(player) - self.updatePlayerList() - - def updatePlayerList(self): - self.playerOrderlistWidget.clear() - for player in self.usedPlayers: - if player in self.playerCheckBoxes.keys(): - if len(self.usedPlayers) == 1: - # At least one media player has to stay active - self.playerCheckBoxes[u'%s' % player].setEnabled(False) - else: - self.playerCheckBoxes[u'%s' % player].setEnabled(True) - self.playerOrderlistWidget.addItem( - self.mediaPlayers[unicode(player)].original_name) - - def onUpButtonClicked(self): - row = self.playerOrderlistWidget.currentRow() - if row <= 0: - return - item = self.playerOrderlistWidget.takeItem(row) - self.playerOrderlistWidget.insertItem(row - 1, item) - self.playerOrderlistWidget.setCurrentRow(row - 1) - self.usedPlayers.insert(row - 1, self.usedPlayers.pop(row)) - - def onDownButtonClicked(self): - row = self.playerOrderlistWidget.currentRow() - if row == -1 or row > self.playerOrderlistWidget.count() - 1: - return - item = self.playerOrderlistWidget.takeItem(row) - self.playerOrderlistWidget.insertItem(row + 1, item) - self.playerOrderlistWidget.setCurrentRow(row + 1) - self.usedPlayers.insert(row + 1, self.usedPlayers.pop(row)) + self.autoStartCheckBox.setText( + translate('MediaPlugin.MediaTab', + 'Start Live items automatically')) def load(self): - if self.savedUsedPlayers: - self.usedPlayers = self.savedUsedPlayers - self.usedPlayers = get_media_players()[0] - self.savedUsedPlayers = self.usedPlayers - for key in self.mediaPlayers: - player = self.mediaPlayers[key] - checkbox = self.playerCheckBoxes[player.name] - if player.available and player.name in self.usedPlayers: - checkbox.setChecked(True) - else: - checkbox.setChecked(False) - self.updatePlayerList() self.overridePlayerCheckBox.setChecked(Settings().value( self.settingsSection + u'/override player', QtCore.QVariant(QtCore.Qt.Unchecked)).toInt()[0]) + self.autoStartCheckBox.setChecked(Settings().value( + self.settingsSection + u'/media auto start', + QtCore.QVariant(QtCore.Qt.Unchecked)).toInt()[0]) def save(self): override_changed = False - player_string_changed = False - old_players, override_player = get_media_players() - if self.usedPlayers != old_players: - # clean old Media stuff - set_media_players(self.usedPlayers, override_player) - player_string_changed = True - override_changed = True setting_key = self.settingsSection + u'/override player' if Settings().value(setting_key).toInt()[0] != \ self.overridePlayerCheckBox.checkState(): Settings().setValue(setting_key, QtCore.QVariant(self.overridePlayerCheckBox.checkState())) override_changed = True + setting_key = self.settingsSection + u'/media auto start' + if Settings().value(setting_key).toInt()[0] !=\ + self.autoStartCheckBox.checkState(): + Settings().setValue(setting_key, + QtCore.QVariant(self.autoStartCheckBox.checkState())) if override_changed: + self.parent.resetSupportedSuffixes() Receiver.send_message(u'mediaitem_media_rebuild') - if player_string_changed: - Receiver.send_message(u'mediaitem_media_rebuild') - Receiver.send_message(u'config_screen_changed') + Receiver.send_message(u'mediaitem_suffixes') \ No newline at end of file diff --git a/openlp/plugins/media/mediaplugin.py b/openlp/plugins/media/mediaplugin.py index 381effd16..a143756b2 100644 --- a/openlp/plugins/media/mediaplugin.py +++ b/openlp/plugins/media/mediaplugin.py @@ -48,14 +48,6 @@ class MediaPlugin(Plugin): self.icon = build_icon(self.iconPath) # passed with drag and drop messages self.dnd_id = u'Media' - self.audio_extensions_list = \ - self.mediaController.get_audio_extensions_list() - for ext in self.audio_extensions_list: - self.serviceManager.supportedSuffixes(ext[2:]) - self.video_extensions_list = \ - self.mediaController.get_video_extensions_list() - for ext in self.video_extensions_list: - self.serviceManager.supportedSuffixes(ext[2:]) def createSettingsTab(self, parent): """ @@ -63,7 +55,7 @@ class MediaPlugin(Plugin): """ visible_name = self.getString(StringContent.VisibleName) self.settingsTab = MediaTab(parent, self.name, visible_name[u'title'], - self.mediaController.mediaPlayers, self.iconPath) + self.iconPath) def about(self): about_text = translate('MediaPlugin', 'Media Plugin' diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index b30efba46..47cc7ee82 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -34,7 +34,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import MediaManagerItem, build_icon, SettingsManager, \ translate, check_item_selected, Receiver, ItemCapabilities, create_thumb, \ - validate_thumb + validate_thumb, ServiceItemContext from openlp.core.lib.ui import UiStrings, critical_error_message_box, \ create_horizontal_adjusting_combo_box from openlp.core.lib.settings import Settings @@ -64,7 +64,10 @@ class PresentationMediaItem(MediaManagerItem): self.hasSearch = True self.singleServiceItem = False QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'mediaitem_presentation_rebuild'), self.rebuild) + QtCore.SIGNAL(u'mediaitem_presentation_rebuild'), + self.populateDisplayTypes) + QtCore.QObject.connect(Receiver.get_receiver(), + QtCore.SIGNAL(u'mediaitem_suffixes'), self.buildFileMaskString) # Allow DnD from the desktop self.listView.activateDnD() @@ -133,14 +136,6 @@ class PresentationMediaItem(MediaManagerItem): self.loadList(files, True) self.populateDisplayTypes() - def rebuild(self): - """ - Rebuild the tab in the media manager when changes are made in - the settings - """ - self.populateDisplayTypes() - self.buildFileMaskString() - def populateDisplayTypes(self): """ Load the combobox with the enabled presentation controllers, @@ -260,7 +255,7 @@ class PresentationMediaItem(MediaManagerItem): u'presentations', self.getFileList()) def generateSlideData(self, service_item, item=None, xmlVersion=False, - remote=False): + remote=False, context=ServiceItemContext.Service): """ Load the relevant information for displaying the presentation in the slidecontroller. In the case of powerpoints, an image diff --git a/openlp/plugins/presentations/lib/presentationtab.py b/openlp/plugins/presentations/lib/presentationtab.py index 0d0bf7b30..569b9ac69 100644 --- a/openlp/plugins/presentations/lib/presentationtab.py +++ b/openlp/plugins/presentations/lib/presentationtab.py @@ -41,6 +41,7 @@ class PresentationTab(SettingsTab): """ Constructor """ + self.parent = parent self.controllers = controllers SettingsTab.__init__(self, parent, title, visible_title, icon_path) self.activated = False @@ -142,7 +143,9 @@ class PresentationTab(SettingsTab): QtCore.QVariant(self.OverrideAppCheckBox.checkState())) changed = True if changed: + self.parent.resetSupportedSuffixes() Receiver.send_message(u'mediaitem_presentation_rebuild') + Receiver.send_message(u'mediaitem_suffixes') def tabVisible(self): """ diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 634346b62..c0996f7fc 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -183,8 +183,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): for plugin in self.parent().pluginManager.plugins: if plugin.name == u'media' and plugin.status == PluginStatus.Active: self.audioAddFromMediaButton.setVisible(True) - self.mediaForm.populateFiles( - plugin.mediaItem.getList(MediaType.Audio)) + self.mediaForm.populateFiles(plugin.mediaItem.getList(MediaType.Audio)) break def newSong(self): diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index c315a59bd..73943eb0d 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -37,7 +37,7 @@ from sqlalchemy.sql import or_ from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \ translate, check_item_selected, PluginStatus, create_separated_list, \ - check_directory_exists + check_directory_exists, ServiceItemContext from openlp.core.lib.ui import UiStrings, create_widget_action from openlp.core.lib.settings import Settings from openlp.core.utils import AppLocation, locale_direct_compare @@ -488,7 +488,7 @@ class SongMediaItem(MediaManagerItem): self.onSongListLoad() def generateSlideData(self, service_item, item=None, xmlVersion=False, - remote=False): + remote=False, context=ServiceItemContext.Service): log.debug(u'generateSlideData: %s, %s, %s' % (service_item, item, self.remoteSong)) item_id = self._getIdOfItemToGenerate(item, self.remoteSong) diff --git a/resources/openlp.xml b/resources/openlp.xml index 215fa2b6e..e8f31dc0d 100644 --- a/resources/openlp.xml +++ b/resources/openlp.xml @@ -17,6 +17,11 @@ Notes: OpenLP Service File + + + + OpenLP Service File +