29
29
from PyQt5 import QtCore, QtWidgets
31
31
from openlp.core.common import OpenLPMixin, Registry, RegistryMixin, RegistryProperties, Settings, UiStrings, translate
32
from openlp.core.lib import OpenLPToolbar, ItemCapabilities
32
from openlp.core.lib import ItemCapabilities
33
33
from openlp.core.lib.ui import critical_error_message_box
34
from openlp.core.common import AppLocation
35
from openlp.core.ui import DisplayControllerType
36
from openlp.core.ui.media.vendor.mediainfoWrapper import MediaInfoWrapper
37
from openlp.core.ui.media.mediaplayer import MediaPlayer
34
38
from openlp.core.ui.media import MediaState, MediaInfo, MediaType, get_media_players, set_media_players,\
36
from openlp.core.ui.media.mediaplayer import MediaPlayer
37
from openlp.core.common import AppLocation
38
from openlp.core.ui import DisplayControllerType
40
from openlp.core.ui.lib.toolbar import OpenLPToolbar
41
from openlp.core.ui.lib.dockwidget import OpenLPDockWidget
40
43
log = logging.getLogger(__name__)
43
48
class MediaSlider(QtWidgets.QSlider):
51
56
super(MediaSlider, self).__init__(direction)
52
57
self.manager = manager
53
58
self.controller = controller
59
self.no_matching_player = translate('MediaPlugin.MediaItem', 'File %s not supported using player %s')
55
61
def mouseMoveEvent(self, event):
57
63
Override event to allow hover time to be displayed.
65
:param event: The triggering event
59
67
time_value = QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width())
60
68
self.setToolTip('%s' % datetime.timedelta(seconds=int(time_value / 1000)))
63
71
def mousePressEvent(self, event):
65
73
Mouse Press event no new functionality
75
:param event: The triggering event
67
77
QtWidgets.QSlider.mousePressEvent(self, event)
69
79
def mouseReleaseEvent(self, event):
71
81
Set the slider position when the mouse is clicked and released on the slider.
83
:param event: The triggering event
73
85
self.setValue(QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width()))
74
86
QtWidgets.QSlider.mouseReleaseEvent(self, event)
96
108
self.display_controllers = {}
97
109
self.current_media_players = {}
98
110
# Timer for video state
99
self.timer = QtCore.QTimer()
100
self.timer.setInterval(200)
111
self.live_timer = QtCore.QTimer()
112
self.live_timer.setInterval(TICK_TIME)
113
self.preview_timer = QtCore.QTimer()
114
self.preview_timer.setInterval(TICK_TIME)
102
self.timer.timeout.connect(self.media_state)
116
self.live_timer.timeout.connect(self.media_state_live)
117
self.preview_timer.timeout.connect(self.media_state_preview)
103
118
Registry().register_function('playbackPlay', self.media_play_msg)
104
119
Registry().register_function('playbackPause', self.media_pause_msg)
105
120
Registry().register_function('playbackStop', self.media_stop_msg)
121
Registry().register_function('playbackLoop', self.media_loop_msg)
106
122
Registry().register_function('seek_slider', self.media_seek_msg)
107
123
Registry().register_function('volume_slider', self.media_volume_msg)
108
124
Registry().register_function('media_hide', self.media_hide)
172
188
log.warning('Failed to import %s on path %s', module_name, path)
173
189
player_classes = MediaPlayer.__subclasses__()
174
190
for player_class in player_classes:
175
player = player_class(self)
176
self.register_players(player)
191
self.register_players(player_class(self))
177
192
if not self.media_players:
179
194
saved_players, overridden_player = get_media_players()
188
203
self._generate_extensions_lists()
191
def media_state(self):
193
Check if there is a running media Player and do updating stuff (e.g. update the UI)
195
if not list(self.current_media_players.keys()):
199
for source in list(self.current_media_players.keys()):
200
display = self._define_display(self.display_controllers[source])
201
self.current_media_players[source].resize(display)
202
self.current_media_players[source].update_ui(display)
203
if self.current_media_players[source].state == MediaState.Playing:
205
# There are still any active players - no need to stop timer.
208
# no players are active anymore
209
for source in list(self.current_media_players.keys()):
210
if self.current_media_players[source].state != MediaState.Paused:
211
display = self._define_display(self.display_controllers[source])
212
display.controller.seek_slider.setSliderPosition(0)
213
display.controller.mediabar.actions['playbackPlay'].setVisible(True)
214
display.controller.mediabar.actions['playbackPause'].setVisible(False)
206
def media_state_live(self):
208
Check if there is a running Live media Player and do updating stuff (e.g. update the UI)
210
display = self._define_display(self.display_controllers[DisplayControllerType.Live])
211
if DisplayControllerType.Live in self.current_media_players:
212
self.current_media_players[DisplayControllerType.Live].resize(display)
213
self.current_media_players[DisplayControllerType.Live].update_ui(display)
214
self.tick(self.display_controllers[DisplayControllerType.Live])
215
if self.current_media_players[DisplayControllerType.Live].get_live_state() is not MediaState.Playing:
216
self.live_timer.stop()
218
self.live_timer.stop()
219
self.media_stop(self.display_controllers[DisplayControllerType.Live])
220
if self.display_controllers[DisplayControllerType.Live].media_info.can_loop_playback:
221
self.media_play(self.display_controllers[DisplayControllerType.Live], True)
223
def media_state_preview(self):
225
Check if there is a running Preview media Player and do updating stuff (e.g. update the UI)
227
display = self._define_display(self.display_controllers[DisplayControllerType.Preview])
228
if DisplayControllerType.Preview in self.current_media_players:
229
self.current_media_players[DisplayControllerType.Preview].resize(display)
230
self.current_media_players[DisplayControllerType.Preview].update_ui(display)
231
self.tick(self.display_controllers[DisplayControllerType.Preview])
232
if self.current_media_players[DisplayControllerType.Preview].get_preview_state() is not MediaState.Playing:
233
self.preview_timer.stop()
235
self.preview_timer.stop()
236
self.media_stop(self.display_controllers[DisplayControllerType.Preview])
237
if self.display_controllers[DisplayControllerType.Preview].media_info.can_loop_playback:
238
self.media_play(self.display_controllers[DisplayControllerType.Preview], True)
217
240
def get_media_display_css(self):
274
297
icon=':/slides/media_playback_stop.png',
275
298
tooltip=translate('OpenLP.SlideController', 'Stop playing media.'),
276
299
triggers=controller.send_to_plugins)
300
controller.mediabar.add_toolbar_action('playbackLoop', text='media_playback_loop',
301
icon=':/media/media_repeat.png', checked=False,
302
tooltip=translate('OpenLP.SlideController', 'Loop playing media.'),
303
triggers=controller.send_to_plugins)
304
controller.position_label = QtWidgets.QLabel()
305
controller.position_label.setText(' 00:00 / 00:00')
306
controller.position_label.setToolTip(translate('OpenLP.SlideController', 'Video timer.'))
307
controller.position_label.setObjectName('position_label')
308
controller.mediabar.add_toolbar_widget(controller.position_label)
277
309
# Build the seek_slider.
278
310
controller.seek_slider = MediaSlider(QtCore.Qt.Horizontal, self, controller)
279
311
controller.seek_slider.setMaximum(1000)
429
467
:param service_item: The ServiceItem containing the details to be played.
431
controller = self.display_controllers[DisplayControllerType.Plugin]
432
log.debug('media_length')
433
# stop running videos
434
self.media_reset(controller)
435
controller.media_info = MediaInfo()
436
controller.media_info.volume = 0
437
controller.media_info.file_info = QtCore.QFileInfo(service_item.get_frame_path())
438
display = controller.preview_display
439
if not self._check_file_type(controller, display, service_item):
469
media_info = MediaInfo()
470
media_info.volume = 0
471
media_info.file_info = QtCore.QFileInfo(service_item.get_frame_path())
472
# display = controller.preview_display
473
suffix = '*.%s' % media_info.file_info.suffix().lower()
474
used_players = get_media_players()[0]
475
player = self.media_players[used_players[0]]
476
if suffix not in player.video_extensions_list and suffix not in player.audio_extensions_list:
440
477
# Media could not be loaded correctly
441
critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'),
442
translate('MediaPlugin.MediaItem', 'Unsupported File'))
444
if not self.media_play(controller):
445
critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'),
446
translate('MediaPlugin.MediaItem', 'Unsupported File'))
448
service_item.set_media_length(controller.media_info.length)
449
self.media_stop(controller)
450
log.debug('use %s controller' % self.current_media_players[controller.controller_type])
478
critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported Media File'),
479
translate('MediaPlugin.MediaItem', 'File %s not supported using player %s') %
480
(service_item.get_frame_path(), used_players[0]))
482
media_data = MediaInfoWrapper.parse(service_item.get_frame_path())
483
# duration returns in milli seconds
484
service_item.set_media_length(media_data.tracks[0].duration)
453
487
def media_setup_optical(self, filename, title, audio_track, subtitle_track, start, end, display, controller):
458
492
:param title: The main/title track to play.
459
493
:param audio_track: The audio track to play.
460
494
:param subtitle_track: The subtitle track to play.
461
:param start: Start position in miliseconds.
462
:param end: End position in miliseconds.
495
:param start: Start position in milliseconds.
496
:param end: End position in milliseconds.
463
497
:param display: The display to play the media.
464
:param controller: The media contraoller.
465
:return: True if setup succeded else False.
498
:param controller: The media controller.
499
:return: True if setup succeeded else False.
467
log.debug('media_setup_optical')
468
501
if controller is None:
469
502
controller = self.display_controllers[DisplayControllerType.Plugin]
470
503
# stop running videos
476
509
controller.media_info.media_type = MediaType.CD
478
511
controller.media_info.media_type = MediaType.DVD
479
controller.media_info.start_time = start / 1000
480
controller.media_info.end_time = end / 1000
481
controller.media_info.length = (end - start) / 1000
512
controller.media_info.start_time = start // 1000
513
controller.media_info.end_time = end // 1000
514
controller.media_info.length = (end - start) // 1000
482
515
controller.media_info.title_track = title
483
516
controller.media_info.audio_track = audio_track
484
517
controller.media_info.subtitle_track = subtitle_track
506
539
controller.media_info.media_type = MediaType.DVD
509
def _check_file_type(self, controller, display, service_item):
543
def _get_used_players(service_item):
511
Select the correct media Player type from the prioritized Player list
545
Find the player for a given service item
513
:param controller: First element is the controller which should be used
514
:param display: Which display to use
515
:param service_item: The ServiceItem containing the details to be played.
547
:param service_item: where the information is about the media and required player
548
:return: player description
517
550
used_players = get_media_players()[0]
518
551
# If no player, we can't play
525
558
used_players = default_player
527
560
used_players = [service_item.processor.lower()]
563
def _check_file_type(self, controller, display, service_item):
565
Select the correct media Player type from the prioritized Player list
567
:param controller: First element is the controller which should be used
568
:param display: Which display to use
569
:param service_item: The ServiceItem containing the details to be played.
571
used_players = self._get_used_players(service_item)
528
572
if controller.media_info.file_info.isFile():
529
573
suffix = '*.%s' % controller.media_info.file_info.suffix().lower()
530
574
for title in used_players:
573
617
:param msg: First element is the controller which should be used
576
log.debug('media_play_msg')
577
620
self.media_play(msg[0], status)
579
def media_play(self, controller, status=True):
622
def media_play(self, controller, first_time=True):
581
624
Responds to the request to play a loaded video
583
626
:param controller: The controller to be played
586
log.debug('media_play')
587
629
controller.seek_slider.blockSignals(True)
588
630
controller.volume_slider.blockSignals(True)
589
631
display = self._define_display(controller)
595
637
self.media_volume(controller, 0)
597
639
self.media_volume(controller, controller.media_info.volume)
599
641
if not controller.media_info.is_background:
600
642
display.frame.evaluateJavaScript('show_blank("desktop");')
601
643
self.current_media_players[controller.controller_type].set_visible(display, True)
602
# Flash needs to be played and will not AutoPlay
603
if controller.media_info.is_flash:
604
controller.mediabar.actions['playbackPlay'].setVisible(True)
605
controller.mediabar.actions['playbackPause'].setVisible(False)
607
controller.mediabar.actions['playbackPlay'].setVisible(False)
608
controller.mediabar.actions['playbackPause'].setVisible(True)
644
controller.mediabar.actions['playbackPlay'].setVisible(False)
645
controller.mediabar.actions['playbackPause'].setVisible(True)
609
646
controller.mediabar.actions['playbackStop'].setDisabled(False)
610
if controller.is_live:
611
if controller.hide_menu.defaultAction().isChecked() and not controller.media_info.is_background:
612
controller.hide_menu.defaultAction().trigger()
613
# Start Timer for ui updates
614
if not self.timer.isActive():
647
if controller.is_live:
648
if controller.hide_menu.defaultAction().isChecked() and not controller.media_info.is_background:
649
controller.hide_menu.defaultAction().trigger()
650
# Start Timer for ui updates
651
if not self.live_timer.isActive():
652
self.live_timer.start()
654
# Start Timer for ui updates
655
if not self.preview_timer.isActive():
656
self.preview_timer.start()
616
657
controller.seek_slider.blockSignals(False)
617
658
controller.volume_slider.blockSignals(False)
659
controller.media_info.is_playing = True
660
display = self._define_display(controller)
661
display.setVisible(True)
664
def tick(self, controller):
666
Add a tick while the media is playing but only count if not paused
668
:param controller: The Controller to be processed
671
if controller.media_info.is_playing and controller.media_info.length > 0:
672
if controller.media_info.timer > controller.media_info.length:
673
self.media_stop(controller, True)
674
if controller.media_info.can_loop_playback:
676
controller.media_info.timer += TICK_TIME
677
seconds = controller.media_info.timer // 1000
678
minutes = seconds // 60
680
total_seconds = controller.media_info.length // 1000
681
total_minutes = total_seconds // 60
683
controller.position_label.setText(' %02d:%02d / %02d:%02d' %
684
(minutes, seconds, total_minutes, total_seconds))
686
self.media_play(controller, True)
620
688
def media_pause_msg(self, msg):
622
690
Responds to the request to pause a loaded video
624
692
:param msg: First element is the controller which should be used
626
log.debug('media_pause_msg')
627
694
self.media_pause(msg[0])
629
696
def media_pause(self, controller):
633
700
:param controller: The Controller to be paused
635
log.debug('media_pause')
636
702
display = self._define_display(controller)
637
self.current_media_players[controller.controller_type].pause(display)
638
controller.mediabar.actions['playbackPlay'].setVisible(True)
639
controller.mediabar.actions['playbackStop'].setDisabled(False)
640
controller.mediabar.actions['playbackPause'].setVisible(False)
703
if controller.controller_type in self.current_media_players:
704
self.current_media_players[controller.controller_type].pause(display)
705
controller.mediabar.actions['playbackPlay'].setVisible(True)
706
controller.mediabar.actions['playbackStop'].setDisabled(False)
707
controller.mediabar.actions['playbackPause'].setVisible(False)
708
controller.media_info.is_playing = False
710
def media_loop_msg(self, msg):
712
Responds to the request to loop a loaded video
714
:param msg: First element is the controller which should be used
716
self.media_loop(msg[0])
719
def media_loop(controller):
721
Responds to the request to loop a loaded video
723
:param controller: The controller that needs to be stopped
725
controller.media_info.can_loop_playback = not controller.media_info.can_loop_playback
726
controller.mediabar.actions['playbackLoop'].setChecked(controller.media_info.can_loop_playback)
642
728
def media_stop_msg(self, msg):
646
732
:param msg: First element is the controller which should be used
648
log.debug('media_stop_msg')
649
734
self.media_stop(msg[0])
651
def media_stop(self, controller):
736
def media_stop(self, controller, looping_background=False):
653
738
Responds to the request to stop a loaded video
655
740
:param controller: The controller that needs to be stopped
741
:param looping_background: The background is looping so do not blank.
657
log.debug('media_stop')
658
743
display = self._define_display(controller)
659
744
if controller.controller_type in self.current_media_players:
660
display.frame.evaluateJavaScript('show_blank("black");')
745
if not looping_background:
746
display.frame.evaluateJavaScript('show_blank("black");')
661
747
self.current_media_players[controller.controller_type].stop(display)
662
748
self.current_media_players[controller.controller_type].set_visible(display, False)
663
749
controller.seek_slider.setSliderPosition(0)
664
750
controller.mediabar.actions['playbackPlay'].setVisible(True)
665
751
controller.mediabar.actions['playbackStop'].setDisabled(True)
666
752
controller.mediabar.actions['playbackPause'].setVisible(False)
753
controller.media_info.is_playing = False
754
controller.media_info.timer = 1000
755
controller.media_timer = 0
668
757
def media_volume_msg(self, msg):
706
794
:param controller: The controller to use.
707
795
:param seek_value: The value to set.
709
log.debug('media_seek')
710
797
display = self._define_display(controller)
711
798
self.current_media_players[controller.controller_type].seek(display, seek_value)
799
controller.media_info.timer = seek_value
713
801
def media_reset(self, controller):
715
803
Responds to the request to reset a loaded video
804
:param controller: The controller to use.
717
log.debug('media_reset')
718
806
self.set_controls_visible(controller, False)
719
807
display = self._define_display(controller)
720
808
if controller.controller_type in self.current_media_players:
753
841
Registry().execute('live_display_hide', hide_mode)
754
842
display = self._define_display(self.live_controller)
755
843
if self.live_controller.controller_type in self.current_media_players and \
756
self.current_media_players[self.live_controller.controller_type].state == MediaState.Playing:
844
self.current_media_players[self.live_controller.controller_type].get_live_state() == MediaState.Playing:
757
845
self.current_media_players[self.live_controller.controller_type].pause(display)
758
846
self.current_media_players[self.live_controller.controller_type].set_visible(display, False)
771
859
display = self._define_display(self.live_controller)
772
860
if self.live_controller.controller_type in self.current_media_players and \
773
self.current_media_players[self.live_controller.controller_type].state != MediaState.Playing:
861
self.current_media_players[self.live_controller.controller_type].get_live_state() != \
774
863
if self.current_media_players[self.live_controller.controller_type].play(display):
775
864
self.current_media_players[self.live_controller.controller_type].set_visible(display, True)
776
865
# Start Timer for ui updates
777
if not self.timer.isActive():
866
if not self.live_timer.isActive():
867
self.live_timer.start()
780
869
def finalise(self):
782
871
Reset all the media controllers when OpenLP shuts down
873
self.live_timer.stop()
874
self.preview_timer.stop()
785
875
for controller in self.display_controllers:
786
876
self.media_reset(self.display_controllers[controller])
788
def _define_display(self, controller):
879
def _define_display(controller):
790
881
Extract the correct display for a given controller