Merge lp:~trb143/openlp/media_state into lp:openlp

Proposed by Tim Bentley
Status: Merged
Approved by: Raoul Snyman
Approved revision: 2936
Merged at revision: 2857
Proposed branch: lp:~trb143/openlp/media_state
Merge into: lp:openlp
Diff against target: 3097 lines (+560/-1663)
22 files modified
.bzrignore (+1/-0)
openlp/core/common/settings.py (+10/-4)
openlp/core/lib/theme.py (+5/-0)
openlp/core/ui/media/__init__.py (+1/-0)
openlp/core/ui/media/mediacontroller.py (+71/-169)
openlp/core/ui/media/mediaplayer.py (+5/-3)
openlp/core/ui/media/mediatab.py (+148/-0)
openlp/core/ui/media/playertab.py (+0/-269)
openlp/core/ui/media/systemplayer.py (+0/-332)
openlp/core/ui/media/vlcplayer.py (+115/-114)
openlp/core/ui/screenstab.py (+1/-1)
openlp/core/ui/settingsform.py (+8/-5)
openlp/core/ui/slidecontroller.py (+97/-8)
openlp/core/ui/themeform.py (+2/-0)
openlp/core/ui/themewizard.py (+3/-1)
openlp/core/widgets/views.py (+4/-1)
openlp/plugins/media/lib/mediaitem.py (+4/-1)
openlp/plugins/media/lib/mediatab.py (+0/-73)
openlp/plugins/media/mediaplugin.py (+0/-13)
tests/functional/openlp_core/common/test_common.py (+2/-2)
tests/functional/openlp_core/ui/media/test_systemplayer.py (+0/-567)
tests/functional/openlp_core/ui/media/test_vlcplayer.py (+83/-100)
To merge this branch: bzr merge lp:~trb143/openlp/media_state
Reviewer Review Type Date Requested Status
Raoul Snyman Approve
Review via email: mp+365879@code.launchpad.net

Commit message

VLC plays and handles missing live display gracefully. Preview still works.
Added experimental setting to remove UI incomplete stuff.
various bug fixes and improvments,

Description of the change

New merge request as being inconsistent.

To post a comment you must log in.
Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

Linux tests passed!

Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

Linting passed!

Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

macOS tests passed!

Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

Overall this looks good. There is some commented code that I think either need to be fixed or removed? Also, I noticed there's some code that deals with streaming on Windows and Linux, which I presume you'll need the macOS equivalent for. Is there any other code that you need the macOS equivalent for?

review: Needs Information
Revision history for this message
Tim Bentley (trb143) wrote :

This is work in progress and needs 1 more merge to finish.
The commented our code is not needed and needs to be cleaned up but want to keep till I finish.
The stream code is the next set and I have infor for linux and windows but not mac. Did ask the mailing list and only tgc responded.

Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

Some scrubbing on the nets found this:

  avcapture://

Revision history for this message
Raoul Snyman (raoul-snyman) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2019-03-04 20:37:11 +0000
3+++ .bzrignore 2019-04-11 20:30:04 +0000
4@@ -15,6 +15,7 @@
5 *.e4*
6 *eric[1-9]project
7 .git
8+env
9 # Git files
10 .gitignore
11 htmlcov
12
13=== modified file 'openlp/core/common/settings.py'
14--- openlp/core/common/settings.py 2019-03-03 13:32:31 +0000
15+++ openlp/core/common/settings.py 2019-04-11 20:30:04 +0000
16@@ -129,6 +129,9 @@
17 ``advanced/slide limits`` to ``SlideLimits.Wrap``. **NOTE**, this means that the rules have to cover all cases!
18 So, if the type of the old value is bool, then there must be two rules.
19 """
20+ on_monitor_default = True
21+ if log.isEnabledFor(logging.DEBUG):
22+ on_monitor_default = False
23 __default_settings__ = {
24 'settings/version': 0,
25 'advanced/add page break': False,
26@@ -185,6 +188,7 @@
27 'core/click live slide to unblank': False,
28 'core/blank warning': False,
29 'core/ccli number': '',
30+ 'core/experimental': False,
31 'core/has run wizard': False,
32 'core/language': '[en]',
33 'core/last version test': '',
34@@ -202,13 +206,13 @@
35 'core/view mode': 'default',
36 # The other display settings (display position and dimensions) are defined in the ScreenList class due to a
37 # circular dependency.
38- 'core/display on monitor': True,
39+ 'core/display on monitor': on_monitor_default,
40 'core/override position': False,
41 'core/monitor': {},
42 'core/application version': '0.0',
43 'images/background color': '#000000',
44- 'media/players': 'system,webkit',
45- 'media/override player': QtCore.Qt.Unchecked,
46+ 'media/media auto start': QtCore.Qt.Unchecked,
47+ 'media/stream command': '',
48 'remotes/download version': '0.0',
49 'players/background color': '#000000',
50 'servicemanager/last directory': None,
51@@ -311,7 +315,9 @@
52 ('bibles/proxy name', '', []), # Just remove these bible proxy settings. They weren't used in 2.4!
53 ('bibles/proxy address', '', []),
54 ('bibles/proxy username', '', []),
55- ('bibles/proxy password', '', [])
56+ ('bibles/proxy password', '', []),
57+ ('media/players', '', []),
58+ ('media/override player', '', [])
59 ]
60
61 @staticmethod
62
63=== modified file 'openlp/core/lib/theme.py'
64--- openlp/core/lib/theme.py 2019-02-14 15:09:09 +0000
65+++ openlp/core/lib/theme.py 2019-04-11 20:30:04 +0000
66@@ -46,6 +46,7 @@
67 Image = 2
68 Transparent = 3
69 Video = 4
70+ Stream = 5
71
72 @staticmethod
73 def to_string(background_type):
74@@ -62,6 +63,8 @@
75 return 'transparent'
76 elif background_type == BackgroundType.Video:
77 return 'video'
78+ elif background_type == BackgroundType.Stream:
79+ return 'stream'
80
81 @staticmethod
82 def from_string(type_string):
83@@ -78,6 +81,8 @@
84 return BackgroundType.Transparent
85 elif type_string == 'video':
86 return BackgroundType.Video
87+ elif type_string == 'stream':
88+ return BackgroundType.Stream
89
90
91 class BackgroundGradientType(object):
92
93=== modified file 'openlp/core/ui/media/__init__.py'
94--- openlp/core/ui/media/__init__.py 2019-03-17 10:01:52 +0000
95+++ openlp/core/ui/media/__init__.py 2019-04-11 20:30:04 +0000
96@@ -48,6 +48,7 @@
97 CD = 3
98 DVD = 4
99 Folder = 5
100+ Stream = 6
101
102
103 class ItemMediaInfo(object):
104
105=== modified file 'openlp/core/ui/media/mediacontroller.py'
106--- openlp/core/ui/media/mediacontroller.py 2019-02-14 15:09:09 +0000
107+++ openlp/core/ui/media/mediacontroller.py 2019-04-11 20:30:04 +0000
108@@ -23,7 +23,6 @@
109 The :mod:`~openlp.core.ui.media.mediacontroller` module contains a base class for media components and other widgets
110 related to playing media, such as sliders.
111 """
112-import datetime
113 import logging
114
115 try:
116@@ -33,7 +32,7 @@
117 pymediainfo_available = False
118
119 from subprocess import check_output
120-from PyQt5 import QtCore, QtWidgets
121+from PyQt5 import QtCore
122
123 from openlp.core.state import State
124 from openlp.core.api.http import register_endpoint
125@@ -44,11 +43,9 @@
126 from openlp.core.lib.serviceitem import ItemCapabilities
127 from openlp.core.lib.ui import critical_error_message_box
128 from openlp.core.ui import DisplayControllerType
129-from openlp.core.ui.icons import UiIcons
130 from openlp.core.ui.media import MediaState, ItemMediaInfo, MediaType, parse_optical_path
131 from openlp.core.ui.media.endpoint import media_endpoint
132 from openlp.core.ui.media.vlcplayer import VlcPlayer, get_vlc
133-from openlp.core.widgets.toolbar import OpenLPToolbar
134
135
136 log = logging.getLogger(__name__)
137@@ -56,45 +53,6 @@
138 TICK_TIME = 200
139
140
141-class MediaSlider(QtWidgets.QSlider):
142- """
143- Allows the mouse events of a slider to be overridden and extra functionality added
144- """
145- def __init__(self, direction, manager, controller):
146- """
147- Constructor
148- """
149- super(MediaSlider, self).__init__(direction)
150- self.manager = manager
151- self.controller = controller
152-
153- def mouseMoveEvent(self, event):
154- """
155- Override event to allow hover time to be displayed.
156-
157- :param event: The triggering event
158- """
159- time_value = QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width())
160- self.setToolTip('%s' % datetime.timedelta(seconds=int(time_value / 1000)))
161- QtWidgets.QSlider.mouseMoveEvent(self, event)
162-
163- def mousePressEvent(self, event):
164- """
165- Mouse Press event no new functionality
166- :param event: The triggering event
167- """
168- QtWidgets.QSlider.mousePressEvent(self, event)
169-
170- def mouseReleaseEvent(self, event):
171- """
172- Set the slider position when the mouse is clicked and released on the slider.
173-
174- :param event: The triggering event
175- """
176- self.setValue(QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width()))
177- QtWidgets.QSlider.mouseReleaseEvent(self, event)
178-
179-
180 class MediaController(RegistryBase, LogMixin, RegistryProperties):
181 """
182 The implementation of the Media Controller. The Media Controller adds an own class for every Player.
183@@ -116,7 +74,6 @@
184
185 def setup(self):
186 self.vlc_player = None
187- self.display_controllers = {}
188 self.current_media_players = {}
189 # Timer for video state
190 self.live_timer = QtCore.QTimer()
191@@ -168,117 +125,71 @@
192 self.setup()
193 self.vlc_player = VlcPlayer(self)
194 State().add_service("mediacontroller", 0)
195+ State().add_service("media_live", 0, requires="mediacontroller")
196 if get_vlc() and pymediainfo_available:
197 State().update_pre_conditions("mediacontroller", True)
198+ State().update_pre_conditions('media_live', True)
199 else:
200 State().missing_text("mediacontroller", translate('OpenLP.SlideController',
201 "VLC or pymediainfo are missing, so you are unable to play any media"))
202 self._generate_extensions_lists()
203 return True
204
205+ def bootstrap_post_set_up(self):
206+ """
207+ Set up the controllers.
208+ :return:
209+ """
210+ try:
211+ self.setup_display(self.live_controller.display, False)
212+ except AttributeError:
213+ State().update_pre_conditions('media_live', False)
214+ self.setup_display(self.preview_controller.preview_display, True)
215+
216+ def display_controllers(self, controller_type):
217+ """
218+ Decides which controller to use.
219+
220+ :param controller_type: The controller type where a player will be placed
221+ """
222+ if controller_type == DisplayControllerType.Live:
223+ return self.live_controller
224+ else:
225+ return self.preview_controller
226+
227 def media_state_live(self):
228 """
229 Check if there is a running Live media Player and do updating stuff (e.g. update the UI)
230 """
231- display = self._define_display(self.display_controllers[DisplayControllerType.Live])
232+ display = self._define_display(self.display_controllers(DisplayControllerType.Live))
233 if DisplayControllerType.Live in self.current_media_players:
234 self.current_media_players[DisplayControllerType.Live].resize(display)
235- self.current_media_players[DisplayControllerType.Live].update_ui(display)
236- self.tick(self.display_controllers[DisplayControllerType.Live])
237+ self.current_media_players[DisplayControllerType.Live].update_ui(self.live_controller, display)
238+ self.tick(self.display_controllers(DisplayControllerType.Live))
239 if self.current_media_players[DisplayControllerType.Live].get_live_state() is not MediaState.Playing:
240 self.live_timer.stop()
241 else:
242 self.live_timer.stop()
243- self.media_stop(self.display_controllers[DisplayControllerType.Live])
244- if self.display_controllers[DisplayControllerType.Live].media_info.can_loop_playback:
245- self.media_play(self.display_controllers[DisplayControllerType.Live], True)
246+ self.media_stop(self.display_controllers(DisplayControllerType.Live))
247+ if self.display_controllers(DisplayControllerType.Live).media_info.can_loop_playback:
248+ self.media_play(self.display_controllers(DisplayControllerType.Live), True)
249
250 def media_state_preview(self):
251 """
252 Check if there is a running Preview media Player and do updating stuff (e.g. update the UI)
253 """
254- display = self._define_display(self.display_controllers[DisplayControllerType.Preview])
255+ display = self._define_display(self.display_controllers(DisplayControllerType.Preview))
256 if DisplayControllerType.Preview in self.current_media_players:
257 self.current_media_players[DisplayControllerType.Preview].resize(display)
258- self.current_media_players[DisplayControllerType.Preview].update_ui(display)
259- self.tick(self.display_controllers[DisplayControllerType.Preview])
260+ self.current_media_players[DisplayControllerType.Preview].update_ui(self.preview_controller, display)
261+ self.tick(self.display_controllers(DisplayControllerType.Preview))
262 if self.current_media_players[DisplayControllerType.Preview].get_preview_state() is not MediaState.Playing:
263 self.preview_timer.stop()
264 else:
265 self.preview_timer.stop()
266- self.media_stop(self.display_controllers[DisplayControllerType.Preview])
267- if self.display_controllers[DisplayControllerType.Preview].media_info.can_loop_playback:
268- self.media_play(self.display_controllers[DisplayControllerType.Preview], True)
269-
270- def register_controller(self, controller):
271- """
272- Registers media controls where the players will be placed to run.
273-
274- :param controller: The controller where a player will be placed
275- """
276- self.display_controllers[controller.controller_type] = controller
277- self.setup_generic_controls(controller)
278-
279- def setup_generic_controls(self, controller):
280- """
281- Set up controls on the control_panel for a given controller
282-
283- :param controller: First element is the controller which should be used
284- """
285- controller.media_info = ItemMediaInfo()
286- # Build a Media ToolBar
287- controller.mediabar = OpenLPToolbar(controller)
288- controller.mediabar.add_toolbar_action('playbackPlay', text='media_playback_play',
289- icon=UiIcons().play,
290- tooltip=translate('OpenLP.SlideController', 'Start playing media.'),
291- triggers=controller.send_to_plugins)
292- controller.mediabar.add_toolbar_action('playbackPause', text='media_playback_pause',
293- icon=UiIcons().pause,
294- tooltip=translate('OpenLP.SlideController', 'Pause playing media.'),
295- triggers=controller.send_to_plugins)
296- controller.mediabar.add_toolbar_action('playbackStop', text='media_playback_stop',
297- icon=UiIcons().stop,
298- tooltip=translate('OpenLP.SlideController', 'Stop playing media.'),
299- triggers=controller.send_to_plugins)
300- controller.mediabar.add_toolbar_action('playbackLoop', text='media_playback_loop',
301- icon=UiIcons().repeat, 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.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
307- controller.position_label.setToolTip(translate('OpenLP.SlideController', 'Video timer.'))
308- controller.position_label.setMinimumSize(90, 0)
309- controller.position_label.setObjectName('position_label')
310- controller.mediabar.add_toolbar_widget(controller.position_label)
311- # Build the seek_slider.
312- controller.seek_slider = MediaSlider(QtCore.Qt.Horizontal, self, controller)
313- controller.seek_slider.setMaximum(1000)
314- controller.seek_slider.setTracking(True)
315- controller.seek_slider.setMouseTracking(True)
316- controller.seek_slider.setToolTip(translate('OpenLP.SlideController', 'Video position.'))
317- controller.seek_slider.setGeometry(QtCore.QRect(90, 260, 221, 24))
318- controller.seek_slider.setObjectName('seek_slider')
319- controller.mediabar.add_toolbar_widget(controller.seek_slider)
320- # Build the volume_slider.
321- controller.volume_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
322- controller.volume_slider.setTickInterval(10)
323- controller.volume_slider.setTickPosition(QtWidgets.QSlider.TicksAbove)
324- controller.volume_slider.setMinimum(0)
325- controller.volume_slider.setMaximum(100)
326- controller.volume_slider.setTracking(True)
327- controller.volume_slider.setToolTip(translate('OpenLP.SlideController', 'Audio Volume.'))
328- controller.volume_slider.setValue(controller.media_info.volume)
329- controller.volume_slider.setGeometry(QtCore.QRect(90, 160, 221, 24))
330- controller.volume_slider.setObjectName('volume_slider')
331- controller.mediabar.add_toolbar_widget(controller.volume_slider)
332- controller.controller_layout.addWidget(controller.mediabar)
333- controller.mediabar.setVisible(False)
334- if not controller.is_live:
335- controller.volume_slider.setEnabled(False)
336- # Signals
337- controller.seek_slider.valueChanged.connect(controller.send_to_plugins)
338- controller.volume_slider.valueChanged.connect(controller.send_to_plugins)
339+ self.media_stop(self.display_controllers(DisplayControllerType.Preview))
340+ if self.display_controllers(DisplayControllerType.Preview).media_info.can_loop_playback:
341+ self.media_play(self.display_controllers(DisplayControllerType.Preview), True)
342
343 def setup_display(self, display, preview):
344 """
345@@ -287,14 +198,13 @@
346 :param display: Display on which the output is to be played
347 :param preview: Whether the display is a main or preview display
348 """
349- # clean up possible running old media files
350- self.finalise()
351+ display.media_info = ItemMediaInfo()
352 display.has_audio = True
353- if display.is_live and preview:
354- return
355+ # if display.is_live and preview:
356+ # return
357 if preview:
358 display.has_audio = False
359- self.vlc_player.setup(display)
360+ self.vlc_player.setup(display, preview)
361
362 def set_controls_visible(self, controller, value):
363 """
364@@ -305,9 +215,9 @@
365 """
366 # Generic controls
367 controller.mediabar.setVisible(value)
368- if controller.is_live and controller.display:
369- if self.current_media_players and value:
370- controller.display.set_transparency(False)
371+ # if controller.is_live and controller.display:
372+ # if self.current_media_players and value:
373+ # controller.display.set_transparency(False)
374
375 @staticmethod
376 def resize(display, player):
377@@ -319,7 +229,7 @@
378 """
379 player.resize(display)
380
381- def video(self, source, service_item, hidden=False, video_behind_text=False):
382+ def load_video(self, source, service_item, hidden=False, video_behind_text=False):
383 """
384 Loads and starts a video to run with the option of sound
385
386@@ -329,7 +239,7 @@
387 :param video_behind_text: Is the video to be played behind text.
388 """
389 is_valid = True
390- controller = self.display_controllers[source]
391+ controller = self.display_controllers(source)
392 # stop running videos
393 self.media_reset(controller)
394 controller.media_info = ItemMediaInfo()
395@@ -354,8 +264,8 @@
396 log.debug('video is not optical and live')
397 controller.media_info.length = service_item.media_length
398 is_valid = self._check_file_type(controller, display)
399- display.override['theme'] = ''
400- display.override['video'] = True
401+ # display.override['theme'] = ''
402+ # display.override['video'] = True
403 if controller.media_info.is_background:
404 # ignore start/end time
405 controller.media_info.start_time = 0
406@@ -379,10 +289,10 @@
407 critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'),
408 translate('MediaPlugin.MediaItem', 'Unsupported File'))
409 return False
410- log.debug('video mediatype: ' + str(controller.media_info.media_type))
411+ log.debug('video media type: ' + str(controller.media_info.media_type))
412 # dont care about actual theme, set a black background
413- if controller.is_live and not controller.media_info.is_background:
414- display.frame.runJavaScript('show_video("setBackBoard", null, null,"visible");')
415+ # if controller.is_live and not controller.media_info.is_background:
416+ # display.frame.runJavaScript('show_video("setBackBoard", null, null,"visible");')
417 # now start playing - Preview is autoplay!
418 autoplay = False
419 # Preview requested
420@@ -471,28 +381,26 @@
421 for file in controller.media_info.file_info:
422 if file.is_file:
423 suffix = '*%s' % file.suffix.lower()
424- player = self.vlc_player
425 file = str(file)
426- if suffix in player.video_extensions_list:
427+ if suffix in self.vlc_player.video_extensions_list:
428 if not controller.media_info.is_background or controller.media_info.is_background and \
429- player.can_background:
430- self.resize(display, player)
431- if player.load(display, file):
432- self.current_media_players[controller.controller_type] = player
433+ self.vlc_player.can_background:
434+ self.resize(display, self.vlc_player)
435+ if self.vlc_player.load(display, file):
436+ self.current_media_players[controller.controller_type] = self.vlc_player
437 controller.media_info.media_type = MediaType.Video
438 return True
439- if suffix in player.audio_extensions_list:
440- if player.load(display, file):
441- self.current_media_players[controller.controller_type] = player
442+ if suffix in self.vlc_player.audio_extensions_list:
443+ if self.vlc_player.load(display, file):
444+ self.current_media_players[controller.controller_type] = self.vlc_player
445 controller.media_info.media_type = MediaType.Audio
446 return True
447 else:
448- player = self.vlc_player
449 file = str(file)
450- if player.can_folder:
451- self.resize(display, player)
452- if player.load(display, file):
453- self.current_media_players[controller.controller_type] = player
454+ if self.vlc_player.can_folder:
455+ self.resize(display, self.vlc_player)
456+ if self.vlc_player.load(display, file):
457+ self.current_media_players[controller.controller_type] = self.vlc_player
458 controller.media_info.media_type = MediaType.Video
459 return True
460 return False
461@@ -509,8 +417,6 @@
462 def on_media_play(self):
463 """
464 Responds to the request to play a loaded video from the web.
465-
466- :param msg: First element is the controller which should be used
467 """
468 self.media_play(Registry().get('live_controller'), False)
469
470@@ -524,7 +430,7 @@
471 controller.seek_slider.blockSignals(True)
472 controller.volume_slider.blockSignals(True)
473 display = self._define_display(controller)
474- if not self.current_media_players[controller.controller_type].play(display):
475+ if not self.current_media_players[controller.controller_type].play(controller, display):
476 controller.seek_slider.blockSignals(False)
477 controller.volume_slider.blockSignals(False)
478 return False
479@@ -533,8 +439,8 @@
480 else:
481 self.media_volume(controller, controller.media_info.volume)
482 if first_time:
483- if not controller.media_info.is_background:
484- display.frame.runJavaScript('show_blank("desktop");')
485+ # if not controller.media_info.is_background:
486+ # display.frame.runJavaScript('show_blank("desktop");')
487 self.current_media_players[controller.controller_type].set_visible(display, True)
488 controller.mediabar.actions['playbackPlay'].setVisible(False)
489 controller.mediabar.actions['playbackPause'].setVisible(True)
490@@ -591,8 +497,6 @@
491 def on_media_pause(self):
492 """
493 Responds to the request to pause a loaded video from the web.
494-
495- :param msg: First element is the controller which should be used
496 """
497 self.media_pause(Registry().get('live_controller'))
498
499@@ -639,8 +543,6 @@
500 def on_media_stop(self):
501 """
502 Responds to the request to stop a loaded video from the web.
503-
504- :param msg: First element is the controller which should be used
505 """
506 self.media_stop(Registry().get('live_controller'))
507
508@@ -653,8 +555,8 @@
509 """
510 display = self._define_display(controller)
511 if controller.controller_type in self.current_media_players:
512- if not looping_background:
513- display.frame.runJavaScript('show_blank("black");')
514+ # if not looping_background:
515+ # display.frame.runJavaScript('show_blank("black");')
516 self.current_media_players[controller.controller_type].stop(display)
517 self.current_media_players[controller.controller_type].set_visible(display, False)
518 controller.seek_slider.setSliderPosition(0)
519@@ -725,7 +627,7 @@
520 display.override = {}
521 self.current_media_players[controller.controller_type].reset(display)
522 self.current_media_players[controller.controller_type].set_visible(display, False)
523- display.frame.runJavaScript('show_video("setBackBoard", null, null, "hidden");')
524+ # display.frame.runJavaScript('show_video("setBackBoard", null, null, "hidden");')
525 del self.current_media_players[controller.controller_type]
526
527 def media_hide(self, msg):
528@@ -788,8 +690,8 @@
529 """
530 self.live_timer.stop()
531 self.preview_timer.stop()
532- for controller in self.display_controllers:
533- self.media_reset(self.display_controllers[controller])
534+ self.media_reset(self.display_controllers(DisplayControllerType.Live))
535+ self.media_reset(self.display_controllers(DisplayControllerType.Preview))
536
537 @staticmethod
538 def _define_display(controller):
539
540=== modified file 'openlp/core/ui/media/mediaplayer.py'
541--- openlp/core/ui/media/mediaplayer.py 2019-02-14 15:09:09 +0000
542+++ openlp/core/ui/media/mediaplayer.py 2019-04-11 20:30:04 +0000
543@@ -52,11 +52,12 @@
544 """
545 return False
546
547- def setup(self, display):
548+ def setup(self, display, live_display):
549 """
550 Create the related widgets for the current display
551
552 :param display: The display to be updated.
553+ :param live_display: Is the display a live one.
554 """
555 pass
556
557@@ -78,10 +79,11 @@
558 """
559 pass
560
561- def play(self, display):
562+ def play(self, controller, display):
563 """
564 Starts playing of current Media File
565
566+ :param controller: Which Controller is running the show.
567 :param display: The display to be updated.
568 """
569 pass
570@@ -206,7 +208,7 @@
571 :param display: Identify the Display type
572 :return: None
573 """
574- if display.controller.is_live:
575+ if display.is_display:
576 self.set_live_state(state)
577 else:
578 self.set_preview_state(state)
579
580=== added file 'openlp/core/ui/media/mediatab.py'
581--- openlp/core/ui/media/mediatab.py 1970-01-01 00:00:00 +0000
582+++ openlp/core/ui/media/mediatab.py 2019-04-11 20:30:04 +0000
583@@ -0,0 +1,148 @@
584+# -*- coding: utf-8 -*-
585+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
586+
587+###############################################################################
588+# OpenLP - Open Source Lyrics Projection #
589+# --------------------------------------------------------------------------- #
590+# Copyright (c) 2008-2019 OpenLP Developers #
591+# --------------------------------------------------------------------------- #
592+# This program is free software; you can redistribute it and/or modify it #
593+# under the terms of the GNU General Public License as published by the Free #
594+# Software Foundation; version 2 of the License. #
595+# #
596+# This program is distributed in the hope that it will be useful, but WITHOUT #
597+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
598+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
599+# more details. #
600+# #
601+# You should have received a copy of the GNU General Public License along #
602+# with this program; if not, write to the Free Software Foundation, Inc., 59 #
603+# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
604+###############################################################################
605+"""
606+The :mod:`~openlp.core.ui.media.playertab` module holds the configuration tab for the media stuff.
607+"""
608+
609+from PyQt5 import QtWidgets
610+# from PyQt5.QtMultimedia import QCameraInfo, QAudioDeviceInfo, QAudio
611+
612+from openlp.core.common import is_linux, is_win
613+from openlp.core.common.i18n import translate
614+from openlp.core.common.settings import Settings
615+from openlp.core.lib.settingstab import SettingsTab
616+from openlp.core.ui.icons import UiIcons
617+
618+LINUX_STREAM = 'v4l2:///dev/video0'
619+WIN_STREAM = 'dshow:// :dshow-vdev='
620+
621+
622+class MediaTab(SettingsTab):
623+ """
624+ MediaTab is the Media settings tab in the settings dialog.
625+ """
626+ def __init__(self, parent):
627+ """
628+ Constructor
629+ """
630+ # self.media_players = Registry().get('media_controller').media_players
631+ # self.saved_used_players = None
632+ self.icon_path = UiIcons().video
633+ player_translated = translate('OpenLP.MediaTab', 'Media')
634+ super(MediaTab, self).__init__(parent, 'Media', player_translated)
635+
636+ def setup_ui(self):
637+ """
638+ Set up the UI
639+ """
640+ self.setObjectName('MediaTab')
641+ super(MediaTab, self).setup_ui()
642+ self.live_media_group_box = QtWidgets.QGroupBox(self.left_column)
643+ self.live_media_group_box.setObjectName('live_media_group_box')
644+ self.media_layout = QtWidgets.QVBoxLayout(self.live_media_group_box)
645+ self.media_layout.setObjectName('live_media_layout')
646+ self.auto_start_check_box = QtWidgets.QCheckBox(self.live_media_group_box)
647+ self.auto_start_check_box.setObjectName('auto_start_check_box')
648+ self.media_layout.addWidget(self.auto_start_check_box)
649+ self.left_layout.addWidget(self.live_media_group_box)
650+ self.stream_media_group_box = QtWidgets.QGroupBox(self.left_column)
651+ self.stream_media_group_box.setObjectName('stream_media_group_box')
652+ self.stream_media_layout = QtWidgets.QHBoxLayout(self.stream_media_group_box)
653+ self.stream_media_layout.setObjectName('live_media_layout')
654+ self.stream_media_layout.setContentsMargins(0, 0, 0, 0)
655+ self.stream_edit = QtWidgets.QPlainTextEdit(self)
656+ self.stream_media_layout.addWidget(self.stream_edit)
657+ self.browse_button = QtWidgets.QToolButton(self)
658+ self.browse_button.setIcon(UiIcons().undo)
659+ self.stream_media_layout.addWidget(self.browse_button)
660+ self.left_layout.addWidget(self.stream_media_group_box)
661+ self.left_layout.addWidget(self.stream_media_group_box)
662+ self.left_layout.addStretch()
663+ self.right_layout.addStretch()
664+ # # Signals and slots
665+ self.browse_button.clicked.connect(self.on_revert)
666+
667+ def retranslateUi(self):
668+ """
669+ Translate the UI on the fly
670+ """
671+ self.live_media_group_box.setTitle(translate('MediaPlugin.MediaTab', 'Live Media'))
672+ self.stream_media_group_box.setTitle(translate('MediaPlugin.MediaTab', 'Stream Media Command'))
673+ self.auto_start_check_box.setText(translate('MediaPlugin.MediaTab', 'Start automatically'))
674+
675+ def load(self):
676+ """
677+ Load the settings
678+ """
679+ self.auto_start_check_box.setChecked(Settings().value(self.settings_section + '/media auto start'))
680+ self.stream_edit.setPlainText(Settings().value(self.settings_section + '/stream command'))
681+ if not self.stream_edit.toPlainText():
682+ if is_linux:
683+ self.stream_edit.setPlainText(LINUX_STREAM)
684+ elif is_win:
685+ self.stream_edit.setPlainText(WIN_STREAM)
686+
687+ def save(self):
688+ """
689+ Save the settings
690+ """
691+ setting_key = self.settings_section + '/media auto start'
692+ if Settings().value(setting_key) != self.auto_start_check_box.checkState():
693+ Settings().setValue(setting_key, self.auto_start_check_box.checkState())
694+ # settings = Settings()
695+ # settings.beginGroup(self.settings_section)
696+ # settings.setValue('background color', self.background_color)
697+ # settings.endGroup()
698+ # old_players, override_player = get_media_players()
699+ # if self.used_players != old_players:
700+ # # clean old Media stuff
701+ # set_media_players(self.used_players, override_player)
702+ # self.settings_form.register_post_process('mediaitem_suffix_reset')
703+ # self.settings_form.register_post_process('mediaitem_media_rebuild')
704+ # self.settings_form.register_post_process('config_screen_changed')
705+
706+ def post_set_up(self, post_update=False):
707+ """
708+ Late setup for players as the MediaController has to be initialised first.
709+
710+ :param post_update: Indicates if called before or after updates.
711+ """
712+ pass
713+ # for key, player in self.media_players.items():
714+ # player = self.media_players[key]
715+ # checkbox = MediaQCheckBox(self.media_player_group_box)
716+ # checkbox.setEnabled(player.available)
717+ # checkbox.setObjectName(player.name + '_check_box')
718+ # checkbox.setToolTip(player.get_info())
719+ # checkbox.set_player_name(player.name)
720+ # self.player_check_boxes[player.name] = checkbox
721+ # checkbox.stateChanged.connect(self.on_player_check_box_changed)
722+ # self.media_player_layout.addWidget(checkbox)
723+ # if player.available and player.name in self.used_players:
724+ # checkbox.setChecked(True)
725+ # else:
726+ # checkbox.setChecked(False)
727+ # self.update_player_list()
728+ # self.retranslate_players()
729+
730+ def on_revert(self):
731+ pass
732
733=== removed file 'openlp/core/ui/media/playertab.py'
734--- openlp/core/ui/media/playertab.py 2019-02-14 15:09:09 +0000
735+++ openlp/core/ui/media/playertab.py 1970-01-01 00:00:00 +0000
736@@ -1,269 +0,0 @@
737-# -*- coding: utf-8 -*-
738-# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
739-
740-###############################################################################
741-# OpenLP - Open Source Lyrics Projection #
742-# --------------------------------------------------------------------------- #
743-# Copyright (c) 2008-2019 OpenLP Developers #
744-# --------------------------------------------------------------------------- #
745-# This program is free software; you can redistribute it and/or modify it #
746-# under the terms of the GNU General Public License as published by the Free #
747-# Software Foundation; version 2 of the License. #
748-# #
749-# This program is distributed in the hope that it will be useful, but WITHOUT #
750-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
751-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
752-# more details. #
753-# #
754-# You should have received a copy of the GNU General Public License along #
755-# with this program; if not, write to the Free Software Foundation, Inc., 59 #
756-# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
757-###############################################################################
758-"""
759-The :mod:`~openlp.core.ui.media.playertab` module holds the configuration tab for the media stuff.
760-"""
761-import platform
762-
763-from PyQt5 import QtCore, QtWidgets
764-
765-from openlp.core.common.i18n import UiStrings, translate
766-# from openlp.core.common.registry import Registry
767-from openlp.core.common.settings import Settings
768-from openlp.core.lib.settingstab import SettingsTab
769-from openlp.core.lib.ui import create_button
770-from openlp.core.ui.icons import UiIcons
771-from openlp.core.widgets.buttons import ColorButton
772-
773-
774-class MediaQCheckBox(QtWidgets.QCheckBox):
775- """
776- MediaQCheckBox adds an extra property, player_name to the QCheckBox class.
777- """
778- def set_player_name(self, name):
779- """
780- Set the player name
781- """
782- self.player_name = name
783-
784-
785-class PlayerTab(SettingsTab):
786- """
787- MediaTab is the Media settings tab in the settings dialog.
788- """
789- def __init__(self, parent):
790- """
791- Constructor
792- """
793- # self.media_players = Registry().get('media_controller').media_players
794- self.saved_used_players = None
795- self.icon_path = UiIcons().player
796- player_translated = translate('OpenLP.PlayerTab', 'Players')
797- super(PlayerTab, self).__init__(parent, 'Players', player_translated)
798-
799- def setup_ui(self):
800- """
801- Set up the UI
802- """
803- self.setObjectName('MediaTab')
804- super(PlayerTab, self).setup_ui()
805- self.background_color_group_box = QtWidgets.QGroupBox(self.left_column)
806- self.background_color_group_box.setObjectName('background_color_group_box')
807- self.form_layout = QtWidgets.QFormLayout(self.background_color_group_box)
808- self.form_layout.setObjectName('form_layout')
809- self.color_layout = QtWidgets.QHBoxLayout()
810- self.background_color_label = QtWidgets.QLabel(self.background_color_group_box)
811- self.background_color_label.setObjectName('background_color_label')
812- self.color_layout.addWidget(self.background_color_label)
813- self.background_color_button = ColorButton(self.background_color_group_box)
814- self.background_color_button.setObjectName('background_color_button')
815- self.color_layout.addWidget(self.background_color_button)
816- self.form_layout.addRow(self.color_layout)
817- self.information_label = QtWidgets.QLabel(self.background_color_group_box)
818- self.information_label.setObjectName('information_label')
819- self.information_label.setWordWrap(True)
820- self.form_layout.addRow(self.information_label)
821- self.left_layout.addWidget(self.background_color_group_box)
822- self.right_column.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
823- self.media_player_group_box = QtWidgets.QGroupBox(self.left_column)
824- self.media_player_group_box.setObjectName('media_player_group_box')
825- self.media_player_layout = QtWidgets.QVBoxLayout(self.media_player_group_box)
826- self.media_player_layout.setObjectName('media_player_layout')
827- self.player_check_boxes = {}
828- self.left_layout.addWidget(self.media_player_group_box)
829- self.player_order_group_box = QtWidgets.QGroupBox(self.left_column)
830- self.player_order_group_box.setObjectName('player_order_group_box')
831- self.player_order_layout = QtWidgets.QHBoxLayout(self.player_order_group_box)
832- self.player_order_layout.setObjectName('player_order_layout')
833- self.player_order_list_widget = QtWidgets.QListWidget(self.player_order_group_box)
834- size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
835- size_policy.setHorizontalStretch(0)
836- size_policy.setVerticalStretch(0)
837- size_policy.setHeightForWidth(self.player_order_list_widget.sizePolicy().hasHeightForWidth())
838- self.player_order_list_widget.setSizePolicy(size_policy)
839- self.player_order_list_widget.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
840- self.player_order_list_widget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
841- self.player_order_list_widget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
842- self.player_order_list_widget.setObjectName('player_order_list_widget')
843- self.player_order_layout.addWidget(self.player_order_list_widget)
844- self.ordering_button_layout = QtWidgets.QVBoxLayout()
845- self.ordering_button_layout.setObjectName('ordering_button_layout')
846- self.ordering_button_layout.addStretch(1)
847- self.ordering_up_button = create_button(self, 'ordering_up_button', role='up',
848- click=self.on_up_button_clicked)
849- self.ordering_down_button = create_button(self, 'ordering_down_button', role='down',
850- click=self.on_down_button_clicked)
851- self.ordering_button_layout.addWidget(self.ordering_up_button)
852- self.ordering_button_layout.addWidget(self.ordering_down_button)
853- self.ordering_button_layout.addStretch(1)
854- self.player_order_layout.addLayout(self.ordering_button_layout)
855- self.left_layout.addWidget(self.player_order_group_box)
856- self.left_layout.addStretch()
857- self.right_layout.addStretch()
858- # Signals and slots
859- self.background_color_button.colorChanged.connect(self.on_background_color_changed)
860-
861- def retranslate_ui(self):
862- """
863- Translate the UI on the fly
864- """
865- self.media_player_group_box.setTitle(translate('OpenLP.PlayerTab', 'Available Media Players'))
866- self.player_order_group_box.setTitle(translate('OpenLP.PlayerTab', 'Player Search Order'))
867- self.background_color_group_box.setTitle(UiStrings().BackgroundColor)
868- self.background_color_label.setText(UiStrings().BackgroundColorColon)
869- self.information_label.setText(translate('OpenLP.PlayerTab',
870- 'Visible background for videos with aspect ratio different to screen.'))
871- self.retranslate_players()
872-
873- def on_background_color_changed(self, color):
874- """
875- Set the background color
876-
877- :param color: The color to be set.
878- """
879- self.background_color = color
880-
881- def on_player_check_box_changed(self, check_state):
882- """
883- Add or remove players depending on their status
884-
885- :param check_state: The requested status.
886- """
887- player = self.sender().player_name
888- if check_state == QtCore.Qt.Checked:
889- if player not in self.used_players:
890- self.used_players.append(player)
891- else:
892- if player in self.used_players:
893- self.used_players.remove(player)
894- self.update_player_list()
895-
896- def update_player_list(self):
897- """
898- Update the list of media players
899- """
900- self.player_order_list_widget.clear()
901- for player in self.used_players:
902- if player in list(self.player_check_boxes.keys()):
903- if len(self.used_players) == 1:
904- # At least one media player has to stay active
905- self.player_check_boxes['%s' % player].setEnabled(False)
906- else:
907- self.player_check_boxes['%s' % player].setEnabled(True)
908- self.player_order_list_widget.addItem(self.media_players[str(player)].original_name)
909-
910- def on_up_button_clicked(self):
911- """
912- Move a media player up in the order
913- """
914- row = self.player_order_list_widget.currentRow()
915- if row <= 0:
916- return
917- item = self.player_order_list_widget.takeItem(row)
918- self.player_order_list_widget.insertItem(row - 1, item)
919- self.player_order_list_widget.setCurrentRow(row - 1)
920- self.used_players.insert(row - 1, self.used_players.pop(row))
921-
922- def on_down_button_clicked(self):
923- """
924- Move a media player down in the order
925- """
926- row = self.player_order_list_widget.currentRow()
927- if row == -1 or row > self.player_order_list_widget.count() - 1:
928- return
929- item = self.player_order_list_widget.takeItem(row)
930- self.player_order_list_widget.insertItem(row + 1, item)
931- self.player_order_list_widget.setCurrentRow(row + 1)
932- self.used_players.insert(row + 1, self.used_players.pop(row))
933-
934- def load(self):
935- """
936- Load the settings
937- """
938- if self.saved_used_players:
939- self.used_players = self.saved_used_players
940- # self.used_players = get_media_players()[0]
941- self.saved_used_players = self.used_players
942- settings = Settings()
943- settings.beginGroup(self.settings_section)
944- self.update_player_list()
945- self.background_color = settings.value('background color')
946- self.initial_color = self.background_color
947- settings.endGroup()
948- self.background_color_button.color = self.background_color
949-
950- def save(self):
951- """
952- Save the settings
953- """
954- settings = Settings()
955- settings.beginGroup(self.settings_section)
956- settings.setValue('background color', self.background_color)
957- settings.endGroup()
958- # old_players, override_player = get_media_players()
959- # if self.used_players != old_players:
960- # # clean old Media stuff
961- # set_media_players(self.used_players, override_player)
962- # self.settings_form.register_post_process('mediaitem_suffix_reset')
963- # self.settings_form.register_post_process('mediaitem_media_rebuild')
964- # self.settings_form.register_post_process('config_screen_changed')
965-
966- def post_set_up(self, post_update=False):
967- """
968- Late setup for players as the MediaController has to be initialised first.
969-
970- :param post_update: Indicates if called before or after updates.
971- """
972- for key, player in self.media_players.items():
973- player = self.media_players[key]
974- checkbox = MediaQCheckBox(self.media_player_group_box)
975- checkbox.setEnabled(player.available)
976- checkbox.setObjectName(player.name + '_check_box')
977- checkbox.setToolTip(player.get_info())
978- checkbox.set_player_name(player.name)
979- self.player_check_boxes[player.name] = checkbox
980- checkbox.stateChanged.connect(self.on_player_check_box_changed)
981- self.media_player_layout.addWidget(checkbox)
982- if player.available and player.name in self.used_players:
983- checkbox.setChecked(True)
984- else:
985- checkbox.setChecked(False)
986- self.update_player_list()
987- self.retranslate_players()
988-
989- def retranslate_players(self):
990- """
991- Translations for players is dependent on their setup as well
992- """
993- for key in self.media_players and self.player_check_boxes:
994- player = self.media_players[key]
995- checkbox = self.player_check_boxes[player.name]
996- checkbox.set_player_name(player.name)
997- if player.available:
998- checkbox.setText(player.display_name)
999- else:
1000- checkbox_text = translate('OpenLP.PlayerTab', '%s (unavailable)') % player.display_name
1001- if player.name == 'vlc':
1002- checkbox_text += ' ' + translate('OpenLP.PlayerTab',
1003- 'NOTE: To use VLC you must install the %s version',
1004- 'Will insert "32bit" or "64bit"') % platform.architecture()[0]
1005- checkbox.setText(checkbox_text)
1006
1007=== removed file 'openlp/core/ui/media/systemplayer.py'
1008--- openlp/core/ui/media/systemplayer.py 2019-02-14 15:09:09 +0000
1009+++ openlp/core/ui/media/systemplayer.py 1970-01-01 00:00:00 +0000
1010@@ -1,332 +0,0 @@
1011-# -*- coding: utf-8 -*-
1012-# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
1013-
1014-###############################################################################
1015-# OpenLP - Open Source Lyrics Projection #
1016-# --------------------------------------------------------------------------- #
1017-# Copyright (c) 2008-2019 OpenLP Developers #
1018-# --------------------------------------------------------------------------- #
1019-# This program is free software; you can redistribute it and/or modify it #
1020-# under the terms of the GNU General Public License as published by the Free #
1021-# Software Foundation; version 2 of the License. #
1022-# #
1023-# This program is distributed in the hope that it will be useful, but WITHOUT #
1024-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
1025-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
1026-# more details. #
1027-# #
1028-# You should have received a copy of the GNU General Public License along #
1029-# with this program; if not, write to the Free Software Foundation, Inc., 59 #
1030-# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
1031-###############################################################################
1032-"""
1033-The :mod:`~openlp.core.ui.media.systemplayer` contains the system (aka QtMultimedia) player component.
1034-"""
1035-import functools
1036-import logging
1037-import mimetypes
1038-
1039-from PyQt5 import QtCore, QtMultimedia, QtMultimediaWidgets
1040-
1041-from openlp.core.common.i18n import translate
1042-from openlp.core.threading import ThreadWorker, is_thread_finished, run_thread
1043-from openlp.core.ui.media import MediaState
1044-from openlp.core.ui.media.mediaplayer import MediaPlayer
1045-
1046-
1047-log = logging.getLogger(__name__)
1048-
1049-ADDITIONAL_EXT = {
1050- 'audio/ac3': ['.ac3'],
1051- 'audio/flac': ['.flac'],
1052- 'audio/x-m4a': ['.m4a'],
1053- 'audio/midi': ['.mid', '.midi'],
1054- 'audio/x-mp3': ['.mp3'],
1055- 'audio/mpeg': ['.mp3', '.mp2', '.mpga', '.mpega', '.m4a'],
1056- 'audio/qcelp': ['.qcp'],
1057- 'audio/x-wma': ['.wma'],
1058- 'audio/x-ms-wma': ['.wma'],
1059- 'video/x-flv': ['.flv'],
1060- 'video/x-matroska': ['.mpv', '.mkv'],
1061- 'video/x-wmv': ['.wmv'],
1062- 'video/x-mpg': ['.mpg'],
1063- 'video/mpeg': ['.mp4', '.mts', '.mov'],
1064- 'video/x-ms-wmv': ['.wmv']
1065-}
1066-
1067-
1068-class SystemPlayer(MediaPlayer):
1069- """
1070- A specialised version of the MediaPlayer class, which provides a QtMultimedia display.
1071- """
1072-
1073- def __init__(self, parent):
1074- """
1075- Constructor
1076- """
1077- super(SystemPlayer, self).__init__(parent, 'system')
1078- self.original_name = 'System'
1079- self.display_name = '&System'
1080- self.parent = parent
1081- self.additional_extensions = ADDITIONAL_EXT
1082- self.media_player = QtMultimedia.QMediaPlayer(None, QtMultimedia.QMediaPlayer.VideoSurface)
1083- mimetypes.init()
1084- media_service = self.media_player.service()
1085- log.info(media_service.__class__.__name__)
1086- # supportedMimeTypes doesn't return anything on Linux and Windows and
1087- # the mimetypes it returns on Mac OS X may not be playable.
1088- supported_codecs = self.media_player.supportedMimeTypes()
1089- for mime_type in supported_codecs:
1090- mime_type = str(mime_type)
1091- log.info(mime_type)
1092- if mime_type.startswith('audio/'):
1093- self._add_to_list(self.audio_extensions_list, mime_type)
1094- elif mime_type.startswith('video/'):
1095- self._add_to_list(self.video_extensions_list, mime_type)
1096-
1097- def _add_to_list(self, mime_type_list, mime_type):
1098- """
1099- Add mimetypes to the provided list
1100- """
1101- # Add all extensions which mimetypes provides us for supported types.
1102- extensions = mimetypes.guess_all_extensions(mime_type)
1103- for extension in extensions:
1104- ext = '*%s' % extension
1105- if ext not in mime_type_list:
1106- mime_type_list.append(ext)
1107- log.info('MediaPlugin: %s extensions: %s', mime_type, ' '.join(extensions))
1108-
1109- def disconnect_slots(self, signal):
1110- """
1111- Safely disconnect the slots from `signal`
1112- """
1113- try:
1114- signal.disconnect()
1115- except TypeError:
1116- # If disconnect() is called on a signal without slots, it throws a TypeError
1117- pass
1118-
1119- def setup(self, display):
1120- """
1121- Set up the player widgets
1122- :param display:
1123- """
1124- display.video_widget = QtMultimediaWidgets.QVideoWidget(display)
1125- display.video_widget.resize(display.size())
1126- display.media_player = QtMultimedia.QMediaPlayer(display)
1127- display.media_player.setVideoOutput(display.video_widget)
1128- display.video_widget.raise_()
1129- display.video_widget.hide()
1130- self.has_own_widget = True
1131-
1132- def check_available(self):
1133- """
1134- Check if the player is available
1135- """
1136- return True
1137-
1138- def load(self, display):
1139- """
1140- Load a video into the display
1141-
1142- :param display: The display where the media is
1143- """
1144- log.debug('load vid in System Controller')
1145- controller = display.controller
1146- volume = controller.media_info.volume
1147- path = controller.media_info.file_info.absoluteFilePath()
1148- # Check if file is playable due to mimetype filters being nonexistent on Linux and Windows
1149- if self.check_media(path):
1150- display.media_player.setMedia(QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(path)))
1151- self.volume(display, volume)
1152- return True
1153- else:
1154- return False
1155-
1156- def resize(self, display):
1157- """
1158- Resize the display
1159-
1160- :param display: The display where the media is
1161- """
1162- display.video_widget.resize(display.size())
1163-
1164- def play(self, display):
1165- """
1166- Play the current media item
1167-
1168- :param display: The display where the media is
1169- """
1170- log.info('Play the current item')
1171- controller = display.controller
1172- start_time = 0
1173- if display.controller.is_live:
1174- if self.get_live_state() != QtMultimedia.QMediaPlayer.PausedState and controller.media_info.start_time > 0:
1175- start_time = controller.media_info.start_time
1176- else:
1177- if self.get_preview_state() != QtMultimedia.QMediaPlayer.PausedState and \
1178- controller.media_info.start_time > 0:
1179- start_time = controller.media_info.start_time
1180- display.media_player.play()
1181- if start_time > 0:
1182- self.seek(display, controller.media_info.start_time * 1000)
1183- self.volume(display, controller.media_info.volume)
1184- self.disconnect_slots(display.media_player.durationChanged)
1185- display.media_player.durationChanged.connect(functools.partial(self.set_duration, controller))
1186- self.set_state(MediaState.Playing, display)
1187- display.video_widget.raise_()
1188- return True
1189-
1190- def pause(self, display):
1191- """
1192- Pause the current media item
1193-
1194- :param display: The display where the media is
1195- """
1196- display.media_player.pause()
1197- if display.controller.is_live:
1198- if self.get_live_state() == QtMultimedia.QMediaPlayer.PausedState:
1199- self.set_state(MediaState.Paused, display)
1200- else:
1201- if self.get_preview_state() == QtMultimedia.QMediaPlayer.PausedState:
1202- self.set_state(MediaState.Paused, display)
1203-
1204- def stop(self, display):
1205- """
1206- Stop the current media item
1207-
1208- :param display: The display where the media is
1209- """
1210- display.media_player.stop()
1211- self.set_visible(display, False)
1212- self.set_state(MediaState.Stopped, display)
1213-
1214- def volume(self, display, volume):
1215- """
1216- Set the volume
1217-
1218- :param display: The display where the media is
1219- :param volume: The volume to be set
1220- """
1221- if display.has_audio:
1222- display.media_player.setVolume(volume)
1223-
1224- def seek(self, display, seek_value):
1225- """
1226- Go to a particular point in the current media item
1227-
1228- :param display: The display where the media is
1229- :param seek_value: The where to seek to
1230- """
1231- display.media_player.setPosition(seek_value)
1232-
1233- def reset(self, display):
1234- """
1235- Reset the media player
1236-
1237- :param display: The display where the media is
1238- """
1239- display.media_player.stop()
1240- display.media_player.setMedia(QtMultimedia.QMediaContent())
1241- self.set_visible(display, False)
1242- display.video_widget.setVisible(False)
1243- self.set_state(MediaState.Off, display)
1244-
1245- def set_visible(self, display, status):
1246- """
1247- Set the visibility of the widget
1248-
1249- :param display: The display where the media is
1250- :param status: The visibility status to be set
1251- """
1252- if self.has_own_widget:
1253- display.video_widget.setVisible(status)
1254-
1255- @staticmethod
1256- def set_duration(controller, duration):
1257- """
1258-
1259- :param controller: the controller displaying the media
1260- :param duration: how long is the media
1261- :return:
1262- """
1263- controller.seek_slider.setMaximum(controller.media_info.length)
1264-
1265- def update_ui(self, display):
1266- """
1267- Update the UI
1268-
1269- :param display: The display where the media is
1270- """
1271- if display.media_player.state() == QtMultimedia.QMediaPlayer.PausedState and self.state != MediaState.Paused:
1272- self.pause(display)
1273- controller = display.controller
1274- if controller.media_info.end_time > 0:
1275- if display.media_player.position() > controller.media_info.end_time:
1276- self.stop(display)
1277- self.set_visible(display, False)
1278- if not controller.seek_slider.isSliderDown():
1279- controller.seek_slider.blockSignals(True)
1280- controller.seek_slider.setSliderPosition(display.media_player.position())
1281- controller.seek_slider.blockSignals(False)
1282-
1283- def get_media_display_css(self):
1284- """
1285- Add css style sheets to htmlbuilder
1286- """
1287- return ''
1288-
1289- def get_info(self):
1290- """
1291- Return some info about this player
1292- """
1293- return (translate('Media.player', 'This media player uses your operating system '
1294- 'to provide media capabilities.') +
1295- '<br/> <strong>' + translate('Media.player', 'Audio') +
1296- '</strong><br/>' + str(self.audio_extensions_list) +
1297- '<br/><strong>' + translate('Media.player', 'Video') +
1298- '</strong><br/>' + str(self.video_extensions_list) + '<br/>')
1299-
1300- def check_media(self, path):
1301- """
1302- Check if a file can be played
1303- Uses a separate QMediaPlayer in a thread
1304-
1305- :param path: Path to file to be checked
1306- :return: True if file can be played otherwise False
1307- """
1308- check_media_worker = CheckMediaWorker(path)
1309- check_media_worker.setVolume(0)
1310- run_thread(check_media_worker, 'check_media')
1311- while not is_thread_finished('check_media'):
1312- self.application.processEvents()
1313- return check_media_worker.result
1314-
1315-
1316-class CheckMediaWorker(QtMultimedia.QMediaPlayer, ThreadWorker):
1317- """
1318- Class used to check if a media file is playable
1319- """
1320- def __init__(self, path):
1321- super(CheckMediaWorker, self).__init__(None, QtMultimedia.QMediaPlayer.VideoSurface)
1322- self.path = path
1323-
1324- def start(self):
1325- """
1326- Start the thread worker
1327- """
1328- self.result = None
1329- self.error.connect(functools.partial(self.signals, 'error'))
1330- self.mediaStatusChanged.connect(functools.partial(self.signals, 'media'))
1331- self.setMedia(QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(self.path)))
1332- self.play()
1333-
1334- def signals(self, origin, status):
1335- if origin == 'media' and status == self.BufferedMedia:
1336- self.result = True
1337- self.stop()
1338- self.quit.emit()
1339- elif origin == 'error' and status != self.NoError:
1340- self.result = False
1341- self.stop()
1342- self.quit.emit()
1343
1344=== modified file 'openlp/core/ui/media/vlcplayer.py'
1345--- openlp/core/ui/media/vlcplayer.py 2019-03-17 20:35:11 +0000
1346+++ openlp/core/ui/media/vlcplayer.py 2019-04-11 20:30:04 +0000
1347@@ -152,43 +152,42 @@
1348 self.audio_extensions_list = AUDIO_EXT
1349 self.video_extensions_list = VIDEO_EXT
1350
1351- def setup(self, display):
1352+ def setup(self, output_display, live_display):
1353 """
1354 Set up the media player
1355
1356- :param display: The display where the media is
1357+ :param output_display: The display where the media is
1358+ :param live_display: Is the display a live one.
1359 :return:
1360 """
1361 vlc = get_vlc()
1362- display.vlc_widget = QtWidgets.QFrame(display)
1363- display.vlc_widget.setFrameStyle(QtWidgets.QFrame.NoFrame)
1364+ output_display.vlc_widget = QtWidgets.QFrame(output_display)
1365+ output_display.vlc_widget.setFrameStyle(QtWidgets.QFrame.NoFrame)
1366 # creating a basic vlc instance
1367 command_line_options = '--no-video-title-show'
1368- if not display.has_audio:
1369- command_line_options += ' --no-audio --no-video-title-show'
1370- if Settings().value('advanced/hide mouse') and display.controller.is_live:
1371+ if Settings().value('advanced/hide mouse') and live_display:
1372 command_line_options += ' --mouse-hide-timeout=0'
1373- display.vlc_instance = vlc.Instance(command_line_options)
1374+ output_display.vlc_instance = vlc.Instance(command_line_options)
1375 # creating an empty vlc media player
1376- display.vlc_media_player = display.vlc_instance.media_player_new()
1377- display.vlc_widget.resize(display.size())
1378- display.vlc_widget.raise_()
1379- display.vlc_widget.hide()
1380+ output_display.vlc_media_player = output_display.vlc_instance.media_player_new()
1381+ output_display.vlc_widget.resize(output_display.size())
1382+ output_display.vlc_widget.raise_()
1383+ output_display.vlc_widget.hide()
1384 # The media player has to be 'connected' to the QFrame.
1385 # (otherwise a video would be displayed in it's own window)
1386 # This is platform specific!
1387 # You have to give the id of the QFrame (or similar object)
1388 # to vlc, different platforms have different functions for this.
1389- win_id = int(display.vlc_widget.winId())
1390+ win_id = int(output_display.vlc_widget.winId())
1391 if is_win():
1392- display.vlc_media_player.set_hwnd(win_id)
1393+ output_display.vlc_media_player.set_hwnd(win_id)
1394 elif is_macosx():
1395 # We have to use 'set_nsobject' since Qt5 on OSX uses Cocoa
1396 # framework and not the old Carbon.
1397- display.vlc_media_player.set_nsobject(win_id)
1398+ output_display.vlc_media_player.set_nsobject(win_id)
1399 else:
1400 # for Linux/*BSD using the X Server
1401- display.vlc_media_player.set_xwindow(win_id)
1402+ output_display.vlc_media_player.set_xwindow(win_id)
1403 self.has_own_widget = True
1404
1405 def check_available(self):
1406@@ -197,43 +196,45 @@
1407 """
1408 return get_vlc() is not None
1409
1410- def load(self, display, file):
1411+ def load(self, output_display, file):
1412 """
1413 Load a video into VLC
1414
1415- :param display: The display where the media is
1416+ :param output_display: The display where the media is
1417 :param file: file to be played
1418 :return:
1419 """
1420 vlc = get_vlc()
1421 log.debug('load vid in Vlc Controller')
1422- controller = display.controller
1423+ controller = output_display
1424 volume = controller.media_info.volume
1425 path = os.path.normcase(file)
1426 # create the media
1427 if controller.media_info.media_type == MediaType.CD:
1428 if is_win():
1429 path = '/' + path
1430- display.vlc_media = display.vlc_instance.media_new_location('cdda://' + path)
1431- display.vlc_media_player.set_media(display.vlc_media)
1432- display.vlc_media_player.play()
1433+ output_display.vlc_media = output_display.vlc_instance.media_new_location('cdda://' + path)
1434+ output_display.vlc_media_player.set_media(output_display.vlc_media)
1435+ output_display.vlc_media_player.play()
1436 # Wait for media to start playing. In this case VLC actually returns an error.
1437- self.media_state_wait(display, vlc.State.Playing)
1438+ self.media_state_wait(output_display, vlc.State.Playing)
1439 # If subitems exists, this is a CD
1440- audio_cd_tracks = display.vlc_media.subitems()
1441+ audio_cd_tracks = output_display.vlc_media.subitems()
1442 if not audio_cd_tracks or audio_cd_tracks.count() < 1:
1443 return False
1444- display.vlc_media = audio_cd_tracks.item_at_index(controller.media_info.title_track)
1445+ output_display.vlc_media = audio_cd_tracks.item_at_index(controller.media_info.title_track)
1446+ elif controller.media_info.media_type == MediaType.Stream:
1447+ output_display.vlc_media = output_display.vlc_instance.media_new_location('ZZZZZZ')
1448 else:
1449- display.vlc_media = display.vlc_instance.media_new_path(path)
1450+ output_display.vlc_media = output_display.vlc_instance.media_new_path(path)
1451 # put the media in the media player
1452- display.vlc_media_player.set_media(display.vlc_media)
1453+ output_display.vlc_media_player.set_media(output_display.vlc_media)
1454 # parse the metadata of the file
1455- display.vlc_media.parse()
1456- self.volume(display, volume)
1457+ output_display.vlc_media.parse()
1458+ self.volume(output_display, volume)
1459 return True
1460
1461- def media_state_wait(self, display, media_state):
1462+ def media_state_wait(self, output_display, media_state):
1463 """
1464 Wait for the video to change its state
1465 Wait no longer than 60 seconds. (loading an iso file needs a long time)
1466@@ -244,171 +245,171 @@
1467 """
1468 vlc = get_vlc()
1469 start = datetime.now()
1470- while media_state != display.vlc_media.get_state():
1471- if display.vlc_media.get_state() == vlc.State.Error:
1472+ while media_state != output_display.vlc_media.get_state():
1473+ if output_display.vlc_media.get_state() == vlc.State.Error:
1474 return False
1475 self.application.process_events()
1476 if (datetime.now() - start).seconds > 60:
1477 return False
1478 return True
1479
1480- def resize(self, display):
1481+ def resize(self, output_display):
1482 """
1483 Resize the player
1484
1485- :param display: The display where the media is
1486+ :param output_display: The display where the media is
1487 :return:
1488 """
1489- display.vlc_widget.resize(display.size())
1490+ output_display.vlc_widget.resize(output_display.size())
1491
1492- def play(self, display):
1493+ def play(self, controller, output_display):
1494 """
1495 Play the current item
1496
1497- :param display: The display where the media is
1498+ :param controller: Which Controller is running the show.
1499+ :param output_display: The display where the media is
1500 :return:
1501 """
1502 vlc = get_vlc()
1503- controller = display.controller
1504 start_time = 0
1505 log.debug('vlc play')
1506- if display.controller.is_live:
1507- if self.get_live_state() != MediaState.Paused and controller.media_info.start_time > 0:
1508- start_time = controller.media_info.start_time
1509+ if output_display.is_display:
1510+ if self.get_live_state() != MediaState.Paused and output_display.media_info.start_time > 0:
1511+ start_time = output_display.media_info.start_time
1512 else:
1513- if self.get_preview_state() != MediaState.Paused and controller.media_info.start_time > 0:
1514- start_time = controller.media_info.start_time
1515- threading.Thread(target=display.vlc_media_player.play).start()
1516- if not self.media_state_wait(display, vlc.State.Playing):
1517+ if self.get_preview_state() != MediaState.Paused and output_display.media_info.start_time > 0:
1518+ start_time = output_display.media_info.start_time
1519+ threading.Thread(target=output_display.vlc_media_player.play).start()
1520+ if not self.media_state_wait(output_display, vlc.State.Playing):
1521 return False
1522- if display.controller.is_live:
1523- if self.get_live_state() != MediaState.Paused and controller.media_info.start_time > 0:
1524+ if output_display.is_display:
1525+ if self.get_live_state() != MediaState.Paused and output_display.media_info.start_time > 0:
1526 log.debug('vlc play, start time set')
1527- start_time = controller.media_info.start_time
1528+ start_time = output_display.media_info.start_time
1529 else:
1530- if self.get_preview_state() != MediaState.Paused and controller.media_info.start_time > 0:
1531+ if self.get_preview_state() != MediaState.Paused and output_display.media_info.start_time > 0:
1532 log.debug('vlc play, start time set')
1533- start_time = controller.media_info.start_time
1534- log.debug('mediatype: ' + str(controller.media_info.media_type))
1535+ start_time = output_display.media_info.start_time
1536+ log.debug('mediatype: ' + str(output_display.media_info.media_type))
1537 # Set tracks for the optical device
1538- if controller.media_info.media_type == MediaType.DVD and \
1539+ if output_display.media_info.media_type == MediaType.DVD and \
1540 self.get_live_state() != MediaState.Paused and self.get_preview_state() != MediaState.Paused:
1541 log.debug('vlc play, playing started')
1542- if controller.media_info.title_track > 0:
1543- log.debug('vlc play, title_track set: ' + str(controller.media_info.title_track))
1544- display.vlc_media_player.set_title(controller.media_info.title_track)
1545- display.vlc_media_player.play()
1546- if not self.media_state_wait(display, vlc.State.Playing):
1547+ if output_display.media_info.title_track > 0:
1548+ log.debug('vlc play, title_track set: ' + str(output_display.media_info.title_track))
1549+ output_display.vlc_media_player.set_title(output_display.media_info.title_track)
1550+ output_display.vlc_media_player.play()
1551+ if not self.media_state_wait(output_display, vlc.State.Playing):
1552 return False
1553- if controller.media_info.audio_track > 0:
1554- display.vlc_media_player.audio_set_track(controller.media_info.audio_track)
1555- log.debug('vlc play, audio_track set: ' + str(controller.media_info.audio_track))
1556- if controller.media_info.subtitle_track > 0:
1557- display.vlc_media_player.video_set_spu(controller.media_info.subtitle_track)
1558- log.debug('vlc play, subtitle_track set: ' + str(controller.media_info.subtitle_track))
1559- if controller.media_info.start_time > 0:
1560- log.debug('vlc play, starttime set: ' + str(controller.media_info.start_time))
1561- start_time = controller.media_info.start_time
1562- controller.media_info.length = controller.media_info.end_time - controller.media_info.start_time
1563- self.volume(display, controller.media_info.volume)
1564- if start_time > 0 and display.vlc_media_player.is_seekable():
1565- display.vlc_media_player.set_time(int(start_time))
1566- controller.seek_slider.setMaximum(controller.media_info.length)
1567- self.set_state(MediaState.Playing, display)
1568- display.vlc_widget.raise_()
1569+ if output_display.media_info.audio_track > 0:
1570+ output_display.vlc_media_player.audio_set_track(output_display.media_info.audio_track)
1571+ log.debug('vlc play, audio_track set: ' + str(output_display.media_info.audio_track))
1572+ if output_display.media_info.subtitle_track > 0:
1573+ output_display.vlc_media_player.video_set_spu(output_display.media_info.subtitle_track)
1574+ log.debug('vlc play, subtitle_track set: ' + str(output_display.media_info.subtitle_track))
1575+ if output_display.media_info.start_time > 0:
1576+ log.debug('vlc play, starttime set: ' + str(output_display.media_info.start_time))
1577+ start_time = output_display.media_info.start_time
1578+ output_display.media_info.length = output_display.media_info.end_time - output_display.media_info.start_time
1579+ self.volume(output_display, output_display.media_info.volume)
1580+ if start_time > 0 and output_display.vlc_media_player.is_seekable():
1581+ output_display.vlc_media_player.set_time(int(start_time))
1582+ controller.seek_slider.setMaximum(output_display.media_info.length)
1583+ self.set_state(MediaState.Playing, output_display)
1584+ output_display.vlc_widget.raise_()
1585 return True
1586
1587- def pause(self, display):
1588+ def pause(self, output_display):
1589 """
1590 Pause the current item
1591
1592- :param display: The display where the media is
1593+ :param output_display: The display where the media is
1594 :return:
1595 """
1596 vlc = get_vlc()
1597- if display.vlc_media.get_state() != vlc.State.Playing:
1598+ if output_display.vlc_media.get_state() != vlc.State.Playing:
1599 return
1600- display.vlc_media_player.pause()
1601- if self.media_state_wait(display, vlc.State.Paused):
1602- self.set_state(MediaState.Paused, display)
1603+ output_display.vlc_media_player.pause()
1604+ if self.media_state_wait(output_display, vlc.State.Paused):
1605+ self.set_state(MediaState.Paused, output_display)
1606
1607- def stop(self, display):
1608+ def stop(self, output_display):
1609 """
1610 Stop the current item
1611
1612- :param display: The display where the media is
1613+ :param output_display: The display where the media is
1614 :return:
1615 """
1616- threading.Thread(target=display.vlc_media_player.stop).start()
1617- self.set_state(MediaState.Stopped, display)
1618+ threading.Thread(target=output_display.vlc_media_player.stop).start()
1619+ self.set_state(MediaState.Stopped, output_display)
1620
1621- def volume(self, display, vol):
1622+ def volume(self, output_display, vol):
1623 """
1624 Set the volume
1625
1626 :param vol: The volume to be sets
1627- :param display: The display where the media is
1628+ :param output_display: The display where the media is
1629 :return:
1630 """
1631- if display.has_audio:
1632- display.vlc_media_player.audio_set_volume(vol)
1633+ if output_display.has_audio:
1634+ output_display.vlc_media_player.audio_set_volume(vol)
1635
1636- def seek(self, display, seek_value):
1637+ def seek(self, output_display, seek_value):
1638 """
1639 Go to a particular position
1640
1641 :param seek_value: The position of where a seek goes to
1642- :param display: The display where the media is
1643+ :param output_display: The display where the media is
1644 """
1645- if display.controller.media_info.media_type == MediaType.CD \
1646- or display.controller.media_info.media_type == MediaType.DVD:
1647- seek_value += int(display.controller.media_info.start_time)
1648- if display.vlc_media_player.is_seekable():
1649- display.vlc_media_player.set_time(seek_value)
1650+ if output_display.controller.media_info.media_type == MediaType.CD \
1651+ or output_display.controller.media_info.media_type == MediaType.DVD:
1652+ seek_value += int(output_display.controller.media_info.start_time)
1653+ if output_display.vlc_media_player.is_seekable():
1654+ output_display.vlc_media_player.set_time(seek_value)
1655
1656- def reset(self, display):
1657+ def reset(self, output_display):
1658 """
1659 Reset the player
1660
1661- :param display: The display where the media is
1662+ :param output_display: The display where the media is
1663 """
1664- display.vlc_media_player.stop()
1665- display.vlc_widget.setVisible(False)
1666- self.set_state(MediaState.Off, display)
1667+ output_display.vlc_media_player.stop()
1668+ output_display.vlc_widget.setVisible(False)
1669+ self.set_state(MediaState.Off, output_display)
1670
1671- def set_visible(self, display, status):
1672+ def set_visible(self, output_display, status):
1673 """
1674 Set the visibility
1675
1676- :param display: The display where the media is
1677+ :param output_display: The display where the media is
1678 :param status: The visibility status
1679 """
1680 if self.has_own_widget:
1681- display.vlc_widget.setVisible(status)
1682+ output_display.vlc_widget.setVisible(status)
1683
1684- def update_ui(self, display):
1685+ def update_ui(self, controller, output_display):
1686 """
1687 Update the UI
1688
1689- :param display: The display where the media is
1690+ :param controller: Which Controller is running the show.
1691+ :param output_display: The display where the media is
1692 """
1693 vlc = get_vlc()
1694 # Stop video if playback is finished.
1695- if display.vlc_media.get_state() == vlc.State.Ended:
1696- self.stop(display)
1697- controller = display.controller
1698+ if output_display.vlc_media.get_state() == vlc.State.Ended:
1699+ self.stop(output_display)
1700 if controller.media_info.end_time > 0:
1701- if display.vlc_media_player.get_time() > controller.media_info.end_time:
1702- self.stop(display)
1703- self.set_visible(display, False)
1704+ if output_display.vlc_media_player.get_time() > controller.media_info.end_time:
1705+ self.stop(output_display)
1706+ self.set_visible(output_display, False)
1707 if not controller.seek_slider.isSliderDown():
1708 controller.seek_slider.blockSignals(True)
1709- if display.controller.media_info.media_type == MediaType.CD \
1710- or display.controller.media_info.media_type == MediaType.DVD:
1711+ if controller.media_info.media_type == MediaType.CD \
1712+ or controller.media_info.media_type == MediaType.DVD:
1713 controller.seek_slider.setSliderPosition(
1714- display.vlc_media_player.get_time() - int(display.controller.media_info.start_time))
1715+ output_display.vlc_media_player.get_time() - int(output_display.controller.media_info.start_time))
1716 else:
1717- controller.seek_slider.setSliderPosition(display.vlc_media_player.get_time())
1718+ controller.seek_slider.setSliderPosition(output_display.vlc_media_player.get_time())
1719 controller.seek_slider.blockSignals(False)
1720
1721 def get_info(self):
1722
1723=== modified file 'openlp/core/ui/screenstab.py'
1724--- openlp/core/ui/screenstab.py 2019-02-14 15:09:09 +0000
1725+++ openlp/core/ui/screenstab.py 2019-04-11 20:30:04 +0000
1726@@ -41,7 +41,7 @@
1727 """
1728 Initialise the screen settings tab
1729 """
1730- self.icon_path = UiIcons().settings
1731+ self.icon_path = UiIcons().desktop
1732 screens_translated = translate('OpenLP.ScreensTab', 'Screens')
1733 super(ScreensTab, self).__init__(parent, 'Screens', screens_translated)
1734 self.settings_section = 'core'
1735
1736=== modified file 'openlp/core/ui/settingsform.py'
1737--- openlp/core/ui/settingsform.py 2019-02-14 15:09:09 +0000
1738+++ openlp/core/ui/settingsform.py 2019-04-11 20:30:04 +0000
1739@@ -30,13 +30,14 @@
1740 from openlp.core.api.tab import ApiTab
1741 from openlp.core.common.mixins import RegistryProperties
1742 from openlp.core.common.registry import Registry
1743+from openlp.core.common.settings import Settings
1744 from openlp.core.lib import build_icon
1745 from openlp.core.projectors.tab import ProjectorTab
1746 from openlp.core.ui.advancedtab import AdvancedTab
1747 from openlp.core.ui.generaltab import GeneralTab
1748 from openlp.core.ui.screenstab import ScreensTab
1749 from openlp.core.ui.themestab import ThemesTab
1750-# from openlp.core.ui.media.playertab import PlayerTab
1751+from openlp.core.ui.media.mediatab import MediaTab
1752 from openlp.core.ui.settingsdialog import Ui_SettingsDialog
1753
1754
1755@@ -78,8 +79,8 @@
1756 self.insert_tab(self.advanced_tab)
1757 self.insert_tab(self.screens_tab)
1758 self.insert_tab(self.themes_tab)
1759- self.insert_tab(self.advanced_tab)
1760- # self.insert_tab(self.player_tab)
1761+ if Settings().value('core/experimental'):
1762+ self.insert_tab(self.player_tab)
1763 self.insert_tab(self.projector_tab)
1764 self.insert_tab(self.api_tab)
1765 for plugin in State().list_plugins():
1766@@ -160,15 +161,17 @@
1767 self.themes_tab = ThemesTab(self)
1768 self.projector_tab = ProjectorTab(self)
1769 self.advanced_tab = AdvancedTab(self)
1770- # self.player_tab = PlayerTab(self)
1771+ if Settings().value('core/experimental'):
1772+ self.player_tab = MediaTab(self)
1773 self.api_tab = ApiTab(self)
1774 self.screens_tab = ScreensTab(self)
1775 except Exception as e:
1776 print(e)
1777- # Post setup
1778 self.general_tab.post_set_up()
1779 self.themes_tab.post_set_up()
1780 self.advanced_tab.post_set_up()
1781+ if Settings().value('core/experimental'):
1782+ self.player_tab.post_set_up()
1783 self.api_tab.post_set_up()
1784 for plugin in State().list_plugins():
1785 if plugin.settings_tab:
1786
1787=== modified file 'openlp/core/ui/slidecontroller.py'
1788--- openlp/core/ui/slidecontroller.py 2019-02-14 15:09:09 +0000
1789+++ openlp/core/ui/slidecontroller.py 2019-04-11 20:30:04 +0000
1790@@ -23,6 +23,7 @@
1791 The :mod:`slidecontroller` module contains the most important part of OpenLP - the slide controller
1792 """
1793 import copy
1794+import datetime
1795 from collections import deque
1796 from threading import Lock
1797
1798@@ -70,6 +71,45 @@
1799 ]
1800
1801
1802+class MediaSlider(QtWidgets.QSlider):
1803+ """
1804+ Allows the mouse events of a slider to be overridden and extra functionality added
1805+ """
1806+ def __init__(self, direction, manager, controller):
1807+ """
1808+ Constructor
1809+ """
1810+ super(MediaSlider, self).__init__(direction)
1811+ self.manager = manager
1812+ self.controller = controller
1813+
1814+ def mouseMoveEvent(self, event):
1815+ """
1816+ Override event to allow hover time to be displayed.
1817+
1818+ :param event: The triggering event
1819+ """
1820+ time_value = QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width())
1821+ self.setToolTip('%s' % datetime.timedelta(seconds=int(time_value / 1000)))
1822+ QtWidgets.QSlider.mouseMoveEvent(self, event)
1823+
1824+ def mousePressEvent(self, event):
1825+ """
1826+ Mouse Press event no new functionality
1827+ :param event: The triggering event
1828+ """
1829+ QtWidgets.QSlider.mousePressEvent(self, event)
1830+
1831+ def mouseReleaseEvent(self, event):
1832+ """
1833+ Set the slider position when the mouse is clicked and released on the slider.
1834+
1835+ :param event: The triggering event
1836+ """
1837+ self.setValue(QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width()))
1838+ QtWidgets.QSlider.mouseReleaseEvent(self, event)
1839+
1840+
1841 class InfoLabel(QtWidgets.QLabel):
1842 """
1843 InfoLabel is a subclassed QLabel. Created to provide the ablilty to add a ellipsis if the text is cut off. Original
1844@@ -316,8 +356,59 @@
1845 'Clear'),
1846 triggers=self.on_clear)
1847 self.controller_layout.addWidget(self.toolbar)
1848- # Build the Media Toolbar
1849- self.media_controller.register_controller(self)
1850+ # Build a Media ToolBar
1851+ self.mediabar = OpenLPToolbar(self)
1852+ self.mediabar.add_toolbar_action('playbackPlay', text='media_playback_play',
1853+ icon=UiIcons().play,
1854+ tooltip=translate('OpenLP.SlideController', 'Start playing media.'),
1855+ triggers=self.send_to_plugins)
1856+ self.mediabar.add_toolbar_action('playbackPause', text='media_playback_pause',
1857+ icon=UiIcons().pause,
1858+ tooltip=translate('OpenLP.SlideController', 'Pause playing media.'),
1859+ triggers=self.send_to_plugins)
1860+ self.mediabar.add_toolbar_action('playbackStop', text='media_playback_stop',
1861+ icon=UiIcons().stop,
1862+ tooltip=translate('OpenLP.SlideController', 'Stop playing media.'),
1863+ triggers=self.send_to_plugins)
1864+ self.mediabar.add_toolbar_action('playbackLoop', text='media_playback_loop',
1865+ icon=UiIcons().repeat, checked=False,
1866+ tooltip=translate('OpenLP.SlideController', 'Loop playing media.'),
1867+ triggers=self.send_to_plugins)
1868+ self.position_label = QtWidgets.QLabel()
1869+ self.position_label.setText(' 00:00 / 00:00')
1870+ self.position_label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
1871+ self.position_label.setToolTip(translate('OpenLP.SlideController', 'Video timer.'))
1872+ self.position_label.setMinimumSize(90, 0)
1873+ self.position_label.setObjectName('position_label')
1874+ self.mediabar.add_toolbar_widget(self.position_label)
1875+ # Build the seek_slider.
1876+ self.seek_slider = MediaSlider(QtCore.Qt.Horizontal, self, self)
1877+ self.seek_slider.setMaximum(1000)
1878+ self.seek_slider.setTracking(True)
1879+ self.seek_slider.setMouseTracking(True)
1880+ self.seek_slider.setToolTip(translate('OpenLP.SlideController', 'Video position.'))
1881+ self.seek_slider.setGeometry(QtCore.QRect(90, 260, 221, 24))
1882+ self.seek_slider.setObjectName('seek_slider')
1883+ self.mediabar.add_toolbar_widget(self.seek_slider)
1884+ # Build the volume_slider.
1885+ self.volume_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
1886+ self.volume_slider.setTickInterval(10)
1887+ self.volume_slider.setTickPosition(QtWidgets.QSlider.TicksAbove)
1888+ self.volume_slider.setMinimum(0)
1889+ self.volume_slider.setMaximum(100)
1890+ self.volume_slider.setTracking(True)
1891+ self.volume_slider.setToolTip(translate('OpenLP.SlideController', 'Audio Volume.'))
1892+ # self.volume_slider.setValue(self.media_info.volume)
1893+ self.volume_slider.setGeometry(QtCore.QRect(90, 160, 221, 24))
1894+ self.volume_slider.setObjectName('volume_slider')
1895+ self.mediabar.add_toolbar_widget(self.volume_slider)
1896+ self.controller_layout.addWidget(self.mediabar)
1897+ self.mediabar.setVisible(False)
1898+ if not self.is_live:
1899+ self.volume_slider.setEnabled(False)
1900+ # Signals
1901+ self.seek_slider.valueChanged.connect(self.send_to_plugins)
1902+ self.volume_slider.valueChanged.connect(self.send_to_plugins)
1903 if self.is_live:
1904 # Build the Song Toolbar
1905 self.song_menu = QtWidgets.QToolButton(self.toolbar)
1906@@ -556,8 +647,6 @@
1907 # self.__add_actions_to_widget(self.display)
1908 # The SlidePreview's ratio.
1909
1910- # TODO: Need to basically update everything
1911-
1912 def __add_actions_to_widget(self, widget):
1913 """
1914 Add actions to the widget specified by `widget`
1915@@ -1398,10 +1487,10 @@
1916 :param item: The service item to be processed
1917 """
1918 if self.is_live and self.hide_mode() == HideMode.Theme:
1919- self.media_controller.video(self.controller_type, item, HideMode.Blank)
1920+ self.media_controller.load_video(self.controller_type, item, HideMode.Blank)
1921 self.on_blank_display(True)
1922 else:
1923- self.media_controller.video(self.controller_type, item, self.hide_mode())
1924+ self.media_controller.load_video(self.controller_type, item, self.hide_mode())
1925 if not self.is_live:
1926 self.preview_display.show()
1927
1928@@ -1491,7 +1580,7 @@
1929 self.type_prefix = 'preview'
1930 self.category = 'Preview Toolbar'
1931
1932- def bootstrap_post_set_up(self):
1933+ def bootstrap_initialise(self):
1934 """
1935 process the bootstrap post setup request
1936 """
1937@@ -1523,7 +1612,7 @@
1938 self.category = UiStrings().LiveToolbar
1939 ActionList.get_instance().add_category(str(self.category), CategoryOrder.standard_toolbar)
1940
1941- def bootstrap_post_set_up(self):
1942+ def bootstrap_initialise(self):
1943 """
1944 process the bootstrap post setup request
1945 """
1946
1947=== modified file 'openlp/core/ui/themeform.py'
1948--- openlp/core/ui/themeform.py 2019-02-14 15:09:09 +0000
1949+++ openlp/core/ui/themeform.py 2019-04-11 20:30:04 +0000
1950@@ -330,6 +330,8 @@
1951 self.video_color_button.color = self.theme.background_border_color
1952 self.video_path_edit.path = self.theme.background_filename
1953 self.setField('background_type', 4)
1954+ elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Stream):
1955+ self.setField('background_type', 5)
1956 elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Transparent):
1957 self.setField('background_type', 3)
1958 if self.theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Horizontal):
1959
1960=== modified file 'openlp/core/ui/themewizard.py'
1961--- openlp/core/ui/themewizard.py 2019-02-14 15:09:09 +0000
1962+++ openlp/core/ui/themewizard.py 2019-04-11 20:30:04 +0000
1963@@ -64,7 +64,7 @@
1964 self.background_label = QtWidgets.QLabel(self.background_page)
1965 self.background_label.setObjectName('background_label')
1966 self.background_combo_box = QtWidgets.QComboBox(self.background_page)
1967- self.background_combo_box.addItems(['', '', '', '', ''])
1968+ self.background_combo_box.addItems(['', '', '', '', '', ''])
1969 self.background_combo_box.setObjectName('background_combo_box')
1970 self.background_type_layout.addRow(self.background_label, self.background_combo_box)
1971 self.background_type_layout.setItem(1, QtWidgets.QFormLayout.LabelRole, self.spacer)
1972@@ -410,6 +410,8 @@
1973 self.background_combo_box.setItemText(BackgroundType.Video, UiStrings().Video)
1974 self.background_combo_box.setItemText(BackgroundType.Transparent,
1975 translate('OpenLP.ThemeWizard', 'Transparent'))
1976+ self.background_combo_box.setItemText(BackgroundType.Stream,
1977+ translate('OpenLP.ThemeWizard', 'Live Stream'))
1978 self.color_label.setText(translate('OpenLP.ThemeWizard', 'color:'))
1979 self.gradient_start_label.setText(translate('OpenLP.ThemeWizard', 'Starting color:'))
1980 self.gradient_end_label.setText(translate('OpenLP.ThemeWizard', 'Ending color:'))
1981
1982=== modified file 'openlp/core/widgets/views.py'
1983--- openlp/core/widgets/views.py 2019-02-14 15:09:09 +0000
1984+++ openlp/core/widgets/views.py 2019-04-11 20:30:04 +0000
1985@@ -203,7 +203,10 @@
1986 if self.service_item.is_capable(ItemCapabilities.HasThumbnails):
1987 pixmap = QtGui.QPixmap(remove_url_prefix(slide['thumbnail']))
1988 else:
1989- pixmap = QtGui.QPixmap(remove_url_prefix(slide['image']))
1990+ if isinstance(slide['image'], QtGui.QIcon):
1991+ pixmap = slide['image'].pixmap(QtCore.QSize(32, 32))
1992+ else:
1993+ pixmap = QtGui.QPixmap(remove_url_prefix(slide['image']))
1994 else:
1995 pixmap = QtGui.QPixmap(remove_url_prefix(slide['path']))
1996 label.setPixmap(pixmap)
1997
1998=== modified file 'openlp/plugins/media/lib/mediaitem.py'
1999--- openlp/plugins/media/lib/mediaitem.py 2019-03-17 20:35:11 +0000
2000+++ openlp/plugins/media/lib/mediaitem.py 2019-04-11 20:30:04 +0000
2001@@ -116,6 +116,8 @@
2002 self.can_preview = False
2003 self.can_make_live = False
2004 self.can_add_to_service = False
2005+ if State().check_preconditions('media_live'):
2006+ self.can_make_live = False
2007
2008 def add_list_view_to_toolbar(self):
2009 """
2010@@ -264,7 +266,8 @@
2011 :param media: The media
2012 :param target_group:
2013 """
2014- media.sort(key=lambda file_path: get_natural_key(file_path.name))
2015+ # TODO needs to be fixed as no idea why this fails
2016+ # media.sort(key=lambda file_path: get_natural_key(file_path.name))
2017 for track in media:
2018 track_info = QtCore.QFileInfo(track)
2019 item_name = None
2020
2021=== removed file 'openlp/plugins/media/lib/mediatab.py'
2022--- openlp/plugins/media/lib/mediatab.py 2019-02-14 15:09:09 +0000
2023+++ openlp/plugins/media/lib/mediatab.py 1970-01-01 00:00:00 +0000
2024@@ -1,73 +0,0 @@
2025-# -*- coding: utf-8 -*-
2026-# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
2027-
2028-###############################################################################
2029-# OpenLP - Open Source Lyrics Projection #
2030-# --------------------------------------------------------------------------- #
2031-# Copyright (c) 2008-2019 OpenLP Developers #
2032-# --------------------------------------------------------------------------- #
2033-# This program is free software; you can redistribute it and/or modify it #
2034-# under the terms of the GNU General Public License as published by the Free #
2035-# Software Foundation; version 2 of the License. #
2036-# #
2037-# This program is distributed in the hope that it will be useful, but WITHOUT #
2038-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
2039-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
2040-# more details. #
2041-# #
2042-# You should have received a copy of the GNU General Public License along #
2043-# with this program; if not, write to the Free Software Foundation, Inc., 59 #
2044-# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
2045-###############################################################################
2046-
2047-from PyQt5 import QtWidgets
2048-
2049-from openlp.core.common.i18n import UiStrings, translate
2050-from openlp.core.common.settings import Settings
2051-from openlp.core.lib.settingstab import SettingsTab
2052-
2053-
2054-class MediaTab(SettingsTab):
2055- """
2056- MediaTab is the Media settings tab in the settings dialog.
2057- """
2058- def __init__(self, parent, title, visible_title, icon_path):
2059- self.parent = parent
2060- super(MediaTab, self).__init__(parent, title, visible_title, icon_path)
2061-
2062- def setup_ui(self):
2063- self.setObjectName('MediaTab')
2064- super(MediaTab, self).setup_ui()
2065- self.advanced_group_box = QtWidgets.QGroupBox(self.left_column)
2066- self.advanced_group_box.setObjectName('advanced_group_box')
2067- self.advanced_layout = QtWidgets.QVBoxLayout(self.advanced_group_box)
2068- self.advanced_layout.setObjectName('advanced_layout')
2069- self.override_player_check_box = QtWidgets.QCheckBox(self.advanced_group_box)
2070- self.override_player_check_box.setObjectName('override_player_check_box')
2071- self.advanced_layout.addWidget(self.override_player_check_box)
2072- self.auto_start_check_box = QtWidgets.QCheckBox(self.advanced_group_box)
2073- self.auto_start_check_box.setObjectName('auto_start_check_box')
2074- self.advanced_layout.addWidget(self.auto_start_check_box)
2075- self.left_layout.addWidget(self.advanced_group_box)
2076- self.left_layout.addStretch()
2077- self.right_layout.addStretch()
2078-
2079- def retranslate_ui(self):
2080- self.advanced_group_box.setTitle(UiStrings().Advanced)
2081- self.override_player_check_box.setText(translate('MediaPlugin.MediaTab', 'Allow media player to be overridden'))
2082- self.auto_start_check_box.setText(translate('MediaPlugin.MediaTab', 'Start new Live media automatically'))
2083-
2084- def load(self):
2085- self.override_player_check_box.setChecked(Settings().value(self.settings_section + '/override player'))
2086- self.auto_start_check_box.setChecked(Settings().value(self.settings_section + '/media auto start'))
2087-
2088- def save(self):
2089- setting_key = self.settings_section + '/override player'
2090- if Settings().value(setting_key) != self.override_player_check_box.checkState():
2091- Settings().setValue(setting_key, self.override_player_check_box.checkState())
2092- self.settings_form.register_post_process('mediaitem_suffix_reset')
2093- self.settings_form.register_post_process('mediaitem_media_rebuild')
2094- self.settings_form.register_post_process('mediaitem_suffixes')
2095- setting_key = self.settings_section + '/media auto start'
2096- if Settings().value(setting_key) != self.auto_start_check_box.checkState():
2097- Settings().setValue(setting_key, self.auto_start_check_box.checkState())
2098
2099=== modified file 'openlp/plugins/media/mediaplugin.py'
2100--- openlp/plugins/media/mediaplugin.py 2019-02-14 15:09:09 +0000
2101+++ openlp/plugins/media/mediaplugin.py 2019-04-11 20:30:04 +0000
2102@@ -24,8 +24,6 @@
2103 """
2104 import logging
2105
2106-from PyQt5 import QtCore
2107-
2108 from openlp.core.state import State
2109 from openlp.core.api.http import register_endpoint
2110 from openlp.core.common.i18n import translate
2111@@ -34,7 +32,6 @@
2112 from openlp.core.lib.plugin import Plugin, StringContent
2113 from openlp.plugins.media.endpoint import api_media_endpoint, media_endpoint
2114 from openlp.plugins.media.lib.mediaitem import MediaMediaItem
2115-from openlp.plugins.media.lib.mediatab import MediaTab
2116
2117
2118 log = logging.getLogger(__name__)
2119@@ -42,7 +39,6 @@
2120
2121 # Some settings starting with "media" are in core, because they are needed for core functionality.
2122 __default_settings__ = {
2123- 'media/media auto start': QtCore.Qt.Unchecked,
2124 'media/media files': [],
2125 'media/last directory': None
2126 }
2127@@ -78,15 +74,6 @@
2128 """
2129 pass
2130
2131- def create_settings_tab(self, parent):
2132- """
2133- Create the settings Tab
2134-
2135- :param parent:
2136- """
2137- visible_name = self.get_string(StringContent.VisibleName)
2138- self.settings_tab = MediaTab(parent, self.name, visible_name['title'], self.icon_path)
2139-
2140 @staticmethod
2141 def about():
2142 """
2143
2144=== modified file 'tests/functional/openlp_core/common/test_common.py'
2145--- tests/functional/openlp_core/common/test_common.py 2019-02-14 15:09:09 +0000
2146+++ tests/functional/openlp_core/common/test_common.py 2019-04-11 20:30:04 +0000
2147@@ -139,13 +139,13 @@
2148 Test `path_to_module` when supplied with a `Path` object
2149 """
2150 # GIVEN: A `Path` object
2151- path = Path('core', 'ui', 'media', 'webkitplayer.py')
2152+ path = Path('core', 'ui', 'media', 'vlcplayer.py')
2153
2154 # WHEN: Calling path_to_module with the `Path` object
2155 result = path_to_module(path)
2156
2157 # THEN: path_to_module should return the module name
2158- assert result == 'openlp.core.ui.media.webkitplayer'
2159+ assert result == 'openlp.core.ui.media.vlcplayer'
2160
2161 def test_trace_error_handler(self):
2162 """
2163
2164=== removed file 'tests/functional/openlp_core/ui/media/test_systemplayer.py'
2165--- tests/functional/openlp_core/ui/media/test_systemplayer.py 2019-02-14 15:09:09 +0000
2166+++ tests/functional/openlp_core/ui/media/test_systemplayer.py 1970-01-01 00:00:00 +0000
2167@@ -1,567 +0,0 @@
2168-# -*- coding: utf-8 -*-
2169-# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
2170-
2171-###############################################################################
2172-# OpenLP - Open Source Lyrics Projection #
2173-# --------------------------------------------------------------------------- #
2174-# Copyright (c) 2008-2019 OpenLP Developers #
2175-# --------------------------------------------------------------------------- #
2176-# This program is free software; you can redistribute it and/or modify it #
2177-# under the terms of the GNU General Public License as published by the Free #
2178-# Software Foundation; version 2 of the License. #
2179-# #
2180-# This program is distributed in the hope that it will be useful, but WITHOUT #
2181-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
2182-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
2183-# more details. #
2184-# #
2185-# You should have received a copy of the GNU General Public License along #
2186-# with this program; if not, write to the Free Software Foundation, Inc., 59 #
2187-# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
2188-###############################################################################
2189-"""
2190-Package to test the openlp.core.ui.media.systemplayer package.
2191-"""
2192-from unittest import TestCase
2193-from unittest.mock import MagicMock, call, patch
2194-
2195-from PyQt5 import QtCore, QtMultimedia
2196-
2197-from openlp.core.common.registry import Registry
2198-from openlp.core.ui.media import MediaState
2199-from openlp.core.ui.media.systemplayer import ADDITIONAL_EXT, CheckMediaWorker, SystemPlayer
2200-
2201-
2202-class TestSystemPlayer(TestCase):
2203- """
2204- Test the system media player
2205- """
2206- @patch('openlp.core.ui.media.systemplayer.mimetypes')
2207- @patch('openlp.core.ui.media.systemplayer.QtMultimedia.QMediaPlayer')
2208- def test_constructor(self, MockQMediaPlayer, mocked_mimetypes):
2209- """
2210- Test the SystemPlayer constructor
2211- """
2212- # GIVEN: The SystemPlayer class and a mockedQMediaPlayer
2213- mocked_media_player = MagicMock()
2214- mocked_media_player.supportedMimeTypes.return_value = [
2215- 'application/postscript',
2216- 'audio/aiff',
2217- 'audio/x-aiff',
2218- 'text/html',
2219- 'video/animaflex',
2220- 'video/x-ms-asf'
2221- ]
2222- mocked_mimetypes.guess_all_extensions.side_effect = [
2223- ['.aiff'],
2224- ['.aiff'],
2225- ['.afl'],
2226- ['.asf']
2227- ]
2228- MockQMediaPlayer.return_value = mocked_media_player
2229-
2230- # WHEN: An object is created from it
2231- player = SystemPlayer(self)
2232-
2233- # THEN: The correct initial values should be set up
2234- assert 'system' == player.name
2235- assert 'System' == player.original_name
2236- assert '&System' == player.display_name
2237- assert self == player.parent
2238- assert ADDITIONAL_EXT == player.additional_extensions
2239- MockQMediaPlayer.assert_called_once_with(None, QtMultimedia.QMediaPlayer.VideoSurface)
2240- mocked_mimetypes.init.assert_called_once_with()
2241- mocked_media_player.service.assert_called_once_with()
2242- mocked_media_player.supportedMimeTypes.assert_called_once_with()
2243- assert ['*.aiff'] == player.audio_extensions_list
2244- assert ['*.afl', '*.asf'] == player.video_extensions_list
2245-
2246- @patch('openlp.core.ui.media.systemplayer.QtMultimediaWidgets.QVideoWidget')
2247- @patch('openlp.core.ui.media.systemplayer.QtMultimedia.QMediaPlayer')
2248- def test_setup(self, MockQMediaPlayer, MockQVideoWidget):
2249- """
2250- Test the setup() method of SystemPlayer
2251- """
2252- # GIVEN: A SystemPlayer instance and a mock display
2253- player = SystemPlayer(self)
2254- mocked_display = MagicMock()
2255- mocked_display.size.return_value = [1, 2, 3, 4]
2256- mocked_video_widget = MagicMock()
2257- mocked_media_player = MagicMock()
2258- MockQVideoWidget.return_value = mocked_video_widget
2259- MockQMediaPlayer.return_value = mocked_media_player
2260-
2261- # WHEN: setup() is run
2262- player.setup(mocked_display)
2263-
2264- # THEN: The player should have a display widget
2265- MockQVideoWidget.assert_called_once_with(mocked_display)
2266- assert mocked_video_widget == mocked_display.video_widget
2267- mocked_display.size.assert_called_once_with()
2268- mocked_video_widget.resize.assert_called_once_with([1, 2, 3, 4])
2269- MockQMediaPlayer.assert_called_with(mocked_display)
2270- assert mocked_media_player == mocked_display.media_player
2271- mocked_media_player.setVideoOutput.assert_called_once_with(mocked_video_widget)
2272- mocked_video_widget.raise_.assert_called_once_with()
2273- mocked_video_widget.hide.assert_called_once_with()
2274- assert player.has_own_widget is True
2275-
2276- def test_disconnect_slots(self):
2277- """
2278- Test that we the disconnect slots method catches the TypeError
2279- """
2280- # GIVEN: A SystemPlayer class and a signal that throws a TypeError
2281- player = SystemPlayer(self)
2282- mocked_signal = MagicMock()
2283- mocked_signal.disconnect.side_effect = \
2284- TypeError('disconnect() failed between \'durationChanged\' and all its connections')
2285-
2286- # WHEN: disconnect_slots() is called
2287- player.disconnect_slots(mocked_signal)
2288-
2289- # THEN: disconnect should have been called and the exception should have been ignored
2290- mocked_signal.disconnect.assert_called_once_with()
2291-
2292- def test_check_available(self):
2293- """
2294- Test the check_available() method on SystemPlayer
2295- """
2296- # GIVEN: A SystemPlayer instance
2297- player = SystemPlayer(self)
2298-
2299- # WHEN: check_available is run
2300- result = player.check_available()
2301-
2302- # THEN: it should be available
2303- assert result is True
2304-
2305- def test_load_valid_media(self):
2306- """
2307- Test the load() method of SystemPlayer with a valid media file
2308- """
2309- # GIVEN: A SystemPlayer instance and a mocked display
2310- player = SystemPlayer(self)
2311- mocked_display = MagicMock()
2312- mocked_display.controller.media_info.volume = 1
2313- mocked_display.controller.media_info.file_info.absoluteFilePath.return_value = '/path/to/file'
2314-
2315- # WHEN: The load() method is run
2316- with patch.object(player, 'check_media') as mocked_check_media, \
2317- patch.object(player, 'volume') as mocked_volume:
2318- mocked_check_media.return_value = True
2319- result = player.load(mocked_display)
2320-
2321- # THEN: the file is sent to the video widget
2322- mocked_display.controller.media_info.file_info.absoluteFilePath.assert_called_once_with()
2323- mocked_check_media.assert_called_once_with('/path/to/file')
2324- mocked_display.media_player.setMedia.assert_called_once_with(
2325- QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile('/path/to/file')))
2326- mocked_volume.assert_called_once_with(mocked_display, 1)
2327- assert result is True
2328-
2329- def test_load_invalid_media(self):
2330- """
2331- Test the load() method of SystemPlayer with an invalid media file
2332- """
2333- # GIVEN: A SystemPlayer instance and a mocked display
2334- player = SystemPlayer(self)
2335- mocked_display = MagicMock()
2336- mocked_display.controller.media_info.volume = 1
2337- mocked_display.controller.media_info.file_info.absoluteFilePath.return_value = '/path/to/file'
2338-
2339- # WHEN: The load() method is run
2340- with patch.object(player, 'check_media') as mocked_check_media, \
2341- patch.object(player, 'volume'):
2342- mocked_check_media.return_value = False
2343- result = player.load(mocked_display)
2344-
2345- # THEN: stuff
2346- mocked_display.controller.media_info.file_info.absoluteFilePath.assert_called_once_with()
2347- mocked_check_media.assert_called_once_with('/path/to/file')
2348- assert result is False
2349-
2350- def test_resize(self):
2351- """
2352- Test the resize() method of the SystemPlayer
2353- """
2354- # GIVEN: A SystemPlayer instance and a mocked display
2355- player = SystemPlayer(self)
2356- mocked_display = MagicMock()
2357- mocked_display.size.return_value = [1, 2, 3, 4]
2358-
2359- # WHEN: The resize() method is called
2360- player.resize(mocked_display)
2361-
2362- # THEN: The player is resized
2363- mocked_display.size.assert_called_once_with()
2364- mocked_display.video_widget.resize.assert_called_once_with([1, 2, 3, 4])
2365-
2366- @patch('openlp.core.ui.media.systemplayer.functools')
2367- def test_play_is_live(self, mocked_functools):
2368- """
2369- Test the play() method of the SystemPlayer on the live display
2370- """
2371- # GIVEN: A SystemPlayer instance and a mocked display
2372- mocked_functools.partial.return_value = 'function'
2373- player = SystemPlayer(self)
2374- mocked_display = MagicMock()
2375- mocked_display.controller.is_live = True
2376- mocked_display.controller.media_info.start_time = 1
2377- mocked_display.controller.media_info.volume = 1
2378-
2379- # WHEN: play() is called
2380- with patch.object(player, 'get_live_state') as mocked_get_live_state, \
2381- patch.object(player, 'seek') as mocked_seek, \
2382- patch.object(player, 'volume') as mocked_volume, \
2383- patch.object(player, 'set_state') as mocked_set_state, \
2384- patch.object(player, 'disconnect_slots') as mocked_disconnect_slots:
2385- mocked_get_live_state.return_value = QtMultimedia.QMediaPlayer.PlayingState
2386- result = player.play(mocked_display)
2387-
2388- # THEN: the media file is played
2389- mocked_get_live_state.assert_called_once_with()
2390- mocked_display.media_player.play.assert_called_once_with()
2391- mocked_seek.assert_called_once_with(mocked_display, 1000)
2392- mocked_volume.assert_called_once_with(mocked_display, 1)
2393- mocked_disconnect_slots.assert_called_once_with(mocked_display.media_player.durationChanged)
2394- mocked_display.media_player.durationChanged.connect.assert_called_once_with('function')
2395- mocked_set_state.assert_called_once_with(MediaState.Playing, mocked_display)
2396- mocked_display.video_widget.raise_.assert_called_once_with()
2397- assert result is True
2398-
2399- @patch('openlp.core.ui.media.systemplayer.functools')
2400- def test_play_is_preview(self, mocked_functools):
2401- """
2402- Test the play() method of the SystemPlayer on the preview display
2403- """
2404- # GIVEN: A SystemPlayer instance and a mocked display
2405- mocked_functools.partial.return_value = 'function'
2406- player = SystemPlayer(self)
2407- mocked_display = MagicMock()
2408- mocked_display.controller.is_live = False
2409- mocked_display.controller.media_info.start_time = 1
2410- mocked_display.controller.media_info.volume = 1
2411-
2412- # WHEN: play() is called
2413- with patch.object(player, 'get_preview_state') as mocked_get_preview_state, \
2414- patch.object(player, 'seek') as mocked_seek, \
2415- patch.object(player, 'volume') as mocked_volume, \
2416- patch.object(player, 'set_state') as mocked_set_state:
2417- mocked_get_preview_state.return_value = QtMultimedia.QMediaPlayer.PlayingState
2418- result = player.play(mocked_display)
2419-
2420- # THEN: the media file is played
2421- mocked_get_preview_state.assert_called_once_with()
2422- mocked_display.media_player.play.assert_called_once_with()
2423- mocked_seek.assert_called_once_with(mocked_display, 1000)
2424- mocked_volume.assert_called_once_with(mocked_display, 1)
2425- mocked_display.media_player.durationChanged.connect.assert_called_once_with('function')
2426- mocked_set_state.assert_called_once_with(MediaState.Playing, mocked_display)
2427- mocked_display.video_widget.raise_.assert_called_once_with()
2428- assert result is True
2429-
2430- def test_pause_is_live(self):
2431- """
2432- Test the pause() method of the SystemPlayer on the live display
2433- """
2434- # GIVEN: A SystemPlayer instance
2435- player = SystemPlayer(self)
2436- mocked_display = MagicMock()
2437- mocked_display.controller.is_live = True
2438-
2439- # WHEN: The pause method is called
2440- with patch.object(player, 'get_live_state') as mocked_get_live_state, \
2441- patch.object(player, 'set_state') as mocked_set_state:
2442- mocked_get_live_state.return_value = QtMultimedia.QMediaPlayer.PausedState
2443- player.pause(mocked_display)
2444-
2445- # THEN: The video is paused
2446- mocked_display.media_player.pause.assert_called_once_with()
2447- mocked_get_live_state.assert_called_once_with()
2448- mocked_set_state.assert_called_once_with(MediaState.Paused, mocked_display)
2449-
2450- def test_pause_is_preview(self):
2451- """
2452- Test the pause() method of the SystemPlayer on the preview display
2453- """
2454- # GIVEN: A SystemPlayer instance
2455- player = SystemPlayer(self)
2456- mocked_display = MagicMock()
2457- mocked_display.controller.is_live = False
2458-
2459- # WHEN: The pause method is called
2460- with patch.object(player, 'get_preview_state') as mocked_get_preview_state, \
2461- patch.object(player, 'set_state') as mocked_set_state:
2462- mocked_get_preview_state.return_value = QtMultimedia.QMediaPlayer.PausedState
2463- player.pause(mocked_display)
2464-
2465- # THEN: The video is paused
2466- mocked_display.media_player.pause.assert_called_once_with()
2467- mocked_get_preview_state.assert_called_once_with()
2468- mocked_set_state.assert_called_once_with(MediaState.Paused, mocked_display)
2469-
2470- def test_stop(self):
2471- """
2472- Test the stop() method of the SystemPlayer
2473- """
2474- # GIVEN: A SystemPlayer instance
2475- player = SystemPlayer(self)
2476- mocked_display = MagicMock()
2477-
2478- # WHEN: The stop method is called
2479- with patch.object(player, 'set_visible') as mocked_set_visible, \
2480- patch.object(player, 'set_state') as mocked_set_state:
2481- player.stop(mocked_display)
2482-
2483- # THEN: The video is stopped
2484- mocked_display.media_player.stop.assert_called_once_with()
2485- mocked_set_visible.assert_called_once_with(mocked_display, False)
2486- mocked_set_state.assert_called_once_with(MediaState.Stopped, mocked_display)
2487-
2488- def test_volume(self):
2489- """
2490- Test the volume() method of the SystemPlayer
2491- """
2492- # GIVEN: A SystemPlayer instance
2493- player = SystemPlayer(self)
2494- mocked_display = MagicMock()
2495- mocked_display.has_audio = True
2496-
2497- # WHEN: The stop method is called
2498- player.volume(mocked_display, 2)
2499-
2500- # THEN: The video is stopped
2501- mocked_display.media_player.setVolume.assert_called_once_with(2)
2502-
2503- def test_seek(self):
2504- """
2505- Test the seek() method of the SystemPlayer
2506- """
2507- # GIVEN: A SystemPlayer instance
2508- player = SystemPlayer(self)
2509- mocked_display = MagicMock()
2510-
2511- # WHEN: The stop method is called
2512- player.seek(mocked_display, 2)
2513-
2514- # THEN: The video is stopped
2515- mocked_display.media_player.setPosition.assert_called_once_with(2)
2516-
2517- def test_reset(self):
2518- """
2519- Test the reset() method of the SystemPlayer
2520- """
2521- # GIVEN: A SystemPlayer instance
2522- player = SystemPlayer(self)
2523- mocked_display = MagicMock()
2524-
2525- # WHEN: reset() is called
2526- with patch.object(player, 'set_state') as mocked_set_state, \
2527- patch.object(player, 'set_visible') as mocked_set_visible:
2528- player.reset(mocked_display)
2529-
2530- # THEN: The media player is reset
2531- mocked_display.media_player.stop()
2532- mocked_display.media_player.setMedia.assert_called_once_with(QtMultimedia.QMediaContent())
2533- mocked_set_visible.assert_called_once_with(mocked_display, False)
2534- mocked_display.video_widget.setVisible.assert_called_once_with(False)
2535- mocked_set_state.assert_called_once_with(MediaState.Off, mocked_display)
2536-
2537- def test_set_visible(self):
2538- """
2539- Test the set_visible() method on the SystemPlayer
2540- """
2541- # GIVEN: A SystemPlayer instance and a mocked display
2542- player = SystemPlayer(self)
2543- player.has_own_widget = True
2544- mocked_display = MagicMock()
2545-
2546- # WHEN: set_visible() is called
2547- player.set_visible(mocked_display, True)
2548-
2549- # THEN: The widget should be visible
2550- mocked_display.video_widget.setVisible.assert_called_once_with(True)
2551-
2552- def test_set_duration(self):
2553- """
2554- Test the set_duration() method of the SystemPlayer
2555- """
2556- # GIVEN: a mocked controller
2557- mocked_controller = MagicMock()
2558- mocked_controller.media_info.length = 5
2559-
2560- # WHEN: The set_duration() is called. NB: the 10 here is ignored by the code
2561- SystemPlayer.set_duration(mocked_controller, 10)
2562-
2563- # THEN: The maximum length of the slider should be set
2564- mocked_controller.seek_slider.setMaximum.assert_called_once_with(5)
2565-
2566- def test_update_ui(self):
2567- """
2568- Test the update_ui() method on the SystemPlayer
2569- """
2570- # GIVEN: A SystemPlayer instance
2571- player = SystemPlayer(self)
2572- player.state = [MediaState.Playing, MediaState.Playing]
2573- mocked_display = MagicMock()
2574- mocked_display.media_player.state.return_value = QtMultimedia.QMediaPlayer.PausedState
2575- mocked_display.controller.media_info.end_time = 1
2576- mocked_display.media_player.position.return_value = 2
2577- mocked_display.controller.seek_slider.isSliderDown.return_value = False
2578-
2579- # WHEN: update_ui() is called
2580- with patch.object(player, 'stop') as mocked_stop, \
2581- patch.object(player, 'set_visible') as mocked_set_visible:
2582- player.update_ui(mocked_display)
2583-
2584- # THEN: The UI is updated
2585- expected_stop_calls = [call(mocked_display)]
2586- expected_position_calls = [call(), call()]
2587- expected_block_signals_calls = [call(True), call(False)]
2588- mocked_display.media_player.state.assert_called_once_with()
2589- assert 1 == mocked_stop.call_count
2590- assert expected_stop_calls == mocked_stop.call_args_list
2591- assert 2 == mocked_display.media_player.position.call_count
2592- assert expected_position_calls == mocked_display.media_player.position.call_args_list
2593- mocked_set_visible.assert_called_once_with(mocked_display, False)
2594- mocked_display.controller.seek_slider.isSliderDown.assert_called_once_with()
2595- assert expected_block_signals_calls == mocked_display.controller.seek_slider.blockSignals.call_args_list
2596- mocked_display.controller.seek_slider.setSliderPosition.assert_called_once_with(2)
2597-
2598- def test_get_media_display_css(self):
2599- """
2600- Test the get_media_display_css() method of the SystemPlayer
2601- """
2602- # GIVEN: A SystemPlayer instance
2603- player = SystemPlayer(self)
2604-
2605- # WHEN: get_media_display_css() is called
2606- result = player.get_media_display_css()
2607-
2608- # THEN: The css should be empty
2609- assert '' == result
2610-
2611- @patch('openlp.core.ui.media.systemplayer.QtMultimedia.QMediaPlayer')
2612- def test_get_info(self, MockQMediaPlayer):
2613- """
2614- Test the get_info() method of the SystemPlayer
2615- """
2616- # GIVEN: A SystemPlayer instance
2617- mocked_media_player = MagicMock()
2618- mocked_media_player.supportedMimeTypes.return_value = []
2619- MockQMediaPlayer.return_value = mocked_media_player
2620- player = SystemPlayer(self)
2621-
2622- # WHEN: get_info() is called
2623- result = player.get_info()
2624-
2625- # THEN: The info should be correct
2626- expected_info = 'This media player uses your operating system to provide media capabilities.<br/> ' \
2627- '<strong>Audio</strong><br/>[]<br/><strong>Video</strong><br/>[]<br/>'
2628- assert expected_info == result
2629-
2630- @patch('openlp.core.ui.media.systemplayer.CheckMediaWorker')
2631- @patch('openlp.core.ui.media.systemplayer.run_thread')
2632- @patch('openlp.core.ui.media.systemplayer.is_thread_finished')
2633- def test_check_media(self, mocked_is_thread_finished, mocked_run_thread, MockCheckMediaWorker):
2634- """
2635- Test the check_media() method of the SystemPlayer
2636- """
2637- # GIVEN: A SystemPlayer instance and a mocked thread
2638- valid_file = '/path/to/video.ogv'
2639- mocked_application = MagicMock()
2640- Registry().create()
2641- Registry().register('application', mocked_application)
2642- player = SystemPlayer(self)
2643- mocked_is_thread_finished.side_effect = [False, True]
2644- mocked_check_media_worker = MagicMock()
2645- mocked_check_media_worker.result = True
2646- MockCheckMediaWorker.return_value = mocked_check_media_worker
2647-
2648- # WHEN: check_media() is called with a valid media file
2649- result = player.check_media(valid_file)
2650-
2651- # THEN: It should return True
2652- MockCheckMediaWorker.assert_called_once_with(valid_file)
2653- mocked_check_media_worker.setVolume.assert_called_once_with(0)
2654- mocked_run_thread.assert_called_once_with(mocked_check_media_worker, 'check_media')
2655- mocked_is_thread_finished.assert_called_with('check_media')
2656- assert mocked_is_thread_finished.call_count == 2, 'is_thread_finished() should have been called twice'
2657- mocked_application.processEvents.assert_called_once_with()
2658- assert result is True
2659-
2660-
2661-class TestCheckMediaWorker(TestCase):
2662- """
2663- Test the CheckMediaWorker class
2664- """
2665- def test_constructor(self):
2666- """
2667- Test the constructor of the CheckMediaWorker class
2668- """
2669- # GIVEN: A file path
2670- path = 'file.ogv'
2671-
2672- # WHEN: The CheckMediaWorker object is instantiated
2673- worker = CheckMediaWorker(path)
2674-
2675- # THEN: The correct values should be set up
2676- assert worker is not None
2677-
2678- @patch('openlp.core.ui.media.systemplayer.functools.partial')
2679- @patch('openlp.core.ui.media.systemplayer.QtMultimedia.QMediaContent')
2680- def test_start(self, MockQMediaContent, mocked_partial):
2681- """
2682- Test the start method
2683- """
2684- # GIVEN: A CheckMediaWorker instance
2685- worker = CheckMediaWorker('file.ogv')
2686- MockQMediaContent.side_effect = lambda x: x
2687- mocked_partial.side_effect = lambda x, y: y
2688-
2689- # WHEN: start() is called
2690- with patch.object(worker, 'error') as mocked_error, \
2691- patch.object(worker, 'mediaStatusChanged') as mocked_status_change, \
2692- patch.object(worker, 'setMedia') as mocked_set_media, \
2693- patch.object(worker, 'play') as mocked_play:
2694- worker.start()
2695-
2696- # THEN: The correct methods should be called
2697- mocked_error.connect.assert_called_once_with('error')
2698- mocked_status_change.connect.assert_called_once_with('media')
2699- mocked_set_media.assert_called_once_with(QtCore.QUrl('file:file.ogv'))
2700- mocked_play.assert_called_once_with()
2701-
2702- def test_signals_media(self):
2703- """
2704- Test the signals() signal of the CheckMediaWorker class with a "media" origin
2705- """
2706- # GIVEN: A CheckMediaWorker instance
2707- worker = CheckMediaWorker('file.ogv')
2708-
2709- # WHEN: signals() is called with media and BufferedMedia
2710- with patch.object(worker, 'stop') as mocked_stop, \
2711- patch.object(worker, 'quit') as mocked_quit:
2712- worker.signals('media', worker.BufferedMedia)
2713-
2714- # THEN: The worker should exit and the result should be True
2715- mocked_stop.assert_called_once_with()
2716- mocked_quit.emit.assert_called_once_with()
2717- assert worker.result is True
2718-
2719- def test_signals_error(self):
2720- """
2721- Test the signals() signal of the CheckMediaWorker class with a "error" origin
2722- """
2723- # GIVEN: A CheckMediaWorker instance
2724- worker = CheckMediaWorker('file.ogv')
2725-
2726- # WHEN: signals() is called with error and BufferedMedia
2727- with patch.object(worker, 'stop') as mocked_stop, \
2728- patch.object(worker, 'quit') as mocked_quit:
2729- worker.signals('error', None)
2730-
2731- # THEN: The worker should exit and the result should be True
2732- mocked_stop.assert_called_once_with()
2733- mocked_quit.emit.assert_called_once_with()
2734- assert worker.result is False
2735
2736=== modified file 'tests/functional/openlp_core/ui/media/test_vlcplayer.py'
2737--- tests/functional/openlp_core/ui/media/test_vlcplayer.py 2019-02-14 15:09:09 +0000
2738+++ tests/functional/openlp_core/ui/media/test_vlcplayer.py 2019-04-11 20:30:04 +0000
2739@@ -138,25 +138,24 @@
2740 mocked_vlc = MagicMock()
2741 mocked_vlc.Instance.return_value = mocked_instance
2742 mocked_get_vlc.return_value = mocked_vlc
2743- mocked_display = MagicMock()
2744- mocked_display.has_audio = False
2745- mocked_display.controller.is_live = True
2746- mocked_display.size.return_value = (10, 10)
2747+ mocked_output_display = MagicMock()
2748+ mocked_controller = MagicMock()
2749+ mocked_controller.is_live = True
2750+ mocked_output_display.size.return_value = (10, 10)
2751 vlc_player = VlcPlayer(None)
2752
2753 # WHEN: setup() is run
2754- vlc_player.setup(mocked_display)
2755+ vlc_player.setup(mocked_output_display, mocked_controller)
2756
2757 # THEN: The VLC widget should be set up correctly
2758- assert mocked_display.vlc_widget == mocked_qframe
2759+ assert mocked_output_display.vlc_widget == mocked_qframe
2760 mocked_qframe.setFrameStyle.assert_called_with(1)
2761 mocked_settings.value.assert_called_with('advanced/hide mouse')
2762- mocked_vlc.Instance.assert_called_with('--no-video-title-show --no-audio --no-video-title-show '
2763- '--mouse-hide-timeout=0')
2764- assert mocked_display.vlc_instance == mocked_instance
2765+ mocked_vlc.Instance.assert_called_with('--no-video-title-show --mouse-hide-timeout=0')
2766+ assert mocked_output_display.vlc_instance == mocked_instance
2767 mocked_instance.media_player_new.assert_called_with()
2768- assert mocked_display.vlc_media_player == mocked_media_player_new
2769- mocked_display.size.assert_called_with()
2770+ assert mocked_output_display.vlc_media_player == mocked_media_player_new
2771+ mocked_output_display.size.assert_called_with()
2772 mocked_qframe.resize.assert_called_with((10, 10))
2773 mocked_qframe.raise_.assert_called_with()
2774 mocked_qframe.hide.assert_called_with()
2775@@ -188,14 +187,14 @@
2776 mocked_vlc = MagicMock()
2777 mocked_vlc.Instance.return_value = mocked_instance
2778 mocked_get_vlc.return_value = mocked_vlc
2779- mocked_display = MagicMock()
2780- mocked_display.has_audio = True
2781- mocked_display.controller.is_live = True
2782- mocked_display.size.return_value = (10, 10)
2783+ mocked_output_display = MagicMock()
2784+ mocked_controller = MagicMock()
2785+ mocked_controller.is_live = True
2786+ mocked_output_display.size.return_value = (10, 10)
2787 vlc_player = VlcPlayer(None)
2788
2789 # WHEN: setup() is run
2790- vlc_player.setup(mocked_display)
2791+ vlc_player.setup(mocked_output_display, mocked_controller)
2792
2793 # THEN: The VLC instance should be created with the correct options
2794 mocked_vlc.Instance.assert_called_with('--no-video-title-show --mouse-hide-timeout=0')
2795@@ -226,17 +225,17 @@
2796 mocked_vlc = MagicMock()
2797 mocked_vlc.Instance.return_value = mocked_instance
2798 mocked_get_vlc.return_value = mocked_vlc
2799- mocked_display = MagicMock()
2800- mocked_display.has_audio = False
2801- mocked_display.controller.is_live = True
2802- mocked_display.size.return_value = (10, 10)
2803+ mocked_output_display = MagicMock()
2804+ mocked_controller = MagicMock()
2805+ mocked_controller.is_live = True
2806+ mocked_output_display.size.return_value = (10, 10)
2807 vlc_player = VlcPlayer(None)
2808
2809 # WHEN: setup() is run
2810- vlc_player.setup(mocked_display)
2811+ vlc_player.setup(mocked_output_display, mocked_controller)
2812
2813 # THEN: The VLC instance should be created with the correct options
2814- mocked_vlc.Instance.assert_called_with('--no-video-title-show --no-audio --no-video-title-show')
2815+ mocked_vlc.Instance.assert_called_with('--no-video-title-show')
2816
2817 @patch('openlp.core.ui.media.vlcplayer.is_win')
2818 @patch('openlp.core.ui.media.vlcplayer.is_macosx')
2819@@ -263,14 +262,14 @@
2820 mocked_vlc = MagicMock()
2821 mocked_vlc.Instance.return_value = mocked_instance
2822 mocked_get_vlc.return_value = mocked_vlc
2823- mocked_display = MagicMock()
2824- mocked_display.has_audio = False
2825- mocked_display.controller.is_live = True
2826- mocked_display.size.return_value = (10, 10)
2827+ mocked_output_display = MagicMock()
2828+ mocked_controller = MagicMock()
2829+ mocked_controller.is_live = True
2830+ mocked_output_display.size.return_value = (10, 10)
2831 vlc_player = VlcPlayer(None)
2832
2833 # WHEN: setup() is run
2834- vlc_player.setup(mocked_display)
2835+ vlc_player.setup(mocked_output_display, mocked_controller)
2836
2837 # THEN: set_hwnd should be called
2838 mocked_media_player_new.set_hwnd.assert_called_with(2)
2839@@ -300,14 +299,14 @@
2840 mocked_vlc = MagicMock()
2841 mocked_vlc.Instance.return_value = mocked_instance
2842 mocked_get_vlc.return_value = mocked_vlc
2843- mocked_display = MagicMock()
2844- mocked_display.has_audio = False
2845- mocked_display.controller.is_live = True
2846- mocked_display.size.return_value = (10, 10)
2847+ mocked_output_display = MagicMock()
2848+ mocked_controller = MagicMock()
2849+ mocked_controller.is_live = True
2850+ mocked_output_display.size.return_value = (10, 10)
2851 vlc_player = VlcPlayer(None)
2852
2853 # WHEN: setup() is run
2854- vlc_player.setup(mocked_display)
2855+ vlc_player.setup(mocked_output_display, mocked_controller)
2856
2857 # THEN: set_nsobject should be called
2858 mocked_media_player_new.set_nsobject.assert_called_with(2)
2859@@ -353,15 +352,13 @@
2860 mocked_normcase.side_effect = lambda x: x
2861 mocked_vlc = MagicMock()
2862 mocked_get_vlc.return_value = mocked_vlc
2863- mocked_controller = MagicMock()
2864- mocked_controller.media_info.volume = 100
2865- mocked_controller.media_info.media_type = MediaType.Video
2866- mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path
2867+ mocked_display = MagicMock()
2868+ mocked_display.media_info.volume = 100
2869+ mocked_display.media_info.media_type = MediaType.Video
2870+ mocked_display.media_info.file_info.absoluteFilePath.return_value = media_path
2871 mocked_vlc_media = MagicMock()
2872 mocked_media = MagicMock()
2873 mocked_media.get_duration.return_value = 10000
2874- mocked_display = MagicMock()
2875- mocked_display.controller = mocked_controller
2876 mocked_display.vlc_instance.media_new_path.return_value = mocked_vlc_media
2877 mocked_display.vlc_media_player.get_media.return_value = mocked_media
2878 vlc_player = VlcPlayer(None)
2879@@ -392,16 +389,13 @@
2880 mocked_normcase.side_effect = lambda x: x
2881 mocked_vlc = MagicMock()
2882 mocked_get_vlc.return_value = mocked_vlc
2883- mocked_controller = MagicMock()
2884- mocked_controller.media_info.volume = 100
2885- mocked_controller.media_info.media_type = MediaType.CD
2886- mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path
2887- mocked_controller.media_info.title_track = 1
2888+ mocked_display = MagicMock()
2889+ mocked_display.media_info.volume = 100
2890+ mocked_display.media_info.media_type = MediaType.CD
2891+ mocked_display.media_info.title_track = 1
2892 mocked_vlc_media = MagicMock()
2893 mocked_media = MagicMock()
2894 mocked_media.get_duration.return_value = 10000
2895- mocked_display = MagicMock()
2896- mocked_display.controller = mocked_controller
2897 mocked_display.vlc_instance.media_new_location.return_value = mocked_vlc_media
2898 mocked_display.vlc_media_player.get_media.return_value = mocked_media
2899 mocked_subitems = MagicMock()
2900@@ -437,16 +431,14 @@
2901 mocked_normcase.side_effect = lambda x: x
2902 mocked_vlc = MagicMock()
2903 mocked_get_vlc.return_value = mocked_vlc
2904- mocked_controller = MagicMock()
2905- mocked_controller.media_info.volume = 100
2906- mocked_controller.media_info.media_type = MediaType.CD
2907- mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path
2908- mocked_controller.media_info.title_track = 1
2909+ mocked_display = MagicMock()
2910+ mocked_display.media_info.volume = 100
2911+ mocked_display.media_info.media_type = MediaType.CD
2912+ mocked_display.media_info.file_info.absoluteFilePath.return_value = media_path
2913+ mocked_display.media_info.title_track = 1
2914 mocked_vlc_media = MagicMock()
2915 mocked_media = MagicMock()
2916 mocked_media.get_duration.return_value = 10000
2917- mocked_display = MagicMock()
2918- mocked_display.controller = mocked_controller
2919 mocked_display.vlc_instance.media_new_location.return_value = mocked_vlc_media
2920 mocked_display.vlc_media_player.get_media.return_value = mocked_media
2921 mocked_subitems = MagicMock()
2922@@ -482,16 +474,14 @@
2923 mocked_normcase.side_effect = lambda x: x
2924 mocked_vlc = MagicMock()
2925 mocked_get_vlc.return_value = mocked_vlc
2926- mocked_controller = MagicMock()
2927- mocked_controller.media_info.volume = 100
2928- mocked_controller.media_info.media_type = MediaType.CD
2929- mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path
2930- mocked_controller.media_info.title_track = 1
2931+ mocked_display = MagicMock()
2932+ mocked_display.media_info.volume = 100
2933+ mocked_display.media_info.media_type = MediaType.CD
2934+ mocked_display.media_info.file_info.absoluteFilePath.return_value = media_path
2935+ mocked_display.media_info.title_track = 1
2936 mocked_vlc_media = MagicMock()
2937 mocked_media = MagicMock()
2938 mocked_media.get_duration.return_value = 10000
2939- mocked_display = MagicMock()
2940- mocked_display.controller = mocked_controller
2941 mocked_display.vlc_instance.media_new_location.return_value = mocked_vlc_media
2942 mocked_display.vlc_media_player.get_media.return_value = mocked_media
2943 mocked_subitems = MagicMock()
2944@@ -611,29 +601,28 @@
2945 mocked_threading.Thread.return_value = mocked_thread
2946 mocked_vlc = MagicMock()
2947 mocked_get_vlc.return_value = mocked_vlc
2948- mocked_controller = MagicMock()
2949- mocked_controller.media_info.start_time = 0
2950- mocked_controller.media_info.media_type = MediaType.Video
2951- mocked_controller.media_info.volume = 100
2952+ mocked_display = MagicMock()
2953 mocked_media = MagicMock()
2954 mocked_media.get_duration.return_value = 50000
2955- mocked_display = MagicMock()
2956- mocked_display.controller = mocked_controller
2957- mocked_display.vlc_media_player.get_media.return_value = mocked_media
2958+ mocked_output_display = MagicMock()
2959+ mocked_output_display.media_info.start_time = 0
2960+ mocked_output_display.media_info.media_type = MediaType.Video
2961+ mocked_output_display.media_info.volume = 100
2962+ mocked_output_display.vlc_media_player.get_media.return_value = mocked_media
2963 vlc_player = VlcPlayer(None)
2964- vlc_player.set_state(MediaState.Paused, mocked_display)
2965+ vlc_player.set_state(MediaState.Paused, mocked_output_display)
2966
2967 # WHEN: play() is called
2968 with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \
2969 patch.object(vlc_player, 'volume') as mocked_volume:
2970 mocked_media_state_wait.return_value = True
2971- result = vlc_player.play(mocked_display)
2972+ result = vlc_player.play(mocked_display, mocked_output_display)
2973
2974 # THEN: A bunch of things should happen to play the media
2975 mocked_thread.start.assert_called_with()
2976- mocked_volume.assert_called_with(mocked_display, 100)
2977+ mocked_volume.assert_called_with(mocked_output_display, 100)
2978 assert MediaState.Playing == vlc_player.get_live_state()
2979- mocked_display.vlc_widget.raise_.assert_called_with()
2980+ mocked_output_display.vlc_widget.raise_.assert_called_with()
2981 assert result is True, 'The value returned from play() should be True'
2982
2983 @patch('openlp.core.ui.media.vlcplayer.threading')
2984@@ -649,16 +638,15 @@
2985 mocked_get_vlc.return_value = mocked_vlc
2986 mocked_controller = MagicMock()
2987 mocked_controller.media_info.start_time = 0
2988- mocked_display = MagicMock()
2989- mocked_display.controller = mocked_controller
2990+ mocked_output_display = MagicMock()
2991 vlc_player = VlcPlayer(None)
2992- vlc_player.set_state(MediaState.Paused, mocked_display)
2993+ vlc_player.set_state(MediaState.Paused, mocked_output_display)
2994
2995 # WHEN: play() is called
2996 with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \
2997 patch.object(vlc_player, 'volume'):
2998 mocked_media_state_wait.return_value = False
2999- result = vlc_player.play(mocked_display)
3000+ result = vlc_player.play(mocked_controller, mocked_output_display)
3001
3002 # THEN: A thread should be started, but the method should return False
3003 mocked_thread.start.assert_called_with()
3004@@ -676,33 +664,32 @@
3005 mocked_vlc = MagicMock()
3006 mocked_get_vlc.return_value = mocked_vlc
3007 mocked_controller = MagicMock()
3008- mocked_controller.media_info.start_time = 0
3009- mocked_controller.media_info.end_time = 50
3010- mocked_controller.media_info.media_type = MediaType.DVD
3011- mocked_controller.media_info.volume = 100
3012- mocked_controller.media_info.title_track = 1
3013- mocked_controller.media_info.audio_track = 1
3014- mocked_controller.media_info.subtitle_track = 1
3015- mocked_display = MagicMock()
3016- mocked_display.controller = mocked_controller
3017+ mocked_output_display = MagicMock()
3018+ mocked_output_display.media_info.start_time = 0
3019+ mocked_output_display.media_info.end_time = 50
3020+ mocked_output_display.media_info.media_type = MediaType.DVD
3021+ mocked_output_display.media_info.volume = 100
3022+ mocked_output_display.media_info.title_track = 1
3023+ mocked_output_display.media_info.audio_track = 1
3024+ mocked_output_display.media_info.subtitle_track = 1
3025 vlc_player = VlcPlayer(None)
3026- vlc_player.set_state(MediaState.Paused, mocked_display)
3027+ vlc_player.set_state(MediaState.Paused, mocked_output_display)
3028
3029 # WHEN: play() is called
3030 with patch.object(vlc_player, 'media_state_wait', return_value=True), \
3031 patch.object(vlc_player, 'volume') as mocked_volume, \
3032 patch.object(vlc_player, 'get_live_state', return_value=MediaState.Loaded):
3033- result = vlc_player.play(mocked_display)
3034+ result = vlc_player.play(mocked_controller, mocked_output_display)
3035
3036 # THEN: A bunch of things should happen to play the media
3037 mocked_thread.start.assert_called_with()
3038- mocked_display.vlc_media_player.set_title.assert_called_with(1)
3039- mocked_display.vlc_media_player.play.assert_called_with()
3040- mocked_display.vlc_media_player.audio_set_track.assert_called_with(1)
3041- mocked_display.vlc_media_player.video_set_spu.assert_called_with(1)
3042- mocked_volume.assert_called_with(mocked_display, 100)
3043+ mocked_output_display.vlc_media_player.set_title.assert_called_with(1)
3044+ mocked_output_display.vlc_media_player.play.assert_called_with()
3045+ mocked_output_display.vlc_media_player.audio_set_track.assert_called_with(1)
3046+ mocked_output_display.vlc_media_player.video_set_spu.assert_called_with(1)
3047+ mocked_volume.assert_called_with(mocked_output_display, 100)
3048 assert MediaState.Playing == vlc_player.get_live_state()
3049- mocked_display.vlc_widget.raise_.assert_called_with()
3050+ mocked_output_display.vlc_widget.raise_.assert_called_with()
3051 assert result is True, 'The value returned from play() should be True'
3052
3053 @patch('openlp.core.ui.media.vlcplayer.get_vlc')
3054@@ -937,7 +924,6 @@
3055 mocked_controller.media_info.end_time = 300
3056 mocked_controller.seek_slider.isSliderDown.return_value = False
3057 mocked_display = MagicMock()
3058- mocked_display.controller = mocked_controller
3059 mocked_display.vlc_media.get_state.return_value = 1
3060 mocked_display.vlc_media_player.get_time.return_value = 400000
3061 vlc_player = VlcPlayer(None)
3062@@ -945,7 +931,7 @@
3063 # WHEN: update_ui() is called
3064 with patch.object(vlc_player, 'stop') as mocked_stop, \
3065 patch.object(vlc_player, 'set_visible') as mocked_set_visible:
3066- vlc_player.update_ui(mocked_display)
3067+ vlc_player.update_ui(mocked_controller, mocked_display)
3068
3069 # THEN: Certain methods should be called
3070 mocked_stop.assert_called_with(mocked_display)
3071@@ -970,22 +956,19 @@
3072 mocked_controller.media_info.end_time = 300
3073 mocked_controller.seek_slider.isSliderDown.return_value = False
3074 mocked_display = MagicMock()
3075- mocked_display.controller = mocked_controller
3076 mocked_display.vlc_media.get_state.return_value = 1
3077- mocked_display.vlc_media_player.get_time.return_value = 400
3078+ mocked_display.vlc_media_player.get_time.return_value = 300
3079 mocked_display.controller.media_info.media_type = MediaType.DVD
3080 vlc_player = VlcPlayer(None)
3081
3082 # WHEN: update_ui() is called
3083- with patch.object(vlc_player, 'stop') as mocked_stop, \
3084- patch.object(vlc_player, 'set_visible') as mocked_set_visible:
3085- vlc_player.update_ui(mocked_display)
3086+ with patch.object(vlc_player, 'stop') as mocked_stop:
3087+ vlc_player.update_ui(mocked_controller, mocked_display)
3088
3089 # THEN: Certain methods should be called
3090 mocked_stop.assert_called_with(mocked_display)
3091- assert 2 == mocked_stop.call_count
3092+ assert 1 == mocked_stop.call_count
3093 mocked_display.vlc_media_player.get_time.assert_called_with()
3094- mocked_set_visible.assert_called_with(mocked_display, False)
3095 mocked_controller.seek_slider.setSliderPosition.assert_called_with(300)
3096 expected_calls = [call(True), call(False)]
3097 assert expected_calls == mocked_controller.seek_slider.blockSignals.call_args_list