23
23
The :mod:`~openlp.core.ui.media.mediacontroller` module contains a base class for media components and other widgets
24
24
related to playing media, such as sliders.
30
from PyQt5 import QtCore, QtWidgets
29
from pymediainfo import MediaInfo
30
pymediainfo_available = True
32
pymediainfo_available = False
34
from subprocess import check_output
35
from PyQt5 import QtCore
37
from openlp.core.state import State
32
38
from openlp.core.api.http import register_endpoint
33
from openlp.core.common import extension_loader
34
from openlp.core.common.i18n import UiStrings, translate
39
from openlp.core.common.i18n import translate
35
40
from openlp.core.common.mixins import LogMixin, RegistryProperties
36
41
from openlp.core.common.registry import Registry, RegistryBase
37
42
from openlp.core.common.settings import Settings
38
from openlp.core.lib import ItemCapabilities
43
from openlp.core.lib.serviceitem import ItemCapabilities
39
44
from openlp.core.lib.ui import critical_error_message_box
40
45
from openlp.core.ui import DisplayControllerType
41
from openlp.core.ui.icons import UiIcons
42
from openlp.core.ui.media import MediaState, MediaInfo, MediaType, get_media_players, set_media_players, \
46
from openlp.core.ui.media import MediaState, ItemMediaInfo, MediaType, parse_optical_path
44
47
from openlp.core.ui.media.endpoint import media_endpoint
45
from openlp.core.ui.media.mediaplayer import MediaPlayer
46
from openlp.core.ui.media.vendor.mediainfoWrapper import MediaInfoWrapper
47
from openlp.core.widgets.toolbar import OpenLPToolbar
48
from openlp.core.ui.media.vlcplayer import VlcPlayer, get_vlc
49
51
log = logging.getLogger(__name__)
54
class MediaSlider(QtWidgets.QSlider):
56
Allows the mouse events of a slider to be overridden and extra functionality added
58
def __init__(self, direction, manager, controller):
62
super(MediaSlider, self).__init__(direction)
63
self.manager = manager
64
self.controller = controller
65
self.no_matching_player = translate('MediaPlugin.MediaItem', 'File %s not supported using player %s')
67
def mouseMoveEvent(self, event):
69
Override event to allow hover time to be displayed.
71
:param event: The triggering event
73
time_value = QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width())
74
self.setToolTip('%s' % datetime.timedelta(seconds=int(time_value / 1000)))
75
QtWidgets.QSlider.mouseMoveEvent(self, event)
77
def mousePressEvent(self, event):
79
Mouse Press event no new functionality
81
:param event: The triggering event
83
QtWidgets.QSlider.mousePressEvent(self, event)
85
def mouseReleaseEvent(self, event):
87
Set the slider position when the mouse is clicked and released on the slider.
89
:param event: The triggering event
91
self.setValue(QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width()))
92
QtWidgets.QSlider.mouseReleaseEvent(self, event)
95
56
class MediaController(RegistryBase, LogMixin, RegistryProperties):
97
58
The implementation of the Media Controller. The Media Controller adds an own class for every Player.
134
96
Registry().register_function('songs_hide', self.media_hide)
135
97
Registry().register_function('songs_blank', self.media_blank)
136
98
Registry().register_function('songs_unblank', self.media_unblank)
137
Registry().register_function('mediaitem_media_rebuild', self._set_active_players)
138
99
Registry().register_function('mediaitem_suffixes', self._generate_extensions_lists)
139
100
register_endpoint(media_endpoint)
141
def _set_active_players(self):
143
Set the active players and available media files
145
saved_players = get_media_players()[0]
146
for player in list(self.media_players.keys()):
147
self.media_players[player].is_active = player in saved_players
149
102
def _generate_extensions_lists(self):
151
104
Set the active players and available media files
154
107
self.audio_extensions_list = []
155
for player in list(self.media_players.values()):
157
for item in player.audio_extensions_list:
158
if item not in self.audio_extensions_list:
159
self.audio_extensions_list.append(item)
160
suffix_list.append(item[2:])
108
if self.vlc_player.is_active:
109
for item in self.vlc_player.audio_extensions_list:
110
if item not in self.audio_extensions_list:
111
self.audio_extensions_list.append(item)
112
suffix_list.append(item[2:])
161
113
self.video_extensions_list = []
162
for player in list(self.media_players.values()):
164
for item in player.video_extensions_list:
165
if item not in self.video_extensions_list:
166
self.video_extensions_list.append(item)
167
suffix_list.append(item[2:])
114
if self.vlc_player.is_active:
115
for item in self.vlc_player.video_extensions_list:
116
if item not in self.video_extensions_list:
117
self.video_extensions_list.append(item)
118
suffix_list.append(item[2:])
168
119
self.service_manager.supported_suffixes(suffix_list)
170
def register_players(self, player):
172
Register each media Player (Webkit, Phonon, etc) and store
175
:param player: Individual player class which has been enabled
177
self.media_players[player.name] = player
179
121
def bootstrap_initialise(self):
181
123
Check to see if we have any media Player's available.
183
controller_dir = os.path.join('core', 'ui', 'media')
184
# Find all files that do not begin with '.' (lp:#1738047) and end with player.py
185
glob_pattern = os.path.join(controller_dir, '[!.]*player.py')
186
extension_loader(glob_pattern, ['mediaplayer.py'])
187
player_classes = MediaPlayer.__subclasses__()
188
for player_class in player_classes:
189
self.register_players(player_class(self))
190
if not self.media_players:
192
saved_players, overridden_player = get_media_players()
193
invalid_media_players = \
194
[media_player for media_player in saved_players if media_player not in self.media_players or
195
not self.media_players[media_player].check_available()]
196
if invalid_media_players:
197
for invalidPlayer in invalid_media_players:
198
saved_players.remove(invalidPlayer)
199
set_media_players(saved_players, overridden_player)
200
self._set_active_players()
126
self.vlc_player = VlcPlayer(self)
127
State().add_service("mediacontroller", 0)
128
State().add_service("media_live", 0, requires="mediacontroller")
129
if get_vlc() and pymediainfo_available:
130
State().update_pre_conditions("mediacontroller", True)
131
State().update_pre_conditions('media_live', True)
133
State().missing_text("mediacontroller", translate('OpenLP.SlideController',
134
"VLC or pymediainfo are missing, so you are unable to play any media"))
201
135
self._generate_extensions_lists()
138
def bootstrap_post_set_up(self):
140
Set up the controllers.
144
self.setup_display(self.live_controller.display, False)
145
except AttributeError:
146
State().update_pre_conditions('media_live', False)
147
self.setup_display(self.preview_controller.preview_display, True)
149
def display_controllers(self, controller_type):
151
Decides which controller to use.
153
:param controller_type: The controller type where a player will be placed
155
if controller_type == DisplayControllerType.Live:
156
return self.live_controller
158
return self.preview_controller
204
160
def media_state_live(self):
206
162
Check if there is a running Live media Player and do updating stuff (e.g. update the UI)
208
display = self._define_display(self.display_controllers[DisplayControllerType.Live])
164
display = self._define_display(self.display_controllers(DisplayControllerType.Live))
209
165
if DisplayControllerType.Live in self.current_media_players:
210
166
self.current_media_players[DisplayControllerType.Live].resize(display)
211
self.current_media_players[DisplayControllerType.Live].update_ui(display)
212
self.tick(self.display_controllers[DisplayControllerType.Live])
167
self.current_media_players[DisplayControllerType.Live].update_ui(self.live_controller, display)
168
self.tick(self.display_controllers(DisplayControllerType.Live))
213
169
if self.current_media_players[DisplayControllerType.Live].get_live_state() is not MediaState.Playing:
214
170
self.live_timer.stop()
216
172
self.live_timer.stop()
217
self.media_stop(self.display_controllers[DisplayControllerType.Live])
218
if self.display_controllers[DisplayControllerType.Live].media_info.can_loop_playback:
219
self.media_play(self.display_controllers[DisplayControllerType.Live], True)
173
self.media_stop(self.display_controllers(DisplayControllerType.Live))
174
if self.display_controllers(DisplayControllerType.Live).media_info.can_loop_playback:
175
self.media_play(self.display_controllers(DisplayControllerType.Live), True)
221
177
def media_state_preview(self):
223
179
Check if there is a running Preview media Player and do updating stuff (e.g. update the UI)
225
display = self._define_display(self.display_controllers[DisplayControllerType.Preview])
181
display = self._define_display(self.display_controllers(DisplayControllerType.Preview))
226
182
if DisplayControllerType.Preview in self.current_media_players:
227
183
self.current_media_players[DisplayControllerType.Preview].resize(display)
228
self.current_media_players[DisplayControllerType.Preview].update_ui(display)
229
self.tick(self.display_controllers[DisplayControllerType.Preview])
184
self.current_media_players[DisplayControllerType.Preview].update_ui(self.preview_controller, display)
185
self.tick(self.display_controllers(DisplayControllerType.Preview))
230
186
if self.current_media_players[DisplayControllerType.Preview].get_preview_state() is not MediaState.Playing:
231
187
self.preview_timer.stop()
233
189
self.preview_timer.stop()
234
self.media_stop(self.display_controllers[DisplayControllerType.Preview])
235
if self.display_controllers[DisplayControllerType.Preview].media_info.can_loop_playback:
236
self.media_play(self.display_controllers[DisplayControllerType.Preview], True)
238
def get_media_display_css(self):
240
Add css style sheets to htmlbuilder
243
for player in list(self.media_players.values()):
245
css += player.get_media_display_css()
248
def get_media_display_javascript(self):
250
Add javascript functions to htmlbuilder
253
for player in list(self.media_players.values()):
255
js += player.get_media_display_javascript()
258
def get_media_display_html(self):
260
Add html code to htmlbuilder
263
for player in list(self.media_players.values()):
265
html += player.get_media_display_html()
268
def register_controller(self, controller):
270
Registers media controls where the players will be placed to run.
272
:param controller: The controller where a player will be placed
274
self.display_controllers[controller.controller_type] = controller
275
self.setup_generic_controls(controller)
277
def setup_generic_controls(self, controller):
279
Set up controls on the control_panel for a given controller
281
:param controller: First element is the controller which should be used
283
controller.media_info = MediaInfo()
284
# Build a Media ToolBar
285
controller.mediabar = OpenLPToolbar(controller)
286
controller.mediabar.add_toolbar_action('playbackPlay', text='media_playback_play',
288
tooltip=translate('OpenLP.SlideController', 'Start playing media.'),
289
triggers=controller.send_to_plugins)
290
controller.mediabar.add_toolbar_action('playbackPause', text='media_playback_pause',
291
icon=UiIcons().pause,
292
tooltip=translate('OpenLP.SlideController', 'Pause playing media.'),
293
triggers=controller.send_to_plugins)
294
controller.mediabar.add_toolbar_action('playbackStop', text='media_playback_stop',
296
tooltip=translate('OpenLP.SlideController', 'Stop playing media.'),
297
triggers=controller.send_to_plugins)
298
controller.mediabar.add_toolbar_action('playbackLoop', text='media_playback_loop',
299
icon=UiIcons().repeat, checked=False,
300
tooltip=translate('OpenLP.SlideController', 'Loop playing media.'),
301
triggers=controller.send_to_plugins)
302
controller.position_label = QtWidgets.QLabel()
303
controller.position_label.setText(' 00:00 / 00:00')
304
controller.position_label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
305
controller.position_label.setToolTip(translate('OpenLP.SlideController', 'Video timer.'))
306
controller.position_label.setMinimumSize(90, 0)
307
controller.position_label.setObjectName('position_label')
308
controller.mediabar.add_toolbar_widget(controller.position_label)
309
# Build the seek_slider.
310
controller.seek_slider = MediaSlider(QtCore.Qt.Horizontal, self, controller)
311
controller.seek_slider.setMaximum(1000)
312
controller.seek_slider.setTracking(True)
313
controller.seek_slider.setMouseTracking(True)
314
controller.seek_slider.setToolTip(translate('OpenLP.SlideController', 'Video position.'))
315
controller.seek_slider.setGeometry(QtCore.QRect(90, 260, 221, 24))
316
controller.seek_slider.setObjectName('seek_slider')
317
controller.mediabar.add_toolbar_widget(controller.seek_slider)
318
# Build the volume_slider.
319
controller.volume_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
320
controller.volume_slider.setTickInterval(10)
321
controller.volume_slider.setTickPosition(QtWidgets.QSlider.TicksAbove)
322
controller.volume_slider.setMinimum(0)
323
controller.volume_slider.setMaximum(100)
324
controller.volume_slider.setTracking(True)
325
controller.volume_slider.setToolTip(translate('OpenLP.SlideController', 'Audio Volume.'))
326
controller.volume_slider.setValue(controller.media_info.volume)
327
controller.volume_slider.setGeometry(QtCore.QRect(90, 160, 221, 24))
328
controller.volume_slider.setObjectName('volume_slider')
329
controller.mediabar.add_toolbar_widget(controller.volume_slider)
330
controller.controller_layout.addWidget(controller.mediabar)
331
controller.mediabar.setVisible(False)
332
if not controller.is_live:
333
controller.volume_slider.setEnabled(False)
335
controller.seek_slider.valueChanged.connect(controller.send_to_plugins)
336
controller.volume_slider.valueChanged.connect(controller.send_to_plugins)
190
self.media_stop(self.display_controllers(DisplayControllerType.Preview))
191
if self.display_controllers(DisplayControllerType.Preview).media_info.can_loop_playback:
192
self.media_play(self.display_controllers(DisplayControllerType.Preview), True)
338
194
def setup_display(self, display, preview):
507
362
# When called from mediaitem display is None
508
363
if display is None:
509
364
display = controller.preview_display
511
used_players = get_media_players()[0]
513
for title in used_players:
514
player = self.media_players[title]
515
if player.name == 'vlc':
517
if vlc_player is None:
518
critical_error_message_box(translate('MediaPlugin.MediaItem', 'VLC player required'),
519
translate('MediaPlugin.MediaItem',
520
'VLC player required for playback of optical devices'))
522
vlc_player.load(display)
523
self.resize(display, vlc_player)
524
self.current_media_players[controller.controller_type] = vlc_player
365
self.vlc_player.load(display, filename)
366
self.resize(display, self.vlc_player)
367
self.current_media_players[controller.controller_type] = self.vlc_player
525
368
if audio_track == -1 and subtitle_track == -1:
526
369
controller.media_info.media_type = MediaType.CD
528
371
controller.media_info.media_type = MediaType.DVD
532
def _get_used_players(service_item):
534
Find the player for a given service item
536
:param service_item: where the information is about the media and required player
537
:return: player description
539
used_players = get_media_players()[0]
540
# If no player, we can't play
543
default_player = [used_players[0]]
544
if service_item.processor and service_item.processor != UiStrings().Automatic:
545
# check to see if the player is usable else use the default one.
546
if service_item.processor.lower() not in used_players:
547
used_players = default_player
549
used_players = [service_item.processor.lower()]
552
def _check_file_type(self, controller, display, service_item):
374
def _check_file_type(self, controller, display):
554
376
Select the correct media Player type from the prioritized Player list
556
378
:param controller: First element is the controller which should be used
557
379
:param display: Which display to use
558
:param service_item: The ServiceItem containing the details to be played.
560
used_players = self._get_used_players(service_item)
561
if controller.media_info.file_info.isFile():
562
suffix = '*.%s' % controller.media_info.file_info.suffix().lower()
563
for title in used_players:
566
player = self.media_players[title]
567
if suffix in player.video_extensions_list:
381
for file in controller.media_info.file_info:
383
suffix = '*%s' % file.suffix.lower()
385
if suffix in self.vlc_player.video_extensions_list:
568
386
if not controller.media_info.is_background or controller.media_info.is_background and \
569
player.can_background:
570
self.resize(display, player)
571
if player.load(display):
572
self.current_media_players[controller.controller_type] = player
387
self.vlc_player.can_background:
388
self.resize(display, self.vlc_player)
389
if self.vlc_player.load(display, file):
390
self.current_media_players[controller.controller_type] = self.vlc_player
573
391
controller.media_info.media_type = MediaType.Video
575
if suffix in player.audio_extensions_list:
576
if player.load(display):
577
self.current_media_players[controller.controller_type] = player
393
if suffix in self.vlc_player.audio_extensions_list:
394
if self.vlc_player.load(display, file):
395
self.current_media_players[controller.controller_type] = self.vlc_player
578
396
controller.media_info.media_type = MediaType.Audio
581
for title in used_players:
582
player = self.media_players[title]
583
if player.can_folder:
584
self.resize(display, player)
585
if player.load(display):
586
self.current_media_players[controller.controller_type] = player
400
if self.vlc_player.can_folder:
401
self.resize(display, self.vlc_player)
402
if self.vlc_player.load(display, file):
403
self.current_media_players[controller.controller_type] = self.vlc_player
587
404
controller.media_info.media_type = MediaType.Video
589
# no valid player found
592
408
def media_play_msg(self, msg, status=True):