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
=== modified file '.bzrignore'
--- .bzrignore 2019-03-04 20:37:11 +0000
+++ .bzrignore 2019-04-11 20:30:04 +0000
@@ -15,6 +15,7 @@
15*.e4*15*.e4*
16*eric[1-9]project16*eric[1-9]project
17.git17.git
18env
18# Git files19# Git files
19.gitignore20.gitignore
20htmlcov21htmlcov
2122
=== modified file 'openlp/core/common/settings.py'
--- openlp/core/common/settings.py 2019-03-03 13:32:31 +0000
+++ openlp/core/common/settings.py 2019-04-11 20:30:04 +0000
@@ -129,6 +129,9 @@
129 ``advanced/slide limits`` to ``SlideLimits.Wrap``. **NOTE**, this means that the rules have to cover all cases!129 ``advanced/slide limits`` to ``SlideLimits.Wrap``. **NOTE**, this means that the rules have to cover all cases!
130 So, if the type of the old value is bool, then there must be two rules.130 So, if the type of the old value is bool, then there must be two rules.
131 """131 """
132 on_monitor_default = True
133 if log.isEnabledFor(logging.DEBUG):
134 on_monitor_default = False
132 __default_settings__ = {135 __default_settings__ = {
133 'settings/version': 0,136 'settings/version': 0,
134 'advanced/add page break': False,137 'advanced/add page break': False,
@@ -185,6 +188,7 @@
185 'core/click live slide to unblank': False,188 'core/click live slide to unblank': False,
186 'core/blank warning': False,189 'core/blank warning': False,
187 'core/ccli number': '',190 'core/ccli number': '',
191 'core/experimental': False,
188 'core/has run wizard': False,192 'core/has run wizard': False,
189 'core/language': '[en]',193 'core/language': '[en]',
190 'core/last version test': '',194 'core/last version test': '',
@@ -202,13 +206,13 @@
202 'core/view mode': 'default',206 'core/view mode': 'default',
203 # The other display settings (display position and dimensions) are defined in the ScreenList class due to a207 # The other display settings (display position and dimensions) are defined in the ScreenList class due to a
204 # circular dependency.208 # circular dependency.
205 'core/display on monitor': True,209 'core/display on monitor': on_monitor_default,
206 'core/override position': False,210 'core/override position': False,
207 'core/monitor': {},211 'core/monitor': {},
208 'core/application version': '0.0',212 'core/application version': '0.0',
209 'images/background color': '#000000',213 'images/background color': '#000000',
210 'media/players': 'system,webkit',214 'media/media auto start': QtCore.Qt.Unchecked,
211 'media/override player': QtCore.Qt.Unchecked,215 'media/stream command': '',
212 'remotes/download version': '0.0',216 'remotes/download version': '0.0',
213 'players/background color': '#000000',217 'players/background color': '#000000',
214 'servicemanager/last directory': None,218 'servicemanager/last directory': None,
@@ -311,7 +315,9 @@
311 ('bibles/proxy name', '', []), # Just remove these bible proxy settings. They weren't used in 2.4!315 ('bibles/proxy name', '', []), # Just remove these bible proxy settings. They weren't used in 2.4!
312 ('bibles/proxy address', '', []),316 ('bibles/proxy address', '', []),
313 ('bibles/proxy username', '', []),317 ('bibles/proxy username', '', []),
314 ('bibles/proxy password', '', [])318 ('bibles/proxy password', '', []),
319 ('media/players', '', []),
320 ('media/override player', '', [])
315 ]321 ]
316322
317 @staticmethod323 @staticmethod
318324
=== modified file 'openlp/core/lib/theme.py'
--- openlp/core/lib/theme.py 2019-02-14 15:09:09 +0000
+++ openlp/core/lib/theme.py 2019-04-11 20:30:04 +0000
@@ -46,6 +46,7 @@
46 Image = 246 Image = 2
47 Transparent = 347 Transparent = 3
48 Video = 448 Video = 4
49 Stream = 5
4950
50 @staticmethod51 @staticmethod
51 def to_string(background_type):52 def to_string(background_type):
@@ -62,6 +63,8 @@
62 return 'transparent'63 return 'transparent'
63 elif background_type == BackgroundType.Video:64 elif background_type == BackgroundType.Video:
64 return 'video'65 return 'video'
66 elif background_type == BackgroundType.Stream:
67 return 'stream'
6568
66 @staticmethod69 @staticmethod
67 def from_string(type_string):70 def from_string(type_string):
@@ -78,6 +81,8 @@
78 return BackgroundType.Transparent81 return BackgroundType.Transparent
79 elif type_string == 'video':82 elif type_string == 'video':
80 return BackgroundType.Video83 return BackgroundType.Video
84 elif type_string == 'stream':
85 return BackgroundType.Stream
8186
8287
83class BackgroundGradientType(object):88class BackgroundGradientType(object):
8489
=== modified file 'openlp/core/ui/media/__init__.py'
--- openlp/core/ui/media/__init__.py 2019-03-17 10:01:52 +0000
+++ openlp/core/ui/media/__init__.py 2019-04-11 20:30:04 +0000
@@ -48,6 +48,7 @@
48 CD = 348 CD = 3
49 DVD = 449 DVD = 4
50 Folder = 550 Folder = 5
51 Stream = 6
5152
5253
53class ItemMediaInfo(object):54class ItemMediaInfo(object):
5455
=== modified file 'openlp/core/ui/media/mediacontroller.py'
--- openlp/core/ui/media/mediacontroller.py 2019-02-14 15:09:09 +0000
+++ openlp/core/ui/media/mediacontroller.py 2019-04-11 20:30:04 +0000
@@ -23,7 +23,6 @@
23The :mod:`~openlp.core.ui.media.mediacontroller` module contains a base class for media components and other widgets23The :mod:`~openlp.core.ui.media.mediacontroller` module contains a base class for media components and other widgets
24related to playing media, such as sliders.24related to playing media, such as sliders.
25"""25"""
26import datetime
27import logging26import logging
2827
29try:28try:
@@ -33,7 +32,7 @@
33 pymediainfo_available = False32 pymediainfo_available = False
3433
35from subprocess import check_output34from subprocess import check_output
36from PyQt5 import QtCore, QtWidgets35from PyQt5 import QtCore
3736
38from openlp.core.state import State37from openlp.core.state import State
39from openlp.core.api.http import register_endpoint38from openlp.core.api.http import register_endpoint
@@ -44,11 +43,9 @@
44from openlp.core.lib.serviceitem import ItemCapabilities43from openlp.core.lib.serviceitem import ItemCapabilities
45from openlp.core.lib.ui import critical_error_message_box44from openlp.core.lib.ui import critical_error_message_box
46from openlp.core.ui import DisplayControllerType45from openlp.core.ui import DisplayControllerType
47from openlp.core.ui.icons import UiIcons
48from openlp.core.ui.media import MediaState, ItemMediaInfo, MediaType, parse_optical_path46from openlp.core.ui.media import MediaState, ItemMediaInfo, MediaType, parse_optical_path
49from openlp.core.ui.media.endpoint import media_endpoint47from openlp.core.ui.media.endpoint import media_endpoint
50from openlp.core.ui.media.vlcplayer import VlcPlayer, get_vlc48from openlp.core.ui.media.vlcplayer import VlcPlayer, get_vlc
51from openlp.core.widgets.toolbar import OpenLPToolbar
5249
5350
54log = logging.getLogger(__name__)51log = logging.getLogger(__name__)
@@ -56,45 +53,6 @@
56TICK_TIME = 20053TICK_TIME = 200
5754
5855
59class MediaSlider(QtWidgets.QSlider):
60 """
61 Allows the mouse events of a slider to be overridden and extra functionality added
62 """
63 def __init__(self, direction, manager, controller):
64 """
65 Constructor
66 """
67 super(MediaSlider, self).__init__(direction)
68 self.manager = manager
69 self.controller = controller
70
71 def mouseMoveEvent(self, event):
72 """
73 Override event to allow hover time to be displayed.
74
75 :param event: The triggering event
76 """
77 time_value = QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width())
78 self.setToolTip('%s' % datetime.timedelta(seconds=int(time_value / 1000)))
79 QtWidgets.QSlider.mouseMoveEvent(self, event)
80
81 def mousePressEvent(self, event):
82 """
83 Mouse Press event no new functionality
84 :param event: The triggering event
85 """
86 QtWidgets.QSlider.mousePressEvent(self, event)
87
88 def mouseReleaseEvent(self, event):
89 """
90 Set the slider position when the mouse is clicked and released on the slider.
91
92 :param event: The triggering event
93 """
94 self.setValue(QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width()))
95 QtWidgets.QSlider.mouseReleaseEvent(self, event)
96
97
98class MediaController(RegistryBase, LogMixin, RegistryProperties):56class MediaController(RegistryBase, LogMixin, RegistryProperties):
99 """57 """
100 The implementation of the Media Controller. The Media Controller adds an own class for every Player.58 The implementation of the Media Controller. The Media Controller adds an own class for every Player.
@@ -116,7 +74,6 @@
11674
117 def setup(self):75 def setup(self):
118 self.vlc_player = None76 self.vlc_player = None
119 self.display_controllers = {}
120 self.current_media_players = {}77 self.current_media_players = {}
121 # Timer for video state78 # Timer for video state
122 self.live_timer = QtCore.QTimer()79 self.live_timer = QtCore.QTimer()
@@ -168,117 +125,71 @@
168 self.setup()125 self.setup()
169 self.vlc_player = VlcPlayer(self)126 self.vlc_player = VlcPlayer(self)
170 State().add_service("mediacontroller", 0)127 State().add_service("mediacontroller", 0)
128 State().add_service("media_live", 0, requires="mediacontroller")
171 if get_vlc() and pymediainfo_available:129 if get_vlc() and pymediainfo_available:
172 State().update_pre_conditions("mediacontroller", True)130 State().update_pre_conditions("mediacontroller", True)
131 State().update_pre_conditions('media_live', True)
173 else:132 else:
174 State().missing_text("mediacontroller", translate('OpenLP.SlideController',133 State().missing_text("mediacontroller", translate('OpenLP.SlideController',
175 "VLC or pymediainfo are missing, so you are unable to play any media"))134 "VLC or pymediainfo are missing, so you are unable to play any media"))
176 self._generate_extensions_lists()135 self._generate_extensions_lists()
177 return True136 return True
178137
138 def bootstrap_post_set_up(self):
139 """
140 Set up the controllers.
141 :return:
142 """
143 try:
144 self.setup_display(self.live_controller.display, False)
145 except AttributeError:
146 State().update_pre_conditions('media_live', False)
147 self.setup_display(self.preview_controller.preview_display, True)
148
149 def display_controllers(self, controller_type):
150 """
151 Decides which controller to use.
152
153 :param controller_type: The controller type where a player will be placed
154 """
155 if controller_type == DisplayControllerType.Live:
156 return self.live_controller
157 else:
158 return self.preview_controller
159
179 def media_state_live(self):160 def media_state_live(self):
180 """161 """
181 Check if there is a running Live media Player and do updating stuff (e.g. update the UI)162 Check if there is a running Live media Player and do updating stuff (e.g. update the UI)
182 """163 """
183 display = self._define_display(self.display_controllers[DisplayControllerType.Live])164 display = self._define_display(self.display_controllers(DisplayControllerType.Live))
184 if DisplayControllerType.Live in self.current_media_players:165 if DisplayControllerType.Live in self.current_media_players:
185 self.current_media_players[DisplayControllerType.Live].resize(display)166 self.current_media_players[DisplayControllerType.Live].resize(display)
186 self.current_media_players[DisplayControllerType.Live].update_ui(display)167 self.current_media_players[DisplayControllerType.Live].update_ui(self.live_controller, display)
187 self.tick(self.display_controllers[DisplayControllerType.Live])168 self.tick(self.display_controllers(DisplayControllerType.Live))
188 if self.current_media_players[DisplayControllerType.Live].get_live_state() is not MediaState.Playing:169 if self.current_media_players[DisplayControllerType.Live].get_live_state() is not MediaState.Playing:
189 self.live_timer.stop()170 self.live_timer.stop()
190 else:171 else:
191 self.live_timer.stop()172 self.live_timer.stop()
192 self.media_stop(self.display_controllers[DisplayControllerType.Live])173 self.media_stop(self.display_controllers(DisplayControllerType.Live))
193 if self.display_controllers[DisplayControllerType.Live].media_info.can_loop_playback:174 if self.display_controllers(DisplayControllerType.Live).media_info.can_loop_playback:
194 self.media_play(self.display_controllers[DisplayControllerType.Live], True)175 self.media_play(self.display_controllers(DisplayControllerType.Live), True)
195176
196 def media_state_preview(self):177 def media_state_preview(self):
197 """178 """
198 Check if there is a running Preview media Player and do updating stuff (e.g. update the UI)179 Check if there is a running Preview media Player and do updating stuff (e.g. update the UI)
199 """180 """
200 display = self._define_display(self.display_controllers[DisplayControllerType.Preview])181 display = self._define_display(self.display_controllers(DisplayControllerType.Preview))
201 if DisplayControllerType.Preview in self.current_media_players:182 if DisplayControllerType.Preview in self.current_media_players:
202 self.current_media_players[DisplayControllerType.Preview].resize(display)183 self.current_media_players[DisplayControllerType.Preview].resize(display)
203 self.current_media_players[DisplayControllerType.Preview].update_ui(display)184 self.current_media_players[DisplayControllerType.Preview].update_ui(self.preview_controller, display)
204 self.tick(self.display_controllers[DisplayControllerType.Preview])185 self.tick(self.display_controllers(DisplayControllerType.Preview))
205 if self.current_media_players[DisplayControllerType.Preview].get_preview_state() is not MediaState.Playing:186 if self.current_media_players[DisplayControllerType.Preview].get_preview_state() is not MediaState.Playing:
206 self.preview_timer.stop()187 self.preview_timer.stop()
207 else:188 else:
208 self.preview_timer.stop()189 self.preview_timer.stop()
209 self.media_stop(self.display_controllers[DisplayControllerType.Preview])190 self.media_stop(self.display_controllers(DisplayControllerType.Preview))
210 if self.display_controllers[DisplayControllerType.Preview].media_info.can_loop_playback:191 if self.display_controllers(DisplayControllerType.Preview).media_info.can_loop_playback:
211 self.media_play(self.display_controllers[DisplayControllerType.Preview], True)192 self.media_play(self.display_controllers(DisplayControllerType.Preview), True)
212
213 def register_controller(self, controller):
214 """
215 Registers media controls where the players will be placed to run.
216
217 :param controller: The controller where a player will be placed
218 """
219 self.display_controllers[controller.controller_type] = controller
220 self.setup_generic_controls(controller)
221
222 def setup_generic_controls(self, controller):
223 """
224 Set up controls on the control_panel for a given controller
225
226 :param controller: First element is the controller which should be used
227 """
228 controller.media_info = ItemMediaInfo()
229 # Build a Media ToolBar
230 controller.mediabar = OpenLPToolbar(controller)
231 controller.mediabar.add_toolbar_action('playbackPlay', text='media_playback_play',
232 icon=UiIcons().play,
233 tooltip=translate('OpenLP.SlideController', 'Start playing media.'),
234 triggers=controller.send_to_plugins)
235 controller.mediabar.add_toolbar_action('playbackPause', text='media_playback_pause',
236 icon=UiIcons().pause,
237 tooltip=translate('OpenLP.SlideController', 'Pause playing media.'),
238 triggers=controller.send_to_plugins)
239 controller.mediabar.add_toolbar_action('playbackStop', text='media_playback_stop',
240 icon=UiIcons().stop,
241 tooltip=translate('OpenLP.SlideController', 'Stop playing media.'),
242 triggers=controller.send_to_plugins)
243 controller.mediabar.add_toolbar_action('playbackLoop', text='media_playback_loop',
244 icon=UiIcons().repeat, checked=False,
245 tooltip=translate('OpenLP.SlideController', 'Loop playing media.'),
246 triggers=controller.send_to_plugins)
247 controller.position_label = QtWidgets.QLabel()
248 controller.position_label.setText(' 00:00 / 00:00')
249 controller.position_label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
250 controller.position_label.setToolTip(translate('OpenLP.SlideController', 'Video timer.'))
251 controller.position_label.setMinimumSize(90, 0)
252 controller.position_label.setObjectName('position_label')
253 controller.mediabar.add_toolbar_widget(controller.position_label)
254 # Build the seek_slider.
255 controller.seek_slider = MediaSlider(QtCore.Qt.Horizontal, self, controller)
256 controller.seek_slider.setMaximum(1000)
257 controller.seek_slider.setTracking(True)
258 controller.seek_slider.setMouseTracking(True)
259 controller.seek_slider.setToolTip(translate('OpenLP.SlideController', 'Video position.'))
260 controller.seek_slider.setGeometry(QtCore.QRect(90, 260, 221, 24))
261 controller.seek_slider.setObjectName('seek_slider')
262 controller.mediabar.add_toolbar_widget(controller.seek_slider)
263 # Build the volume_slider.
264 controller.volume_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
265 controller.volume_slider.setTickInterval(10)
266 controller.volume_slider.setTickPosition(QtWidgets.QSlider.TicksAbove)
267 controller.volume_slider.setMinimum(0)
268 controller.volume_slider.setMaximum(100)
269 controller.volume_slider.setTracking(True)
270 controller.volume_slider.setToolTip(translate('OpenLP.SlideController', 'Audio Volume.'))
271 controller.volume_slider.setValue(controller.media_info.volume)
272 controller.volume_slider.setGeometry(QtCore.QRect(90, 160, 221, 24))
273 controller.volume_slider.setObjectName('volume_slider')
274 controller.mediabar.add_toolbar_widget(controller.volume_slider)
275 controller.controller_layout.addWidget(controller.mediabar)
276 controller.mediabar.setVisible(False)
277 if not controller.is_live:
278 controller.volume_slider.setEnabled(False)
279 # Signals
280 controller.seek_slider.valueChanged.connect(controller.send_to_plugins)
281 controller.volume_slider.valueChanged.connect(controller.send_to_plugins)
282193
283 def setup_display(self, display, preview):194 def setup_display(self, display, preview):
284 """195 """
@@ -287,14 +198,13 @@
287 :param display: Display on which the output is to be played198 :param display: Display on which the output is to be played
288 :param preview: Whether the display is a main or preview display199 :param preview: Whether the display is a main or preview display
289 """200 """
290 # clean up possible running old media files201 display.media_info = ItemMediaInfo()
291 self.finalise()
292 display.has_audio = True202 display.has_audio = True
293 if display.is_live and preview:203 # if display.is_live and preview:
294 return204 # return
295 if preview:205 if preview:
296 display.has_audio = False206 display.has_audio = False
297 self.vlc_player.setup(display)207 self.vlc_player.setup(display, preview)
298208
299 def set_controls_visible(self, controller, value):209 def set_controls_visible(self, controller, value):
300 """210 """
@@ -305,9 +215,9 @@
305 """215 """
306 # Generic controls216 # Generic controls
307 controller.mediabar.setVisible(value)217 controller.mediabar.setVisible(value)
308 if controller.is_live and controller.display:218 # if controller.is_live and controller.display:
309 if self.current_media_players and value:219 # if self.current_media_players and value:
310 controller.display.set_transparency(False)220 # controller.display.set_transparency(False)
311221
312 @staticmethod222 @staticmethod
313 def resize(display, player):223 def resize(display, player):
@@ -319,7 +229,7 @@
319 """229 """
320 player.resize(display)230 player.resize(display)
321231
322 def video(self, source, service_item, hidden=False, video_behind_text=False):232 def load_video(self, source, service_item, hidden=False, video_behind_text=False):
323 """233 """
324 Loads and starts a video to run with the option of sound234 Loads and starts a video to run with the option of sound
325235
@@ -329,7 +239,7 @@
329 :param video_behind_text: Is the video to be played behind text.239 :param video_behind_text: Is the video to be played behind text.
330 """240 """
331 is_valid = True241 is_valid = True
332 controller = self.display_controllers[source]242 controller = self.display_controllers(source)
333 # stop running videos243 # stop running videos
334 self.media_reset(controller)244 self.media_reset(controller)
335 controller.media_info = ItemMediaInfo()245 controller.media_info = ItemMediaInfo()
@@ -354,8 +264,8 @@
354 log.debug('video is not optical and live')264 log.debug('video is not optical and live')
355 controller.media_info.length = service_item.media_length265 controller.media_info.length = service_item.media_length
356 is_valid = self._check_file_type(controller, display)266 is_valid = self._check_file_type(controller, display)
357 display.override['theme'] = ''267 # display.override['theme'] = ''
358 display.override['video'] = True268 # display.override['video'] = True
359 if controller.media_info.is_background:269 if controller.media_info.is_background:
360 # ignore start/end time270 # ignore start/end time
361 controller.media_info.start_time = 0271 controller.media_info.start_time = 0
@@ -379,10 +289,10 @@
379 critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'),289 critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'),
380 translate('MediaPlugin.MediaItem', 'Unsupported File'))290 translate('MediaPlugin.MediaItem', 'Unsupported File'))
381 return False291 return False
382 log.debug('video mediatype: ' + str(controller.media_info.media_type))292 log.debug('video media type: ' + str(controller.media_info.media_type))
383 # dont care about actual theme, set a black background293 # dont care about actual theme, set a black background
384 if controller.is_live and not controller.media_info.is_background:294 # if controller.is_live and not controller.media_info.is_background:
385 display.frame.runJavaScript('show_video("setBackBoard", null, null,"visible");')295 # display.frame.runJavaScript('show_video("setBackBoard", null, null,"visible");')
386 # now start playing - Preview is autoplay!296 # now start playing - Preview is autoplay!
387 autoplay = False297 autoplay = False
388 # Preview requested298 # Preview requested
@@ -471,28 +381,26 @@
471 for file in controller.media_info.file_info:381 for file in controller.media_info.file_info:
472 if file.is_file:382 if file.is_file:
473 suffix = '*%s' % file.suffix.lower()383 suffix = '*%s' % file.suffix.lower()
474 player = self.vlc_player
475 file = str(file)384 file = str(file)
476 if suffix in player.video_extensions_list:385 if suffix in self.vlc_player.video_extensions_list:
477 if not controller.media_info.is_background or controller.media_info.is_background and \386 if not controller.media_info.is_background or controller.media_info.is_background and \
478 player.can_background:387 self.vlc_player.can_background:
479 self.resize(display, player)388 self.resize(display, self.vlc_player)
480 if player.load(display, file):389 if self.vlc_player.load(display, file):
481 self.current_media_players[controller.controller_type] = player390 self.current_media_players[controller.controller_type] = self.vlc_player
482 controller.media_info.media_type = MediaType.Video391 controller.media_info.media_type = MediaType.Video
483 return True392 return True
484 if suffix in player.audio_extensions_list:393 if suffix in self.vlc_player.audio_extensions_list:
485 if player.load(display, file):394 if self.vlc_player.load(display, file):
486 self.current_media_players[controller.controller_type] = player395 self.current_media_players[controller.controller_type] = self.vlc_player
487 controller.media_info.media_type = MediaType.Audio396 controller.media_info.media_type = MediaType.Audio
488 return True397 return True
489 else:398 else:
490 player = self.vlc_player
491 file = str(file)399 file = str(file)
492 if player.can_folder:400 if self.vlc_player.can_folder:
493 self.resize(display, player)401 self.resize(display, self.vlc_player)
494 if player.load(display, file):402 if self.vlc_player.load(display, file):
495 self.current_media_players[controller.controller_type] = player403 self.current_media_players[controller.controller_type] = self.vlc_player
496 controller.media_info.media_type = MediaType.Video404 controller.media_info.media_type = MediaType.Video
497 return True405 return True
498 return False406 return False
@@ -509,8 +417,6 @@
509 def on_media_play(self):417 def on_media_play(self):
510 """418 """
511 Responds to the request to play a loaded video from the web.419 Responds to the request to play a loaded video from the web.
512
513 :param msg: First element is the controller which should be used
514 """420 """
515 self.media_play(Registry().get('live_controller'), False)421 self.media_play(Registry().get('live_controller'), False)
516422
@@ -524,7 +430,7 @@
524 controller.seek_slider.blockSignals(True)430 controller.seek_slider.blockSignals(True)
525 controller.volume_slider.blockSignals(True)431 controller.volume_slider.blockSignals(True)
526 display = self._define_display(controller)432 display = self._define_display(controller)
527 if not self.current_media_players[controller.controller_type].play(display):433 if not self.current_media_players[controller.controller_type].play(controller, display):
528 controller.seek_slider.blockSignals(False)434 controller.seek_slider.blockSignals(False)
529 controller.volume_slider.blockSignals(False)435 controller.volume_slider.blockSignals(False)
530 return False436 return False
@@ -533,8 +439,8 @@
533 else:439 else:
534 self.media_volume(controller, controller.media_info.volume)440 self.media_volume(controller, controller.media_info.volume)
535 if first_time:441 if first_time:
536 if not controller.media_info.is_background:442 # if not controller.media_info.is_background:
537 display.frame.runJavaScript('show_blank("desktop");')443 # display.frame.runJavaScript('show_blank("desktop");')
538 self.current_media_players[controller.controller_type].set_visible(display, True)444 self.current_media_players[controller.controller_type].set_visible(display, True)
539 controller.mediabar.actions['playbackPlay'].setVisible(False)445 controller.mediabar.actions['playbackPlay'].setVisible(False)
540 controller.mediabar.actions['playbackPause'].setVisible(True)446 controller.mediabar.actions['playbackPause'].setVisible(True)
@@ -591,8 +497,6 @@
591 def on_media_pause(self):497 def on_media_pause(self):
592 """498 """
593 Responds to the request to pause a loaded video from the web.499 Responds to the request to pause a loaded video from the web.
594
595 :param msg: First element is the controller which should be used
596 """500 """
597 self.media_pause(Registry().get('live_controller'))501 self.media_pause(Registry().get('live_controller'))
598502
@@ -639,8 +543,6 @@
639 def on_media_stop(self):543 def on_media_stop(self):
640 """544 """
641 Responds to the request to stop a loaded video from the web.545 Responds to the request to stop a loaded video from the web.
642
643 :param msg: First element is the controller which should be used
644 """546 """
645 self.media_stop(Registry().get('live_controller'))547 self.media_stop(Registry().get('live_controller'))
646548
@@ -653,8 +555,8 @@
653 """555 """
654 display = self._define_display(controller)556 display = self._define_display(controller)
655 if controller.controller_type in self.current_media_players:557 if controller.controller_type in self.current_media_players:
656 if not looping_background:558 # if not looping_background:
657 display.frame.runJavaScript('show_blank("black");')559 # display.frame.runJavaScript('show_blank("black");')
658 self.current_media_players[controller.controller_type].stop(display)560 self.current_media_players[controller.controller_type].stop(display)
659 self.current_media_players[controller.controller_type].set_visible(display, False)561 self.current_media_players[controller.controller_type].set_visible(display, False)
660 controller.seek_slider.setSliderPosition(0)562 controller.seek_slider.setSliderPosition(0)
@@ -725,7 +627,7 @@
725 display.override = {}627 display.override = {}
726 self.current_media_players[controller.controller_type].reset(display)628 self.current_media_players[controller.controller_type].reset(display)
727 self.current_media_players[controller.controller_type].set_visible(display, False)629 self.current_media_players[controller.controller_type].set_visible(display, False)
728 display.frame.runJavaScript('show_video("setBackBoard", null, null, "hidden");')630 # display.frame.runJavaScript('show_video("setBackBoard", null, null, "hidden");')
729 del self.current_media_players[controller.controller_type]631 del self.current_media_players[controller.controller_type]
730632
731 def media_hide(self, msg):633 def media_hide(self, msg):
@@ -788,8 +690,8 @@
788 """690 """
789 self.live_timer.stop()691 self.live_timer.stop()
790 self.preview_timer.stop()692 self.preview_timer.stop()
791 for controller in self.display_controllers:693 self.media_reset(self.display_controllers(DisplayControllerType.Live))
792 self.media_reset(self.display_controllers[controller])694 self.media_reset(self.display_controllers(DisplayControllerType.Preview))
793695
794 @staticmethod696 @staticmethod
795 def _define_display(controller):697 def _define_display(controller):
796698
=== modified file 'openlp/core/ui/media/mediaplayer.py'
--- openlp/core/ui/media/mediaplayer.py 2019-02-14 15:09:09 +0000
+++ openlp/core/ui/media/mediaplayer.py 2019-04-11 20:30:04 +0000
@@ -52,11 +52,12 @@
52 """52 """
53 return False53 return False
5454
55 def setup(self, display):55 def setup(self, display, live_display):
56 """56 """
57 Create the related widgets for the current display57 Create the related widgets for the current display
5858
59 :param display: The display to be updated.59 :param display: The display to be updated.
60 :param live_display: Is the display a live one.
60 """61 """
61 pass62 pass
6263
@@ -78,10 +79,11 @@
78 """79 """
79 pass80 pass
8081
81 def play(self, display):82 def play(self, controller, display):
82 """83 """
83 Starts playing of current Media File84 Starts playing of current Media File
8485
86 :param controller: Which Controller is running the show.
85 :param display: The display to be updated.87 :param display: The display to be updated.
86 """88 """
87 pass89 pass
@@ -206,7 +208,7 @@
206 :param display: Identify the Display type208 :param display: Identify the Display type
207 :return: None209 :return: None
208 """210 """
209 if display.controller.is_live:211 if display.is_display:
210 self.set_live_state(state)212 self.set_live_state(state)
211 else:213 else:
212 self.set_preview_state(state)214 self.set_preview_state(state)
213215
=== added file 'openlp/core/ui/media/mediatab.py'
--- openlp/core/ui/media/mediatab.py 1970-01-01 00:00:00 +0000
+++ openlp/core/ui/media/mediatab.py 2019-04-11 20:30:04 +0000
@@ -0,0 +1,148 @@
1# -*- coding: utf-8 -*-
2# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
3
4###############################################################################
5# OpenLP - Open Source Lyrics Projection #
6# --------------------------------------------------------------------------- #
7# Copyright (c) 2008-2019 OpenLP Developers #
8# --------------------------------------------------------------------------- #
9# This program is free software; you can redistribute it and/or modify it #
10# under the terms of the GNU General Public License as published by the Free #
11# Software Foundation; version 2 of the License. #
12# #
13# This program is distributed in the hope that it will be useful, but WITHOUT #
14# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
15# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
16# more details. #
17# #
18# You should have received a copy of the GNU General Public License along #
19# with this program; if not, write to the Free Software Foundation, Inc., 59 #
20# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
21###############################################################################
22"""
23The :mod:`~openlp.core.ui.media.playertab` module holds the configuration tab for the media stuff.
24"""
25
26from PyQt5 import QtWidgets
27# from PyQt5.QtMultimedia import QCameraInfo, QAudioDeviceInfo, QAudio
28
29from openlp.core.common import is_linux, is_win
30from openlp.core.common.i18n import translate
31from openlp.core.common.settings import Settings
32from openlp.core.lib.settingstab import SettingsTab
33from openlp.core.ui.icons import UiIcons
34
35LINUX_STREAM = 'v4l2:///dev/video0'
36WIN_STREAM = 'dshow:// :dshow-vdev='
37
38
39class MediaTab(SettingsTab):
40 """
41 MediaTab is the Media settings tab in the settings dialog.
42 """
43 def __init__(self, parent):
44 """
45 Constructor
46 """
47 # self.media_players = Registry().get('media_controller').media_players
48 # self.saved_used_players = None
49 self.icon_path = UiIcons().video
50 player_translated = translate('OpenLP.MediaTab', 'Media')
51 super(MediaTab, self).__init__(parent, 'Media', player_translated)
52
53 def setup_ui(self):
54 """
55 Set up the UI
56 """
57 self.setObjectName('MediaTab')
58 super(MediaTab, self).setup_ui()
59 self.live_media_group_box = QtWidgets.QGroupBox(self.left_column)
60 self.live_media_group_box.setObjectName('live_media_group_box')
61 self.media_layout = QtWidgets.QVBoxLayout(self.live_media_group_box)
62 self.media_layout.setObjectName('live_media_layout')
63 self.auto_start_check_box = QtWidgets.QCheckBox(self.live_media_group_box)
64 self.auto_start_check_box.setObjectName('auto_start_check_box')
65 self.media_layout.addWidget(self.auto_start_check_box)
66 self.left_layout.addWidget(self.live_media_group_box)
67 self.stream_media_group_box = QtWidgets.QGroupBox(self.left_column)
68 self.stream_media_group_box.setObjectName('stream_media_group_box')
69 self.stream_media_layout = QtWidgets.QHBoxLayout(self.stream_media_group_box)
70 self.stream_media_layout.setObjectName('live_media_layout')
71 self.stream_media_layout.setContentsMargins(0, 0, 0, 0)
72 self.stream_edit = QtWidgets.QPlainTextEdit(self)
73 self.stream_media_layout.addWidget(self.stream_edit)
74 self.browse_button = QtWidgets.QToolButton(self)
75 self.browse_button.setIcon(UiIcons().undo)
76 self.stream_media_layout.addWidget(self.browse_button)
77 self.left_layout.addWidget(self.stream_media_group_box)
78 self.left_layout.addWidget(self.stream_media_group_box)
79 self.left_layout.addStretch()
80 self.right_layout.addStretch()
81 # # Signals and slots
82 self.browse_button.clicked.connect(self.on_revert)
83
84 def retranslateUi(self):
85 """
86 Translate the UI on the fly
87 """
88 self.live_media_group_box.setTitle(translate('MediaPlugin.MediaTab', 'Live Media'))
89 self.stream_media_group_box.setTitle(translate('MediaPlugin.MediaTab', 'Stream Media Command'))
90 self.auto_start_check_box.setText(translate('MediaPlugin.MediaTab', 'Start automatically'))
91
92 def load(self):
93 """
94 Load the settings
95 """
96 self.auto_start_check_box.setChecked(Settings().value(self.settings_section + '/media auto start'))
97 self.stream_edit.setPlainText(Settings().value(self.settings_section + '/stream command'))
98 if not self.stream_edit.toPlainText():
99 if is_linux:
100 self.stream_edit.setPlainText(LINUX_STREAM)
101 elif is_win:
102 self.stream_edit.setPlainText(WIN_STREAM)
103
104 def save(self):
105 """
106 Save the settings
107 """
108 setting_key = self.settings_section + '/media auto start'
109 if Settings().value(setting_key) != self.auto_start_check_box.checkState():
110 Settings().setValue(setting_key, self.auto_start_check_box.checkState())
111 # settings = Settings()
112 # settings.beginGroup(self.settings_section)
113 # settings.setValue('background color', self.background_color)
114 # settings.endGroup()
115 # old_players, override_player = get_media_players()
116 # if self.used_players != old_players:
117 # # clean old Media stuff
118 # set_media_players(self.used_players, override_player)
119 # self.settings_form.register_post_process('mediaitem_suffix_reset')
120 # self.settings_form.register_post_process('mediaitem_media_rebuild')
121 # self.settings_form.register_post_process('config_screen_changed')
122
123 def post_set_up(self, post_update=False):
124 """
125 Late setup for players as the MediaController has to be initialised first.
126
127 :param post_update: Indicates if called before or after updates.
128 """
129 pass
130 # for key, player in self.media_players.items():
131 # player = self.media_players[key]
132 # checkbox = MediaQCheckBox(self.media_player_group_box)
133 # checkbox.setEnabled(player.available)
134 # checkbox.setObjectName(player.name + '_check_box')
135 # checkbox.setToolTip(player.get_info())
136 # checkbox.set_player_name(player.name)
137 # self.player_check_boxes[player.name] = checkbox
138 # checkbox.stateChanged.connect(self.on_player_check_box_changed)
139 # self.media_player_layout.addWidget(checkbox)
140 # if player.available and player.name in self.used_players:
141 # checkbox.setChecked(True)
142 # else:
143 # checkbox.setChecked(False)
144 # self.update_player_list()
145 # self.retranslate_players()
146
147 def on_revert(self):
148 pass
0149
=== removed file 'openlp/core/ui/media/playertab.py'
--- openlp/core/ui/media/playertab.py 2019-02-14 15:09:09 +0000
+++ openlp/core/ui/media/playertab.py 1970-01-01 00:00:00 +0000
@@ -1,269 +0,0 @@
1# -*- coding: utf-8 -*-
2# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
3
4###############################################################################
5# OpenLP - Open Source Lyrics Projection #
6# --------------------------------------------------------------------------- #
7# Copyright (c) 2008-2019 OpenLP Developers #
8# --------------------------------------------------------------------------- #
9# This program is free software; you can redistribute it and/or modify it #
10# under the terms of the GNU General Public License as published by the Free #
11# Software Foundation; version 2 of the License. #
12# #
13# This program is distributed in the hope that it will be useful, but WITHOUT #
14# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
15# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
16# more details. #
17# #
18# You should have received a copy of the GNU General Public License along #
19# with this program; if not, write to the Free Software Foundation, Inc., 59 #
20# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
21###############################################################################
22"""
23The :mod:`~openlp.core.ui.media.playertab` module holds the configuration tab for the media stuff.
24"""
25import platform
26
27from PyQt5 import QtCore, QtWidgets
28
29from openlp.core.common.i18n import UiStrings, translate
30# from openlp.core.common.registry import Registry
31from openlp.core.common.settings import Settings
32from openlp.core.lib.settingstab import SettingsTab
33from openlp.core.lib.ui import create_button
34from openlp.core.ui.icons import UiIcons
35from openlp.core.widgets.buttons import ColorButton
36
37
38class MediaQCheckBox(QtWidgets.QCheckBox):
39 """
40 MediaQCheckBox adds an extra property, player_name to the QCheckBox class.
41 """
42 def set_player_name(self, name):
43 """
44 Set the player name
45 """
46 self.player_name = name
47
48
49class PlayerTab(SettingsTab):
50 """
51 MediaTab is the Media settings tab in the settings dialog.
52 """
53 def __init__(self, parent):
54 """
55 Constructor
56 """
57 # self.media_players = Registry().get('media_controller').media_players
58 self.saved_used_players = None
59 self.icon_path = UiIcons().player
60 player_translated = translate('OpenLP.PlayerTab', 'Players')
61 super(PlayerTab, self).__init__(parent, 'Players', player_translated)
62
63 def setup_ui(self):
64 """
65 Set up the UI
66 """
67 self.setObjectName('MediaTab')
68 super(PlayerTab, self).setup_ui()
69 self.background_color_group_box = QtWidgets.QGroupBox(self.left_column)
70 self.background_color_group_box.setObjectName('background_color_group_box')
71 self.form_layout = QtWidgets.QFormLayout(self.background_color_group_box)
72 self.form_layout.setObjectName('form_layout')
73 self.color_layout = QtWidgets.QHBoxLayout()
74 self.background_color_label = QtWidgets.QLabel(self.background_color_group_box)
75 self.background_color_label.setObjectName('background_color_label')
76 self.color_layout.addWidget(self.background_color_label)
77 self.background_color_button = ColorButton(self.background_color_group_box)
78 self.background_color_button.setObjectName('background_color_button')
79 self.color_layout.addWidget(self.background_color_button)
80 self.form_layout.addRow(self.color_layout)
81 self.information_label = QtWidgets.QLabel(self.background_color_group_box)
82 self.information_label.setObjectName('information_label')
83 self.information_label.setWordWrap(True)
84 self.form_layout.addRow(self.information_label)
85 self.left_layout.addWidget(self.background_color_group_box)
86 self.right_column.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
87 self.media_player_group_box = QtWidgets.QGroupBox(self.left_column)
88 self.media_player_group_box.setObjectName('media_player_group_box')
89 self.media_player_layout = QtWidgets.QVBoxLayout(self.media_player_group_box)
90 self.media_player_layout.setObjectName('media_player_layout')
91 self.player_check_boxes = {}
92 self.left_layout.addWidget(self.media_player_group_box)
93 self.player_order_group_box = QtWidgets.QGroupBox(self.left_column)
94 self.player_order_group_box.setObjectName('player_order_group_box')
95 self.player_order_layout = QtWidgets.QHBoxLayout(self.player_order_group_box)
96 self.player_order_layout.setObjectName('player_order_layout')
97 self.player_order_list_widget = QtWidgets.QListWidget(self.player_order_group_box)
98 size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
99 size_policy.setHorizontalStretch(0)
100 size_policy.setVerticalStretch(0)
101 size_policy.setHeightForWidth(self.player_order_list_widget.sizePolicy().hasHeightForWidth())
102 self.player_order_list_widget.setSizePolicy(size_policy)
103 self.player_order_list_widget.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
104 self.player_order_list_widget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
105 self.player_order_list_widget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
106 self.player_order_list_widget.setObjectName('player_order_list_widget')
107 self.player_order_layout.addWidget(self.player_order_list_widget)
108 self.ordering_button_layout = QtWidgets.QVBoxLayout()
109 self.ordering_button_layout.setObjectName('ordering_button_layout')
110 self.ordering_button_layout.addStretch(1)
111 self.ordering_up_button = create_button(self, 'ordering_up_button', role='up',
112 click=self.on_up_button_clicked)
113 self.ordering_down_button = create_button(self, 'ordering_down_button', role='down',
114 click=self.on_down_button_clicked)
115 self.ordering_button_layout.addWidget(self.ordering_up_button)
116 self.ordering_button_layout.addWidget(self.ordering_down_button)
117 self.ordering_button_layout.addStretch(1)
118 self.player_order_layout.addLayout(self.ordering_button_layout)
119 self.left_layout.addWidget(self.player_order_group_box)
120 self.left_layout.addStretch()
121 self.right_layout.addStretch()
122 # Signals and slots
123 self.background_color_button.colorChanged.connect(self.on_background_color_changed)
124
125 def retranslate_ui(self):
126 """
127 Translate the UI on the fly
128 """
129 self.media_player_group_box.setTitle(translate('OpenLP.PlayerTab', 'Available Media Players'))
130 self.player_order_group_box.setTitle(translate('OpenLP.PlayerTab', 'Player Search Order'))
131 self.background_color_group_box.setTitle(UiStrings().BackgroundColor)
132 self.background_color_label.setText(UiStrings().BackgroundColorColon)
133 self.information_label.setText(translate('OpenLP.PlayerTab',
134 'Visible background for videos with aspect ratio different to screen.'))
135 self.retranslate_players()
136
137 def on_background_color_changed(self, color):
138 """
139 Set the background color
140
141 :param color: The color to be set.
142 """
143 self.background_color = color
144
145 def on_player_check_box_changed(self, check_state):
146 """
147 Add or remove players depending on their status
148
149 :param check_state: The requested status.
150 """
151 player = self.sender().player_name
152 if check_state == QtCore.Qt.Checked:
153 if player not in self.used_players:
154 self.used_players.append(player)
155 else:
156 if player in self.used_players:
157 self.used_players.remove(player)
158 self.update_player_list()
159
160 def update_player_list(self):
161 """
162 Update the list of media players
163 """
164 self.player_order_list_widget.clear()
165 for player in self.used_players:
166 if player in list(self.player_check_boxes.keys()):
167 if len(self.used_players) == 1:
168 # At least one media player has to stay active
169 self.player_check_boxes['%s' % player].setEnabled(False)
170 else:
171 self.player_check_boxes['%s' % player].setEnabled(True)
172 self.player_order_list_widget.addItem(self.media_players[str(player)].original_name)
173
174 def on_up_button_clicked(self):
175 """
176 Move a media player up in the order
177 """
178 row = self.player_order_list_widget.currentRow()
179 if row <= 0:
180 return
181 item = self.player_order_list_widget.takeItem(row)
182 self.player_order_list_widget.insertItem(row - 1, item)
183 self.player_order_list_widget.setCurrentRow(row - 1)
184 self.used_players.insert(row - 1, self.used_players.pop(row))
185
186 def on_down_button_clicked(self):
187 """
188 Move a media player down in the order
189 """
190 row = self.player_order_list_widget.currentRow()
191 if row == -1 or row > self.player_order_list_widget.count() - 1:
192 return
193 item = self.player_order_list_widget.takeItem(row)
194 self.player_order_list_widget.insertItem(row + 1, item)
195 self.player_order_list_widget.setCurrentRow(row + 1)
196 self.used_players.insert(row + 1, self.used_players.pop(row))
197
198 def load(self):
199 """
200 Load the settings
201 """
202 if self.saved_used_players:
203 self.used_players = self.saved_used_players
204 # self.used_players = get_media_players()[0]
205 self.saved_used_players = self.used_players
206 settings = Settings()
207 settings.beginGroup(self.settings_section)
208 self.update_player_list()
209 self.background_color = settings.value('background color')
210 self.initial_color = self.background_color
211 settings.endGroup()
212 self.background_color_button.color = self.background_color
213
214 def save(self):
215 """
216 Save the settings
217 """
218 settings = Settings()
219 settings.beginGroup(self.settings_section)
220 settings.setValue('background color', self.background_color)
221 settings.endGroup()
222 # old_players, override_player = get_media_players()
223 # if self.used_players != old_players:
224 # # clean old Media stuff
225 # set_media_players(self.used_players, override_player)
226 # self.settings_form.register_post_process('mediaitem_suffix_reset')
227 # self.settings_form.register_post_process('mediaitem_media_rebuild')
228 # self.settings_form.register_post_process('config_screen_changed')
229
230 def post_set_up(self, post_update=False):
231 """
232 Late setup for players as the MediaController has to be initialised first.
233
234 :param post_update: Indicates if called before or after updates.
235 """
236 for key, player in self.media_players.items():
237 player = self.media_players[key]
238 checkbox = MediaQCheckBox(self.media_player_group_box)
239 checkbox.setEnabled(player.available)
240 checkbox.setObjectName(player.name + '_check_box')
241 checkbox.setToolTip(player.get_info())
242 checkbox.set_player_name(player.name)
243 self.player_check_boxes[player.name] = checkbox
244 checkbox.stateChanged.connect(self.on_player_check_box_changed)
245 self.media_player_layout.addWidget(checkbox)
246 if player.available and player.name in self.used_players:
247 checkbox.setChecked(True)
248 else:
249 checkbox.setChecked(False)
250 self.update_player_list()
251 self.retranslate_players()
252
253 def retranslate_players(self):
254 """
255 Translations for players is dependent on their setup as well
256 """
257 for key in self.media_players and self.player_check_boxes:
258 player = self.media_players[key]
259 checkbox = self.player_check_boxes[player.name]
260 checkbox.set_player_name(player.name)
261 if player.available:
262 checkbox.setText(player.display_name)
263 else:
264 checkbox_text = translate('OpenLP.PlayerTab', '%s (unavailable)') % player.display_name
265 if player.name == 'vlc':
266 checkbox_text += ' ' + translate('OpenLP.PlayerTab',
267 'NOTE: To use VLC you must install the %s version',
268 'Will insert "32bit" or "64bit"') % platform.architecture()[0]
269 checkbox.setText(checkbox_text)
2700
=== removed file 'openlp/core/ui/media/systemplayer.py'
--- openlp/core/ui/media/systemplayer.py 2019-02-14 15:09:09 +0000
+++ openlp/core/ui/media/systemplayer.py 1970-01-01 00:00:00 +0000
@@ -1,332 +0,0 @@
1# -*- coding: utf-8 -*-
2# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
3
4###############################################################################
5# OpenLP - Open Source Lyrics Projection #
6# --------------------------------------------------------------------------- #
7# Copyright (c) 2008-2019 OpenLP Developers #
8# --------------------------------------------------------------------------- #
9# This program is free software; you can redistribute it and/or modify it #
10# under the terms of the GNU General Public License as published by the Free #
11# Software Foundation; version 2 of the License. #
12# #
13# This program is distributed in the hope that it will be useful, but WITHOUT #
14# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
15# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
16# more details. #
17# #
18# You should have received a copy of the GNU General Public License along #
19# with this program; if not, write to the Free Software Foundation, Inc., 59 #
20# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
21###############################################################################
22"""
23The :mod:`~openlp.core.ui.media.systemplayer` contains the system (aka QtMultimedia) player component.
24"""
25import functools
26import logging
27import mimetypes
28
29from PyQt5 import QtCore, QtMultimedia, QtMultimediaWidgets
30
31from openlp.core.common.i18n import translate
32from openlp.core.threading import ThreadWorker, is_thread_finished, run_thread
33from openlp.core.ui.media import MediaState
34from openlp.core.ui.media.mediaplayer import MediaPlayer
35
36
37log = logging.getLogger(__name__)
38
39ADDITIONAL_EXT = {
40 'audio/ac3': ['.ac3'],
41 'audio/flac': ['.flac'],
42 'audio/x-m4a': ['.m4a'],
43 'audio/midi': ['.mid', '.midi'],
44 'audio/x-mp3': ['.mp3'],
45 'audio/mpeg': ['.mp3', '.mp2', '.mpga', '.mpega', '.m4a'],
46 'audio/qcelp': ['.qcp'],
47 'audio/x-wma': ['.wma'],
48 'audio/x-ms-wma': ['.wma'],
49 'video/x-flv': ['.flv'],
50 'video/x-matroska': ['.mpv', '.mkv'],
51 'video/x-wmv': ['.wmv'],
52 'video/x-mpg': ['.mpg'],
53 'video/mpeg': ['.mp4', '.mts', '.mov'],
54 'video/x-ms-wmv': ['.wmv']
55}
56
57
58class SystemPlayer(MediaPlayer):
59 """
60 A specialised version of the MediaPlayer class, which provides a QtMultimedia display.
61 """
62
63 def __init__(self, parent):
64 """
65 Constructor
66 """
67 super(SystemPlayer, self).__init__(parent, 'system')
68 self.original_name = 'System'
69 self.display_name = '&System'
70 self.parent = parent
71 self.additional_extensions = ADDITIONAL_EXT
72 self.media_player = QtMultimedia.QMediaPlayer(None, QtMultimedia.QMediaPlayer.VideoSurface)
73 mimetypes.init()
74 media_service = self.media_player.service()
75 log.info(media_service.__class__.__name__)
76 # supportedMimeTypes doesn't return anything on Linux and Windows and
77 # the mimetypes it returns on Mac OS X may not be playable.
78 supported_codecs = self.media_player.supportedMimeTypes()
79 for mime_type in supported_codecs:
80 mime_type = str(mime_type)
81 log.info(mime_type)
82 if mime_type.startswith('audio/'):
83 self._add_to_list(self.audio_extensions_list, mime_type)
84 elif mime_type.startswith('video/'):
85 self._add_to_list(self.video_extensions_list, mime_type)
86
87 def _add_to_list(self, mime_type_list, mime_type):
88 """
89 Add mimetypes to the provided list
90 """
91 # Add all extensions which mimetypes provides us for supported types.
92 extensions = mimetypes.guess_all_extensions(mime_type)
93 for extension in extensions:
94 ext = '*%s' % extension
95 if ext not in mime_type_list:
96 mime_type_list.append(ext)
97 log.info('MediaPlugin: %s extensions: %s', mime_type, ' '.join(extensions))
98
99 def disconnect_slots(self, signal):
100 """
101 Safely disconnect the slots from `signal`
102 """
103 try:
104 signal.disconnect()
105 except TypeError:
106 # If disconnect() is called on a signal without slots, it throws a TypeError
107 pass
108
109 def setup(self, display):
110 """
111 Set up the player widgets
112 :param display:
113 """
114 display.video_widget = QtMultimediaWidgets.QVideoWidget(display)
115 display.video_widget.resize(display.size())
116 display.media_player = QtMultimedia.QMediaPlayer(display)
117 display.media_player.setVideoOutput(display.video_widget)
118 display.video_widget.raise_()
119 display.video_widget.hide()
120 self.has_own_widget = True
121
122 def check_available(self):
123 """
124 Check if the player is available
125 """
126 return True
127
128 def load(self, display):
129 """
130 Load a video into the display
131
132 :param display: The display where the media is
133 """
134 log.debug('load vid in System Controller')
135 controller = display.controller
136 volume = controller.media_info.volume
137 path = controller.media_info.file_info.absoluteFilePath()
138 # Check if file is playable due to mimetype filters being nonexistent on Linux and Windows
139 if self.check_media(path):
140 display.media_player.setMedia(QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(path)))
141 self.volume(display, volume)
142 return True
143 else:
144 return False
145
146 def resize(self, display):
147 """
148 Resize the display
149
150 :param display: The display where the media is
151 """
152 display.video_widget.resize(display.size())
153
154 def play(self, display):
155 """
156 Play the current media item
157
158 :param display: The display where the media is
159 """
160 log.info('Play the current item')
161 controller = display.controller
162 start_time = 0
163 if display.controller.is_live:
164 if self.get_live_state() != QtMultimedia.QMediaPlayer.PausedState and controller.media_info.start_time > 0:
165 start_time = controller.media_info.start_time
166 else:
167 if self.get_preview_state() != QtMultimedia.QMediaPlayer.PausedState and \
168 controller.media_info.start_time > 0:
169 start_time = controller.media_info.start_time
170 display.media_player.play()
171 if start_time > 0:
172 self.seek(display, controller.media_info.start_time * 1000)
173 self.volume(display, controller.media_info.volume)
174 self.disconnect_slots(display.media_player.durationChanged)
175 display.media_player.durationChanged.connect(functools.partial(self.set_duration, controller))
176 self.set_state(MediaState.Playing, display)
177 display.video_widget.raise_()
178 return True
179
180 def pause(self, display):
181 """
182 Pause the current media item
183
184 :param display: The display where the media is
185 """
186 display.media_player.pause()
187 if display.controller.is_live:
188 if self.get_live_state() == QtMultimedia.QMediaPlayer.PausedState:
189 self.set_state(MediaState.Paused, display)
190 else:
191 if self.get_preview_state() == QtMultimedia.QMediaPlayer.PausedState:
192 self.set_state(MediaState.Paused, display)
193
194 def stop(self, display):
195 """
196 Stop the current media item
197
198 :param display: The display where the media is
199 """
200 display.media_player.stop()
201 self.set_visible(display, False)
202 self.set_state(MediaState.Stopped, display)
203
204 def volume(self, display, volume):
205 """
206 Set the volume
207
208 :param display: The display where the media is
209 :param volume: The volume to be set
210 """
211 if display.has_audio:
212 display.media_player.setVolume(volume)
213
214 def seek(self, display, seek_value):
215 """
216 Go to a particular point in the current media item
217
218 :param display: The display where the media is
219 :param seek_value: The where to seek to
220 """
221 display.media_player.setPosition(seek_value)
222
223 def reset(self, display):
224 """
225 Reset the media player
226
227 :param display: The display where the media is
228 """
229 display.media_player.stop()
230 display.media_player.setMedia(QtMultimedia.QMediaContent())
231 self.set_visible(display, False)
232 display.video_widget.setVisible(False)
233 self.set_state(MediaState.Off, display)
234
235 def set_visible(self, display, status):
236 """
237 Set the visibility of the widget
238
239 :param display: The display where the media is
240 :param status: The visibility status to be set
241 """
242 if self.has_own_widget:
243 display.video_widget.setVisible(status)
244
245 @staticmethod
246 def set_duration(controller, duration):
247 """
248
249 :param controller: the controller displaying the media
250 :param duration: how long is the media
251 :return:
252 """
253 controller.seek_slider.setMaximum(controller.media_info.length)
254
255 def update_ui(self, display):
256 """
257 Update the UI
258
259 :param display: The display where the media is
260 """
261 if display.media_player.state() == QtMultimedia.QMediaPlayer.PausedState and self.state != MediaState.Paused:
262 self.pause(display)
263 controller = display.controller
264 if controller.media_info.end_time > 0:
265 if display.media_player.position() > controller.media_info.end_time:
266 self.stop(display)
267 self.set_visible(display, False)
268 if not controller.seek_slider.isSliderDown():
269 controller.seek_slider.blockSignals(True)
270 controller.seek_slider.setSliderPosition(display.media_player.position())
271 controller.seek_slider.blockSignals(False)
272
273 def get_media_display_css(self):
274 """
275 Add css style sheets to htmlbuilder
276 """
277 return ''
278
279 def get_info(self):
280 """
281 Return some info about this player
282 """
283 return (translate('Media.player', 'This media player uses your operating system '
284 'to provide media capabilities.') +
285 '<br/> <strong>' + translate('Media.player', 'Audio') +
286 '</strong><br/>' + str(self.audio_extensions_list) +
287 '<br/><strong>' + translate('Media.player', 'Video') +
288 '</strong><br/>' + str(self.video_extensions_list) + '<br/>')
289
290 def check_media(self, path):
291 """
292 Check if a file can be played
293 Uses a separate QMediaPlayer in a thread
294
295 :param path: Path to file to be checked
296 :return: True if file can be played otherwise False
297 """
298 check_media_worker = CheckMediaWorker(path)
299 check_media_worker.setVolume(0)
300 run_thread(check_media_worker, 'check_media')
301 while not is_thread_finished('check_media'):
302 self.application.processEvents()
303 return check_media_worker.result
304
305
306class CheckMediaWorker(QtMultimedia.QMediaPlayer, ThreadWorker):
307 """
308 Class used to check if a media file is playable
309 """
310 def __init__(self, path):
311 super(CheckMediaWorker, self).__init__(None, QtMultimedia.QMediaPlayer.VideoSurface)
312 self.path = path
313
314 def start(self):
315 """
316 Start the thread worker
317 """
318 self.result = None
319 self.error.connect(functools.partial(self.signals, 'error'))
320 self.mediaStatusChanged.connect(functools.partial(self.signals, 'media'))
321 self.setMedia(QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(self.path)))
322 self.play()
323
324 def signals(self, origin, status):
325 if origin == 'media' and status == self.BufferedMedia:
326 self.result = True
327 self.stop()
328 self.quit.emit()
329 elif origin == 'error' and status != self.NoError:
330 self.result = False
331 self.stop()
332 self.quit.emit()
3330
=== modified file 'openlp/core/ui/media/vlcplayer.py'
--- openlp/core/ui/media/vlcplayer.py 2019-03-17 20:35:11 +0000
+++ openlp/core/ui/media/vlcplayer.py 2019-04-11 20:30:04 +0000
@@ -152,43 +152,42 @@
152 self.audio_extensions_list = AUDIO_EXT152 self.audio_extensions_list = AUDIO_EXT
153 self.video_extensions_list = VIDEO_EXT153 self.video_extensions_list = VIDEO_EXT
154154
155 def setup(self, display):155 def setup(self, output_display, live_display):
156 """156 """
157 Set up the media player157 Set up the media player
158158
159 :param display: The display where the media is159 :param output_display: The display where the media is
160 :param live_display: Is the display a live one.
160 :return:161 :return:
161 """162 """
162 vlc = get_vlc()163 vlc = get_vlc()
163 display.vlc_widget = QtWidgets.QFrame(display)164 output_display.vlc_widget = QtWidgets.QFrame(output_display)
164 display.vlc_widget.setFrameStyle(QtWidgets.QFrame.NoFrame)165 output_display.vlc_widget.setFrameStyle(QtWidgets.QFrame.NoFrame)
165 # creating a basic vlc instance166 # creating a basic vlc instance
166 command_line_options = '--no-video-title-show'167 command_line_options = '--no-video-title-show'
167 if not display.has_audio:168 if Settings().value('advanced/hide mouse') and live_display:
168 command_line_options += ' --no-audio --no-video-title-show'
169 if Settings().value('advanced/hide mouse') and display.controller.is_live:
170 command_line_options += ' --mouse-hide-timeout=0'169 command_line_options += ' --mouse-hide-timeout=0'
171 display.vlc_instance = vlc.Instance(command_line_options)170 output_display.vlc_instance = vlc.Instance(command_line_options)
172 # creating an empty vlc media player171 # creating an empty vlc media player
173 display.vlc_media_player = display.vlc_instance.media_player_new()172 output_display.vlc_media_player = output_display.vlc_instance.media_player_new()
174 display.vlc_widget.resize(display.size())173 output_display.vlc_widget.resize(output_display.size())
175 display.vlc_widget.raise_()174 output_display.vlc_widget.raise_()
176 display.vlc_widget.hide()175 output_display.vlc_widget.hide()
177 # The media player has to be 'connected' to the QFrame.176 # The media player has to be 'connected' to the QFrame.
178 # (otherwise a video would be displayed in it's own window)177 # (otherwise a video would be displayed in it's own window)
179 # This is platform specific!178 # This is platform specific!
180 # You have to give the id of the QFrame (or similar object)179 # You have to give the id of the QFrame (or similar object)
181 # to vlc, different platforms have different functions for this.180 # to vlc, different platforms have different functions for this.
182 win_id = int(display.vlc_widget.winId())181 win_id = int(output_display.vlc_widget.winId())
183 if is_win():182 if is_win():
184 display.vlc_media_player.set_hwnd(win_id)183 output_display.vlc_media_player.set_hwnd(win_id)
185 elif is_macosx():184 elif is_macosx():
186 # We have to use 'set_nsobject' since Qt5 on OSX uses Cocoa185 # We have to use 'set_nsobject' since Qt5 on OSX uses Cocoa
187 # framework and not the old Carbon.186 # framework and not the old Carbon.
188 display.vlc_media_player.set_nsobject(win_id)187 output_display.vlc_media_player.set_nsobject(win_id)
189 else:188 else:
190 # for Linux/*BSD using the X Server189 # for Linux/*BSD using the X Server
191 display.vlc_media_player.set_xwindow(win_id)190 output_display.vlc_media_player.set_xwindow(win_id)
192 self.has_own_widget = True191 self.has_own_widget = True
193192
194 def check_available(self):193 def check_available(self):
@@ -197,43 +196,45 @@
197 """196 """
198 return get_vlc() is not None197 return get_vlc() is not None
199198
200 def load(self, display, file):199 def load(self, output_display, file):
201 """200 """
202 Load a video into VLC201 Load a video into VLC
203202
204 :param display: The display where the media is203 :param output_display: The display where the media is
205 :param file: file to be played204 :param file: file to be played
206 :return:205 :return:
207 """206 """
208 vlc = get_vlc()207 vlc = get_vlc()
209 log.debug('load vid in Vlc Controller')208 log.debug('load vid in Vlc Controller')
210 controller = display.controller209 controller = output_display
211 volume = controller.media_info.volume210 volume = controller.media_info.volume
212 path = os.path.normcase(file)211 path = os.path.normcase(file)
213 # create the media212 # create the media
214 if controller.media_info.media_type == MediaType.CD:213 if controller.media_info.media_type == MediaType.CD:
215 if is_win():214 if is_win():
216 path = '/' + path215 path = '/' + path
217 display.vlc_media = display.vlc_instance.media_new_location('cdda://' + path)216 output_display.vlc_media = output_display.vlc_instance.media_new_location('cdda://' + path)
218 display.vlc_media_player.set_media(display.vlc_media)217 output_display.vlc_media_player.set_media(output_display.vlc_media)
219 display.vlc_media_player.play()218 output_display.vlc_media_player.play()
220 # Wait for media to start playing. In this case VLC actually returns an error.219 # Wait for media to start playing. In this case VLC actually returns an error.
221 self.media_state_wait(display, vlc.State.Playing)220 self.media_state_wait(output_display, vlc.State.Playing)
222 # If subitems exists, this is a CD221 # If subitems exists, this is a CD
223 audio_cd_tracks = display.vlc_media.subitems()222 audio_cd_tracks = output_display.vlc_media.subitems()
224 if not audio_cd_tracks or audio_cd_tracks.count() < 1:223 if not audio_cd_tracks or audio_cd_tracks.count() < 1:
225 return False224 return False
226 display.vlc_media = audio_cd_tracks.item_at_index(controller.media_info.title_track)225 output_display.vlc_media = audio_cd_tracks.item_at_index(controller.media_info.title_track)
226 elif controller.media_info.media_type == MediaType.Stream:
227 output_display.vlc_media = output_display.vlc_instance.media_new_location('ZZZZZZ')
227 else:228 else:
228 display.vlc_media = display.vlc_instance.media_new_path(path)229 output_display.vlc_media = output_display.vlc_instance.media_new_path(path)
229 # put the media in the media player230 # put the media in the media player
230 display.vlc_media_player.set_media(display.vlc_media)231 output_display.vlc_media_player.set_media(output_display.vlc_media)
231 # parse the metadata of the file232 # parse the metadata of the file
232 display.vlc_media.parse()233 output_display.vlc_media.parse()
233 self.volume(display, volume)234 self.volume(output_display, volume)
234 return True235 return True
235236
236 def media_state_wait(self, display, media_state):237 def media_state_wait(self, output_display, media_state):
237 """238 """
238 Wait for the video to change its state239 Wait for the video to change its state
239 Wait no longer than 60 seconds. (loading an iso file needs a long time)240 Wait no longer than 60 seconds. (loading an iso file needs a long time)
@@ -244,171 +245,171 @@
244 """245 """
245 vlc = get_vlc()246 vlc = get_vlc()
246 start = datetime.now()247 start = datetime.now()
247 while media_state != display.vlc_media.get_state():248 while media_state != output_display.vlc_media.get_state():
248 if display.vlc_media.get_state() == vlc.State.Error:249 if output_display.vlc_media.get_state() == vlc.State.Error:
249 return False250 return False
250 self.application.process_events()251 self.application.process_events()
251 if (datetime.now() - start).seconds > 60:252 if (datetime.now() - start).seconds > 60:
252 return False253 return False
253 return True254 return True
254255
255 def resize(self, display):256 def resize(self, output_display):
256 """257 """
257 Resize the player258 Resize the player
258259
259 :param display: The display where the media is260 :param output_display: The display where the media is
260 :return:261 :return:
261 """262 """
262 display.vlc_widget.resize(display.size())263 output_display.vlc_widget.resize(output_display.size())
263264
264 def play(self, display):265 def play(self, controller, output_display):
265 """266 """
266 Play the current item267 Play the current item
267268
268 :param display: The display where the media is269 :param controller: Which Controller is running the show.
270 :param output_display: The display where the media is
269 :return:271 :return:
270 """272 """
271 vlc = get_vlc()273 vlc = get_vlc()
272 controller = display.controller
273 start_time = 0274 start_time = 0
274 log.debug('vlc play')275 log.debug('vlc play')
275 if display.controller.is_live:276 if output_display.is_display:
276 if self.get_live_state() != MediaState.Paused and controller.media_info.start_time > 0:277 if self.get_live_state() != MediaState.Paused and output_display.media_info.start_time > 0:
277 start_time = controller.media_info.start_time278 start_time = output_display.media_info.start_time
278 else:279 else:
279 if self.get_preview_state() != MediaState.Paused and controller.media_info.start_time > 0:280 if self.get_preview_state() != MediaState.Paused and output_display.media_info.start_time > 0:
280 start_time = controller.media_info.start_time281 start_time = output_display.media_info.start_time
281 threading.Thread(target=display.vlc_media_player.play).start()282 threading.Thread(target=output_display.vlc_media_player.play).start()
282 if not self.media_state_wait(display, vlc.State.Playing):283 if not self.media_state_wait(output_display, vlc.State.Playing):
283 return False284 return False
284 if display.controller.is_live:285 if output_display.is_display:
285 if self.get_live_state() != MediaState.Paused and controller.media_info.start_time > 0:286 if self.get_live_state() != MediaState.Paused and output_display.media_info.start_time > 0:
286 log.debug('vlc play, start time set')287 log.debug('vlc play, start time set')
287 start_time = controller.media_info.start_time288 start_time = output_display.media_info.start_time
288 else:289 else:
289 if self.get_preview_state() != MediaState.Paused and controller.media_info.start_time > 0:290 if self.get_preview_state() != MediaState.Paused and output_display.media_info.start_time > 0:
290 log.debug('vlc play, start time set')291 log.debug('vlc play, start time set')
291 start_time = controller.media_info.start_time292 start_time = output_display.media_info.start_time
292 log.debug('mediatype: ' + str(controller.media_info.media_type))293 log.debug('mediatype: ' + str(output_display.media_info.media_type))
293 # Set tracks for the optical device294 # Set tracks for the optical device
294 if controller.media_info.media_type == MediaType.DVD and \295 if output_display.media_info.media_type == MediaType.DVD and \
295 self.get_live_state() != MediaState.Paused and self.get_preview_state() != MediaState.Paused:296 self.get_live_state() != MediaState.Paused and self.get_preview_state() != MediaState.Paused:
296 log.debug('vlc play, playing started')297 log.debug('vlc play, playing started')
297 if controller.media_info.title_track > 0:298 if output_display.media_info.title_track > 0:
298 log.debug('vlc play, title_track set: ' + str(controller.media_info.title_track))299 log.debug('vlc play, title_track set: ' + str(output_display.media_info.title_track))
299 display.vlc_media_player.set_title(controller.media_info.title_track)300 output_display.vlc_media_player.set_title(output_display.media_info.title_track)
300 display.vlc_media_player.play()301 output_display.vlc_media_player.play()
301 if not self.media_state_wait(display, vlc.State.Playing):302 if not self.media_state_wait(output_display, vlc.State.Playing):
302 return False303 return False
303 if controller.media_info.audio_track > 0:304 if output_display.media_info.audio_track > 0:
304 display.vlc_media_player.audio_set_track(controller.media_info.audio_track)305 output_display.vlc_media_player.audio_set_track(output_display.media_info.audio_track)
305 log.debug('vlc play, audio_track set: ' + str(controller.media_info.audio_track))306 log.debug('vlc play, audio_track set: ' + str(output_display.media_info.audio_track))
306 if controller.media_info.subtitle_track > 0:307 if output_display.media_info.subtitle_track > 0:
307 display.vlc_media_player.video_set_spu(controller.media_info.subtitle_track)308 output_display.vlc_media_player.video_set_spu(output_display.media_info.subtitle_track)
308 log.debug('vlc play, subtitle_track set: ' + str(controller.media_info.subtitle_track))309 log.debug('vlc play, subtitle_track set: ' + str(output_display.media_info.subtitle_track))
309 if controller.media_info.start_time > 0:310 if output_display.media_info.start_time > 0:
310 log.debug('vlc play, starttime set: ' + str(controller.media_info.start_time))311 log.debug('vlc play, starttime set: ' + str(output_display.media_info.start_time))
311 start_time = controller.media_info.start_time312 start_time = output_display.media_info.start_time
312 controller.media_info.length = controller.media_info.end_time - controller.media_info.start_time313 output_display.media_info.length = output_display.media_info.end_time - output_display.media_info.start_time
313 self.volume(display, controller.media_info.volume)314 self.volume(output_display, output_display.media_info.volume)
314 if start_time > 0 and display.vlc_media_player.is_seekable():315 if start_time > 0 and output_display.vlc_media_player.is_seekable():
315 display.vlc_media_player.set_time(int(start_time))316 output_display.vlc_media_player.set_time(int(start_time))
316 controller.seek_slider.setMaximum(controller.media_info.length)317 controller.seek_slider.setMaximum(output_display.media_info.length)
317 self.set_state(MediaState.Playing, display)318 self.set_state(MediaState.Playing, output_display)
318 display.vlc_widget.raise_()319 output_display.vlc_widget.raise_()
319 return True320 return True
320321
321 def pause(self, display):322 def pause(self, output_display):
322 """323 """
323 Pause the current item324 Pause the current item
324325
325 :param display: The display where the media is326 :param output_display: The display where the media is
326 :return:327 :return:
327 """328 """
328 vlc = get_vlc()329 vlc = get_vlc()
329 if display.vlc_media.get_state() != vlc.State.Playing:330 if output_display.vlc_media.get_state() != vlc.State.Playing:
330 return331 return
331 display.vlc_media_player.pause()332 output_display.vlc_media_player.pause()
332 if self.media_state_wait(display, vlc.State.Paused):333 if self.media_state_wait(output_display, vlc.State.Paused):
333 self.set_state(MediaState.Paused, display)334 self.set_state(MediaState.Paused, output_display)
334335
335 def stop(self, display):336 def stop(self, output_display):
336 """337 """
337 Stop the current item338 Stop the current item
338339
339 :param display: The display where the media is340 :param output_display: The display where the media is
340 :return:341 :return:
341 """342 """
342 threading.Thread(target=display.vlc_media_player.stop).start()343 threading.Thread(target=output_display.vlc_media_player.stop).start()
343 self.set_state(MediaState.Stopped, display)344 self.set_state(MediaState.Stopped, output_display)
344345
345 def volume(self, display, vol):346 def volume(self, output_display, vol):
346 """347 """
347 Set the volume348 Set the volume
348349
349 :param vol: The volume to be sets350 :param vol: The volume to be sets
350 :param display: The display where the media is351 :param output_display: The display where the media is
351 :return:352 :return:
352 """353 """
353 if display.has_audio:354 if output_display.has_audio:
354 display.vlc_media_player.audio_set_volume(vol)355 output_display.vlc_media_player.audio_set_volume(vol)
355356
356 def seek(self, display, seek_value):357 def seek(self, output_display, seek_value):
357 """358 """
358 Go to a particular position359 Go to a particular position
359360
360 :param seek_value: The position of where a seek goes to361 :param seek_value: The position of where a seek goes to
361 :param display: The display where the media is362 :param output_display: The display where the media is
362 """363 """
363 if display.controller.media_info.media_type == MediaType.CD \364 if output_display.controller.media_info.media_type == MediaType.CD \
364 or display.controller.media_info.media_type == MediaType.DVD:365 or output_display.controller.media_info.media_type == MediaType.DVD:
365 seek_value += int(display.controller.media_info.start_time)366 seek_value += int(output_display.controller.media_info.start_time)
366 if display.vlc_media_player.is_seekable():367 if output_display.vlc_media_player.is_seekable():
367 display.vlc_media_player.set_time(seek_value)368 output_display.vlc_media_player.set_time(seek_value)
368369
369 def reset(self, display):370 def reset(self, output_display):
370 """371 """
371 Reset the player372 Reset the player
372373
373 :param display: The display where the media is374 :param output_display: The display where the media is
374 """375 """
375 display.vlc_media_player.stop()376 output_display.vlc_media_player.stop()
376 display.vlc_widget.setVisible(False)377 output_display.vlc_widget.setVisible(False)
377 self.set_state(MediaState.Off, display)378 self.set_state(MediaState.Off, output_display)
378379
379 def set_visible(self, display, status):380 def set_visible(self, output_display, status):
380 """381 """
381 Set the visibility382 Set the visibility
382383
383 :param display: The display where the media is384 :param output_display: The display where the media is
384 :param status: The visibility status385 :param status: The visibility status
385 """386 """
386 if self.has_own_widget:387 if self.has_own_widget:
387 display.vlc_widget.setVisible(status)388 output_display.vlc_widget.setVisible(status)
388389
389 def update_ui(self, display):390 def update_ui(self, controller, output_display):
390 """391 """
391 Update the UI392 Update the UI
392393
393 :param display: The display where the media is394 :param controller: Which Controller is running the show.
395 :param output_display: The display where the media is
394 """396 """
395 vlc = get_vlc()397 vlc = get_vlc()
396 # Stop video if playback is finished.398 # Stop video if playback is finished.
397 if display.vlc_media.get_state() == vlc.State.Ended:399 if output_display.vlc_media.get_state() == vlc.State.Ended:
398 self.stop(display)400 self.stop(output_display)
399 controller = display.controller
400 if controller.media_info.end_time > 0:401 if controller.media_info.end_time > 0:
401 if display.vlc_media_player.get_time() > controller.media_info.end_time:402 if output_display.vlc_media_player.get_time() > controller.media_info.end_time:
402 self.stop(display)403 self.stop(output_display)
403 self.set_visible(display, False)404 self.set_visible(output_display, False)
404 if not controller.seek_slider.isSliderDown():405 if not controller.seek_slider.isSliderDown():
405 controller.seek_slider.blockSignals(True)406 controller.seek_slider.blockSignals(True)
406 if display.controller.media_info.media_type == MediaType.CD \407 if controller.media_info.media_type == MediaType.CD \
407 or display.controller.media_info.media_type == MediaType.DVD:408 or controller.media_info.media_type == MediaType.DVD:
408 controller.seek_slider.setSliderPosition(409 controller.seek_slider.setSliderPosition(
409 display.vlc_media_player.get_time() - int(display.controller.media_info.start_time))410 output_display.vlc_media_player.get_time() - int(output_display.controller.media_info.start_time))
410 else:411 else:
411 controller.seek_slider.setSliderPosition(display.vlc_media_player.get_time())412 controller.seek_slider.setSliderPosition(output_display.vlc_media_player.get_time())
412 controller.seek_slider.blockSignals(False)413 controller.seek_slider.blockSignals(False)
413414
414 def get_info(self):415 def get_info(self):
415416
=== modified file 'openlp/core/ui/screenstab.py'
--- openlp/core/ui/screenstab.py 2019-02-14 15:09:09 +0000
+++ openlp/core/ui/screenstab.py 2019-04-11 20:30:04 +0000
@@ -41,7 +41,7 @@
41 """41 """
42 Initialise the screen settings tab42 Initialise the screen settings tab
43 """43 """
44 self.icon_path = UiIcons().settings44 self.icon_path = UiIcons().desktop
45 screens_translated = translate('OpenLP.ScreensTab', 'Screens')45 screens_translated = translate('OpenLP.ScreensTab', 'Screens')
46 super(ScreensTab, self).__init__(parent, 'Screens', screens_translated)46 super(ScreensTab, self).__init__(parent, 'Screens', screens_translated)
47 self.settings_section = 'core'47 self.settings_section = 'core'
4848
=== modified file 'openlp/core/ui/settingsform.py'
--- openlp/core/ui/settingsform.py 2019-02-14 15:09:09 +0000
+++ openlp/core/ui/settingsform.py 2019-04-11 20:30:04 +0000
@@ -30,13 +30,14 @@
30from openlp.core.api.tab import ApiTab30from openlp.core.api.tab import ApiTab
31from openlp.core.common.mixins import RegistryProperties31from openlp.core.common.mixins import RegistryProperties
32from openlp.core.common.registry import Registry32from openlp.core.common.registry import Registry
33from openlp.core.common.settings import Settings
33from openlp.core.lib import build_icon34from openlp.core.lib import build_icon
34from openlp.core.projectors.tab import ProjectorTab35from openlp.core.projectors.tab import ProjectorTab
35from openlp.core.ui.advancedtab import AdvancedTab36from openlp.core.ui.advancedtab import AdvancedTab
36from openlp.core.ui.generaltab import GeneralTab37from openlp.core.ui.generaltab import GeneralTab
37from openlp.core.ui.screenstab import ScreensTab38from openlp.core.ui.screenstab import ScreensTab
38from openlp.core.ui.themestab import ThemesTab39from openlp.core.ui.themestab import ThemesTab
39# from openlp.core.ui.media.playertab import PlayerTab40from openlp.core.ui.media.mediatab import MediaTab
40from openlp.core.ui.settingsdialog import Ui_SettingsDialog41from openlp.core.ui.settingsdialog import Ui_SettingsDialog
4142
4243
@@ -78,8 +79,8 @@
78 self.insert_tab(self.advanced_tab)79 self.insert_tab(self.advanced_tab)
79 self.insert_tab(self.screens_tab)80 self.insert_tab(self.screens_tab)
80 self.insert_tab(self.themes_tab)81 self.insert_tab(self.themes_tab)
81 self.insert_tab(self.advanced_tab)82 if Settings().value('core/experimental'):
82 # self.insert_tab(self.player_tab)83 self.insert_tab(self.player_tab)
83 self.insert_tab(self.projector_tab)84 self.insert_tab(self.projector_tab)
84 self.insert_tab(self.api_tab)85 self.insert_tab(self.api_tab)
85 for plugin in State().list_plugins():86 for plugin in State().list_plugins():
@@ -160,15 +161,17 @@
160 self.themes_tab = ThemesTab(self)161 self.themes_tab = ThemesTab(self)
161 self.projector_tab = ProjectorTab(self)162 self.projector_tab = ProjectorTab(self)
162 self.advanced_tab = AdvancedTab(self)163 self.advanced_tab = AdvancedTab(self)
163 # self.player_tab = PlayerTab(self)164 if Settings().value('core/experimental'):
165 self.player_tab = MediaTab(self)
164 self.api_tab = ApiTab(self)166 self.api_tab = ApiTab(self)
165 self.screens_tab = ScreensTab(self)167 self.screens_tab = ScreensTab(self)
166 except Exception as e:168 except Exception as e:
167 print(e)169 print(e)
168 # Post setup
169 self.general_tab.post_set_up()170 self.general_tab.post_set_up()
170 self.themes_tab.post_set_up()171 self.themes_tab.post_set_up()
171 self.advanced_tab.post_set_up()172 self.advanced_tab.post_set_up()
173 if Settings().value('core/experimental'):
174 self.player_tab.post_set_up()
172 self.api_tab.post_set_up()175 self.api_tab.post_set_up()
173 for plugin in State().list_plugins():176 for plugin in State().list_plugins():
174 if plugin.settings_tab:177 if plugin.settings_tab:
175178
=== modified file 'openlp/core/ui/slidecontroller.py'
--- openlp/core/ui/slidecontroller.py 2019-02-14 15:09:09 +0000
+++ openlp/core/ui/slidecontroller.py 2019-04-11 20:30:04 +0000
@@ -23,6 +23,7 @@
23The :mod:`slidecontroller` module contains the most important part of OpenLP - the slide controller23The :mod:`slidecontroller` module contains the most important part of OpenLP - the slide controller
24"""24"""
25import copy25import copy
26import datetime
26from collections import deque27from collections import deque
27from threading import Lock28from threading import Lock
2829
@@ -70,6 +71,45 @@
70]71]
7172
7273
74class MediaSlider(QtWidgets.QSlider):
75 """
76 Allows the mouse events of a slider to be overridden and extra functionality added
77 """
78 def __init__(self, direction, manager, controller):
79 """
80 Constructor
81 """
82 super(MediaSlider, self).__init__(direction)
83 self.manager = manager
84 self.controller = controller
85
86 def mouseMoveEvent(self, event):
87 """
88 Override event to allow hover time to be displayed.
89
90 :param event: The triggering event
91 """
92 time_value = QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width())
93 self.setToolTip('%s' % datetime.timedelta(seconds=int(time_value / 1000)))
94 QtWidgets.QSlider.mouseMoveEvent(self, event)
95
96 def mousePressEvent(self, event):
97 """
98 Mouse Press event no new functionality
99 :param event: The triggering event
100 """
101 QtWidgets.QSlider.mousePressEvent(self, event)
102
103 def mouseReleaseEvent(self, event):
104 """
105 Set the slider position when the mouse is clicked and released on the slider.
106
107 :param event: The triggering event
108 """
109 self.setValue(QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), event.x(), self.width()))
110 QtWidgets.QSlider.mouseReleaseEvent(self, event)
111
112
73class InfoLabel(QtWidgets.QLabel):113class InfoLabel(QtWidgets.QLabel):
74 """114 """
75 InfoLabel is a subclassed QLabel. Created to provide the ablilty to add a ellipsis if the text is cut off. Original115 InfoLabel is a subclassed QLabel. Created to provide the ablilty to add a ellipsis if the text is cut off. Original
@@ -316,8 +356,59 @@
316 'Clear'),356 'Clear'),
317 triggers=self.on_clear)357 triggers=self.on_clear)
318 self.controller_layout.addWidget(self.toolbar)358 self.controller_layout.addWidget(self.toolbar)
319 # Build the Media Toolbar359 # Build a Media ToolBar
320 self.media_controller.register_controller(self)360 self.mediabar = OpenLPToolbar(self)
361 self.mediabar.add_toolbar_action('playbackPlay', text='media_playback_play',
362 icon=UiIcons().play,
363 tooltip=translate('OpenLP.SlideController', 'Start playing media.'),
364 triggers=self.send_to_plugins)
365 self.mediabar.add_toolbar_action('playbackPause', text='media_playback_pause',
366 icon=UiIcons().pause,
367 tooltip=translate('OpenLP.SlideController', 'Pause playing media.'),
368 triggers=self.send_to_plugins)
369 self.mediabar.add_toolbar_action('playbackStop', text='media_playback_stop',
370 icon=UiIcons().stop,
371 tooltip=translate('OpenLP.SlideController', 'Stop playing media.'),
372 triggers=self.send_to_plugins)
373 self.mediabar.add_toolbar_action('playbackLoop', text='media_playback_loop',
374 icon=UiIcons().repeat, checked=False,
375 tooltip=translate('OpenLP.SlideController', 'Loop playing media.'),
376 triggers=self.send_to_plugins)
377 self.position_label = QtWidgets.QLabel()
378 self.position_label.setText(' 00:00 / 00:00')
379 self.position_label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
380 self.position_label.setToolTip(translate('OpenLP.SlideController', 'Video timer.'))
381 self.position_label.setMinimumSize(90, 0)
382 self.position_label.setObjectName('position_label')
383 self.mediabar.add_toolbar_widget(self.position_label)
384 # Build the seek_slider.
385 self.seek_slider = MediaSlider(QtCore.Qt.Horizontal, self, self)
386 self.seek_slider.setMaximum(1000)
387 self.seek_slider.setTracking(True)
388 self.seek_slider.setMouseTracking(True)
389 self.seek_slider.setToolTip(translate('OpenLP.SlideController', 'Video position.'))
390 self.seek_slider.setGeometry(QtCore.QRect(90, 260, 221, 24))
391 self.seek_slider.setObjectName('seek_slider')
392 self.mediabar.add_toolbar_widget(self.seek_slider)
393 # Build the volume_slider.
394 self.volume_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
395 self.volume_slider.setTickInterval(10)
396 self.volume_slider.setTickPosition(QtWidgets.QSlider.TicksAbove)
397 self.volume_slider.setMinimum(0)
398 self.volume_slider.setMaximum(100)
399 self.volume_slider.setTracking(True)
400 self.volume_slider.setToolTip(translate('OpenLP.SlideController', 'Audio Volume.'))
401 # self.volume_slider.setValue(self.media_info.volume)
402 self.volume_slider.setGeometry(QtCore.QRect(90, 160, 221, 24))
403 self.volume_slider.setObjectName('volume_slider')
404 self.mediabar.add_toolbar_widget(self.volume_slider)
405 self.controller_layout.addWidget(self.mediabar)
406 self.mediabar.setVisible(False)
407 if not self.is_live:
408 self.volume_slider.setEnabled(False)
409 # Signals
410 self.seek_slider.valueChanged.connect(self.send_to_plugins)
411 self.volume_slider.valueChanged.connect(self.send_to_plugins)
321 if self.is_live:412 if self.is_live:
322 # Build the Song Toolbar413 # Build the Song Toolbar
323 self.song_menu = QtWidgets.QToolButton(self.toolbar)414 self.song_menu = QtWidgets.QToolButton(self.toolbar)
@@ -556,8 +647,6 @@
556 # self.__add_actions_to_widget(self.display)647 # self.__add_actions_to_widget(self.display)
557 # The SlidePreview's ratio.648 # The SlidePreview's ratio.
558649
559 # TODO: Need to basically update everything
560
561 def __add_actions_to_widget(self, widget):650 def __add_actions_to_widget(self, widget):
562 """651 """
563 Add actions to the widget specified by `widget`652 Add actions to the widget specified by `widget`
@@ -1398,10 +1487,10 @@
1398 :param item: The service item to be processed1487 :param item: The service item to be processed
1399 """1488 """
1400 if self.is_live and self.hide_mode() == HideMode.Theme:1489 if self.is_live and self.hide_mode() == HideMode.Theme:
1401 self.media_controller.video(self.controller_type, item, HideMode.Blank)1490 self.media_controller.load_video(self.controller_type, item, HideMode.Blank)
1402 self.on_blank_display(True)1491 self.on_blank_display(True)
1403 else:1492 else:
1404 self.media_controller.video(self.controller_type, item, self.hide_mode())1493 self.media_controller.load_video(self.controller_type, item, self.hide_mode())
1405 if not self.is_live:1494 if not self.is_live:
1406 self.preview_display.show()1495 self.preview_display.show()
14071496
@@ -1491,7 +1580,7 @@
1491 self.type_prefix = 'preview'1580 self.type_prefix = 'preview'
1492 self.category = 'Preview Toolbar'1581 self.category = 'Preview Toolbar'
14931582
1494 def bootstrap_post_set_up(self):1583 def bootstrap_initialise(self):
1495 """1584 """
1496 process the bootstrap post setup request1585 process the bootstrap post setup request
1497 """1586 """
@@ -1523,7 +1612,7 @@
1523 self.category = UiStrings().LiveToolbar1612 self.category = UiStrings().LiveToolbar
1524 ActionList.get_instance().add_category(str(self.category), CategoryOrder.standard_toolbar)1613 ActionList.get_instance().add_category(str(self.category), CategoryOrder.standard_toolbar)
15251614
1526 def bootstrap_post_set_up(self):1615 def bootstrap_initialise(self):
1527 """1616 """
1528 process the bootstrap post setup request1617 process the bootstrap post setup request
1529 """1618 """
15301619
=== modified file 'openlp/core/ui/themeform.py'
--- openlp/core/ui/themeform.py 2019-02-14 15:09:09 +0000
+++ openlp/core/ui/themeform.py 2019-04-11 20:30:04 +0000
@@ -330,6 +330,8 @@
330 self.video_color_button.color = self.theme.background_border_color330 self.video_color_button.color = self.theme.background_border_color
331 self.video_path_edit.path = self.theme.background_filename331 self.video_path_edit.path = self.theme.background_filename
332 self.setField('background_type', 4)332 self.setField('background_type', 4)
333 elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Stream):
334 self.setField('background_type', 5)
333 elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Transparent):335 elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Transparent):
334 self.setField('background_type', 3)336 self.setField('background_type', 3)
335 if self.theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Horizontal):337 if self.theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Horizontal):
336338
=== modified file 'openlp/core/ui/themewizard.py'
--- openlp/core/ui/themewizard.py 2019-02-14 15:09:09 +0000
+++ openlp/core/ui/themewizard.py 2019-04-11 20:30:04 +0000
@@ -64,7 +64,7 @@
64 self.background_label = QtWidgets.QLabel(self.background_page)64 self.background_label = QtWidgets.QLabel(self.background_page)
65 self.background_label.setObjectName('background_label')65 self.background_label.setObjectName('background_label')
66 self.background_combo_box = QtWidgets.QComboBox(self.background_page)66 self.background_combo_box = QtWidgets.QComboBox(self.background_page)
67 self.background_combo_box.addItems(['', '', '', '', ''])67 self.background_combo_box.addItems(['', '', '', '', '', ''])
68 self.background_combo_box.setObjectName('background_combo_box')68 self.background_combo_box.setObjectName('background_combo_box')
69 self.background_type_layout.addRow(self.background_label, self.background_combo_box)69 self.background_type_layout.addRow(self.background_label, self.background_combo_box)
70 self.background_type_layout.setItem(1, QtWidgets.QFormLayout.LabelRole, self.spacer)70 self.background_type_layout.setItem(1, QtWidgets.QFormLayout.LabelRole, self.spacer)
@@ -410,6 +410,8 @@
410 self.background_combo_box.setItemText(BackgroundType.Video, UiStrings().Video)410 self.background_combo_box.setItemText(BackgroundType.Video, UiStrings().Video)
411 self.background_combo_box.setItemText(BackgroundType.Transparent,411 self.background_combo_box.setItemText(BackgroundType.Transparent,
412 translate('OpenLP.ThemeWizard', 'Transparent'))412 translate('OpenLP.ThemeWizard', 'Transparent'))
413 self.background_combo_box.setItemText(BackgroundType.Stream,
414 translate('OpenLP.ThemeWizard', 'Live Stream'))
413 self.color_label.setText(translate('OpenLP.ThemeWizard', 'color:'))415 self.color_label.setText(translate('OpenLP.ThemeWizard', 'color:'))
414 self.gradient_start_label.setText(translate('OpenLP.ThemeWizard', 'Starting color:'))416 self.gradient_start_label.setText(translate('OpenLP.ThemeWizard', 'Starting color:'))
415 self.gradient_end_label.setText(translate('OpenLP.ThemeWizard', 'Ending color:'))417 self.gradient_end_label.setText(translate('OpenLP.ThemeWizard', 'Ending color:'))
416418
=== modified file 'openlp/core/widgets/views.py'
--- openlp/core/widgets/views.py 2019-02-14 15:09:09 +0000
+++ openlp/core/widgets/views.py 2019-04-11 20:30:04 +0000
@@ -203,7 +203,10 @@
203 if self.service_item.is_capable(ItemCapabilities.HasThumbnails):203 if self.service_item.is_capable(ItemCapabilities.HasThumbnails):
204 pixmap = QtGui.QPixmap(remove_url_prefix(slide['thumbnail']))204 pixmap = QtGui.QPixmap(remove_url_prefix(slide['thumbnail']))
205 else:205 else:
206 pixmap = QtGui.QPixmap(remove_url_prefix(slide['image']))206 if isinstance(slide['image'], QtGui.QIcon):
207 pixmap = slide['image'].pixmap(QtCore.QSize(32, 32))
208 else:
209 pixmap = QtGui.QPixmap(remove_url_prefix(slide['image']))
207 else:210 else:
208 pixmap = QtGui.QPixmap(remove_url_prefix(slide['path']))211 pixmap = QtGui.QPixmap(remove_url_prefix(slide['path']))
209 label.setPixmap(pixmap)212 label.setPixmap(pixmap)
210213
=== modified file 'openlp/plugins/media/lib/mediaitem.py'
--- openlp/plugins/media/lib/mediaitem.py 2019-03-17 20:35:11 +0000
+++ openlp/plugins/media/lib/mediaitem.py 2019-04-11 20:30:04 +0000
@@ -116,6 +116,8 @@
116 self.can_preview = False116 self.can_preview = False
117 self.can_make_live = False117 self.can_make_live = False
118 self.can_add_to_service = False118 self.can_add_to_service = False
119 if State().check_preconditions('media_live'):
120 self.can_make_live = False
119121
120 def add_list_view_to_toolbar(self):122 def add_list_view_to_toolbar(self):
121 """123 """
@@ -264,7 +266,8 @@
264 :param media: The media266 :param media: The media
265 :param target_group:267 :param target_group:
266 """268 """
267 media.sort(key=lambda file_path: get_natural_key(file_path.name))269 # TODO needs to be fixed as no idea why this fails
270 # media.sort(key=lambda file_path: get_natural_key(file_path.name))
268 for track in media:271 for track in media:
269 track_info = QtCore.QFileInfo(track)272 track_info = QtCore.QFileInfo(track)
270 item_name = None273 item_name = None
271274
=== removed file 'openlp/plugins/media/lib/mediatab.py'
--- openlp/plugins/media/lib/mediatab.py 2019-02-14 15:09:09 +0000
+++ openlp/plugins/media/lib/mediatab.py 1970-01-01 00:00:00 +0000
@@ -1,73 +0,0 @@
1# -*- coding: utf-8 -*-
2# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
3
4###############################################################################
5# OpenLP - Open Source Lyrics Projection #
6# --------------------------------------------------------------------------- #
7# Copyright (c) 2008-2019 OpenLP Developers #
8# --------------------------------------------------------------------------- #
9# This program is free software; you can redistribute it and/or modify it #
10# under the terms of the GNU General Public License as published by the Free #
11# Software Foundation; version 2 of the License. #
12# #
13# This program is distributed in the hope that it will be useful, but WITHOUT #
14# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
15# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
16# more details. #
17# #
18# You should have received a copy of the GNU General Public License along #
19# with this program; if not, write to the Free Software Foundation, Inc., 59 #
20# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
21###############################################################################
22
23from PyQt5 import QtWidgets
24
25from openlp.core.common.i18n import UiStrings, translate
26from openlp.core.common.settings import Settings
27from openlp.core.lib.settingstab import SettingsTab
28
29
30class MediaTab(SettingsTab):
31 """
32 MediaTab is the Media settings tab in the settings dialog.
33 """
34 def __init__(self, parent, title, visible_title, icon_path):
35 self.parent = parent
36 super(MediaTab, self).__init__(parent, title, visible_title, icon_path)
37
38 def setup_ui(self):
39 self.setObjectName('MediaTab')
40 super(MediaTab, self).setup_ui()
41 self.advanced_group_box = QtWidgets.QGroupBox(self.left_column)
42 self.advanced_group_box.setObjectName('advanced_group_box')
43 self.advanced_layout = QtWidgets.QVBoxLayout(self.advanced_group_box)
44 self.advanced_layout.setObjectName('advanced_layout')
45 self.override_player_check_box = QtWidgets.QCheckBox(self.advanced_group_box)
46 self.override_player_check_box.setObjectName('override_player_check_box')
47 self.advanced_layout.addWidget(self.override_player_check_box)
48 self.auto_start_check_box = QtWidgets.QCheckBox(self.advanced_group_box)
49 self.auto_start_check_box.setObjectName('auto_start_check_box')
50 self.advanced_layout.addWidget(self.auto_start_check_box)
51 self.left_layout.addWidget(self.advanced_group_box)
52 self.left_layout.addStretch()
53 self.right_layout.addStretch()
54
55 def retranslate_ui(self):
56 self.advanced_group_box.setTitle(UiStrings().Advanced)
57 self.override_player_check_box.setText(translate('MediaPlugin.MediaTab', 'Allow media player to be overridden'))
58 self.auto_start_check_box.setText(translate('MediaPlugin.MediaTab', 'Start new Live media automatically'))
59
60 def load(self):
61 self.override_player_check_box.setChecked(Settings().value(self.settings_section + '/override player'))
62 self.auto_start_check_box.setChecked(Settings().value(self.settings_section + '/media auto start'))
63
64 def save(self):
65 setting_key = self.settings_section + '/override player'
66 if Settings().value(setting_key) != self.override_player_check_box.checkState():
67 Settings().setValue(setting_key, self.override_player_check_box.checkState())
68 self.settings_form.register_post_process('mediaitem_suffix_reset')
69 self.settings_form.register_post_process('mediaitem_media_rebuild')
70 self.settings_form.register_post_process('mediaitem_suffixes')
71 setting_key = self.settings_section + '/media auto start'
72 if Settings().value(setting_key) != self.auto_start_check_box.checkState():
73 Settings().setValue(setting_key, self.auto_start_check_box.checkState())
740
=== modified file 'openlp/plugins/media/mediaplugin.py'
--- openlp/plugins/media/mediaplugin.py 2019-02-14 15:09:09 +0000
+++ openlp/plugins/media/mediaplugin.py 2019-04-11 20:30:04 +0000
@@ -24,8 +24,6 @@
24"""24"""
25import logging25import logging
2626
27from PyQt5 import QtCore
28
29from openlp.core.state import State27from openlp.core.state import State
30from openlp.core.api.http import register_endpoint28from openlp.core.api.http import register_endpoint
31from openlp.core.common.i18n import translate29from openlp.core.common.i18n import translate
@@ -34,7 +32,6 @@
34from openlp.core.lib.plugin import Plugin, StringContent32from openlp.core.lib.plugin import Plugin, StringContent
35from openlp.plugins.media.endpoint import api_media_endpoint, media_endpoint33from openlp.plugins.media.endpoint import api_media_endpoint, media_endpoint
36from openlp.plugins.media.lib.mediaitem import MediaMediaItem34from openlp.plugins.media.lib.mediaitem import MediaMediaItem
37from openlp.plugins.media.lib.mediatab import MediaTab
3835
3936
40log = logging.getLogger(__name__)37log = logging.getLogger(__name__)
@@ -42,7 +39,6 @@
4239
43# Some settings starting with "media" are in core, because they are needed for core functionality.40# Some settings starting with "media" are in core, because they are needed for core functionality.
44__default_settings__ = {41__default_settings__ = {
45 'media/media auto start': QtCore.Qt.Unchecked,
46 'media/media files': [],42 'media/media files': [],
47 'media/last directory': None43 'media/last directory': None
48}44}
@@ -78,15 +74,6 @@
78 """74 """
79 pass75 pass
8076
81 def create_settings_tab(self, parent):
82 """
83 Create the settings Tab
84
85 :param parent:
86 """
87 visible_name = self.get_string(StringContent.VisibleName)
88 self.settings_tab = MediaTab(parent, self.name, visible_name['title'], self.icon_path)
89
90 @staticmethod77 @staticmethod
91 def about():78 def about():
92 """79 """
9380
=== modified file 'tests/functional/openlp_core/common/test_common.py'
--- tests/functional/openlp_core/common/test_common.py 2019-02-14 15:09:09 +0000
+++ tests/functional/openlp_core/common/test_common.py 2019-04-11 20:30:04 +0000
@@ -139,13 +139,13 @@
139 Test `path_to_module` when supplied with a `Path` object139 Test `path_to_module` when supplied with a `Path` object
140 """140 """
141 # GIVEN: A `Path` object141 # GIVEN: A `Path` object
142 path = Path('core', 'ui', 'media', 'webkitplayer.py')142 path = Path('core', 'ui', 'media', 'vlcplayer.py')
143143
144 # WHEN: Calling path_to_module with the `Path` object144 # WHEN: Calling path_to_module with the `Path` object
145 result = path_to_module(path)145 result = path_to_module(path)
146146
147 # THEN: path_to_module should return the module name147 # THEN: path_to_module should return the module name
148 assert result == 'openlp.core.ui.media.webkitplayer'148 assert result == 'openlp.core.ui.media.vlcplayer'
149149
150 def test_trace_error_handler(self):150 def test_trace_error_handler(self):
151 """151 """
152152
=== removed file 'tests/functional/openlp_core/ui/media/test_systemplayer.py'
--- tests/functional/openlp_core/ui/media/test_systemplayer.py 2019-02-14 15:09:09 +0000
+++ tests/functional/openlp_core/ui/media/test_systemplayer.py 1970-01-01 00:00:00 +0000
@@ -1,567 +0,0 @@
1# -*- coding: utf-8 -*-
2# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
3
4###############################################################################
5# OpenLP - Open Source Lyrics Projection #
6# --------------------------------------------------------------------------- #
7# Copyright (c) 2008-2019 OpenLP Developers #
8# --------------------------------------------------------------------------- #
9# This program is free software; you can redistribute it and/or modify it #
10# under the terms of the GNU General Public License as published by the Free #
11# Software Foundation; version 2 of the License. #
12# #
13# This program is distributed in the hope that it will be useful, but WITHOUT #
14# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
15# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
16# more details. #
17# #
18# You should have received a copy of the GNU General Public License along #
19# with this program; if not, write to the Free Software Foundation, Inc., 59 #
20# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
21###############################################################################
22"""
23Package to test the openlp.core.ui.media.systemplayer package.
24"""
25from unittest import TestCase
26from unittest.mock import MagicMock, call, patch
27
28from PyQt5 import QtCore, QtMultimedia
29
30from openlp.core.common.registry import Registry
31from openlp.core.ui.media import MediaState
32from openlp.core.ui.media.systemplayer import ADDITIONAL_EXT, CheckMediaWorker, SystemPlayer
33
34
35class TestSystemPlayer(TestCase):
36 """
37 Test the system media player
38 """
39 @patch('openlp.core.ui.media.systemplayer.mimetypes')
40 @patch('openlp.core.ui.media.systemplayer.QtMultimedia.QMediaPlayer')
41 def test_constructor(self, MockQMediaPlayer, mocked_mimetypes):
42 """
43 Test the SystemPlayer constructor
44 """
45 # GIVEN: The SystemPlayer class and a mockedQMediaPlayer
46 mocked_media_player = MagicMock()
47 mocked_media_player.supportedMimeTypes.return_value = [
48 'application/postscript',
49 'audio/aiff',
50 'audio/x-aiff',
51 'text/html',
52 'video/animaflex',
53 'video/x-ms-asf'
54 ]
55 mocked_mimetypes.guess_all_extensions.side_effect = [
56 ['.aiff'],
57 ['.aiff'],
58 ['.afl'],
59 ['.asf']
60 ]
61 MockQMediaPlayer.return_value = mocked_media_player
62
63 # WHEN: An object is created from it
64 player = SystemPlayer(self)
65
66 # THEN: The correct initial values should be set up
67 assert 'system' == player.name
68 assert 'System' == player.original_name
69 assert '&System' == player.display_name
70 assert self == player.parent
71 assert ADDITIONAL_EXT == player.additional_extensions
72 MockQMediaPlayer.assert_called_once_with(None, QtMultimedia.QMediaPlayer.VideoSurface)
73 mocked_mimetypes.init.assert_called_once_with()
74 mocked_media_player.service.assert_called_once_with()
75 mocked_media_player.supportedMimeTypes.assert_called_once_with()
76 assert ['*.aiff'] == player.audio_extensions_list
77 assert ['*.afl', '*.asf'] == player.video_extensions_list
78
79 @patch('openlp.core.ui.media.systemplayer.QtMultimediaWidgets.QVideoWidget')
80 @patch('openlp.core.ui.media.systemplayer.QtMultimedia.QMediaPlayer')
81 def test_setup(self, MockQMediaPlayer, MockQVideoWidget):
82 """
83 Test the setup() method of SystemPlayer
84 """
85 # GIVEN: A SystemPlayer instance and a mock display
86 player = SystemPlayer(self)
87 mocked_display = MagicMock()
88 mocked_display.size.return_value = [1, 2, 3, 4]
89 mocked_video_widget = MagicMock()
90 mocked_media_player = MagicMock()
91 MockQVideoWidget.return_value = mocked_video_widget
92 MockQMediaPlayer.return_value = mocked_media_player
93
94 # WHEN: setup() is run
95 player.setup(mocked_display)
96
97 # THEN: The player should have a display widget
98 MockQVideoWidget.assert_called_once_with(mocked_display)
99 assert mocked_video_widget == mocked_display.video_widget
100 mocked_display.size.assert_called_once_with()
101 mocked_video_widget.resize.assert_called_once_with([1, 2, 3, 4])
102 MockQMediaPlayer.assert_called_with(mocked_display)
103 assert mocked_media_player == mocked_display.media_player
104 mocked_media_player.setVideoOutput.assert_called_once_with(mocked_video_widget)
105 mocked_video_widget.raise_.assert_called_once_with()
106 mocked_video_widget.hide.assert_called_once_with()
107 assert player.has_own_widget is True
108
109 def test_disconnect_slots(self):
110 """
111 Test that we the disconnect slots method catches the TypeError
112 """
113 # GIVEN: A SystemPlayer class and a signal that throws a TypeError
114 player = SystemPlayer(self)
115 mocked_signal = MagicMock()
116 mocked_signal.disconnect.side_effect = \
117 TypeError('disconnect() failed between \'durationChanged\' and all its connections')
118
119 # WHEN: disconnect_slots() is called
120 player.disconnect_slots(mocked_signal)
121
122 # THEN: disconnect should have been called and the exception should have been ignored
123 mocked_signal.disconnect.assert_called_once_with()
124
125 def test_check_available(self):
126 """
127 Test the check_available() method on SystemPlayer
128 """
129 # GIVEN: A SystemPlayer instance
130 player = SystemPlayer(self)
131
132 # WHEN: check_available is run
133 result = player.check_available()
134
135 # THEN: it should be available
136 assert result is True
137
138 def test_load_valid_media(self):
139 """
140 Test the load() method of SystemPlayer with a valid media file
141 """
142 # GIVEN: A SystemPlayer instance and a mocked display
143 player = SystemPlayer(self)
144 mocked_display = MagicMock()
145 mocked_display.controller.media_info.volume = 1
146 mocked_display.controller.media_info.file_info.absoluteFilePath.return_value = '/path/to/file'
147
148 # WHEN: The load() method is run
149 with patch.object(player, 'check_media') as mocked_check_media, \
150 patch.object(player, 'volume') as mocked_volume:
151 mocked_check_media.return_value = True
152 result = player.load(mocked_display)
153
154 # THEN: the file is sent to the video widget
155 mocked_display.controller.media_info.file_info.absoluteFilePath.assert_called_once_with()
156 mocked_check_media.assert_called_once_with('/path/to/file')
157 mocked_display.media_player.setMedia.assert_called_once_with(
158 QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile('/path/to/file')))
159 mocked_volume.assert_called_once_with(mocked_display, 1)
160 assert result is True
161
162 def test_load_invalid_media(self):
163 """
164 Test the load() method of SystemPlayer with an invalid media file
165 """
166 # GIVEN: A SystemPlayer instance and a mocked display
167 player = SystemPlayer(self)
168 mocked_display = MagicMock()
169 mocked_display.controller.media_info.volume = 1
170 mocked_display.controller.media_info.file_info.absoluteFilePath.return_value = '/path/to/file'
171
172 # WHEN: The load() method is run
173 with patch.object(player, 'check_media') as mocked_check_media, \
174 patch.object(player, 'volume'):
175 mocked_check_media.return_value = False
176 result = player.load(mocked_display)
177
178 # THEN: stuff
179 mocked_display.controller.media_info.file_info.absoluteFilePath.assert_called_once_with()
180 mocked_check_media.assert_called_once_with('/path/to/file')
181 assert result is False
182
183 def test_resize(self):
184 """
185 Test the resize() method of the SystemPlayer
186 """
187 # GIVEN: A SystemPlayer instance and a mocked display
188 player = SystemPlayer(self)
189 mocked_display = MagicMock()
190 mocked_display.size.return_value = [1, 2, 3, 4]
191
192 # WHEN: The resize() method is called
193 player.resize(mocked_display)
194
195 # THEN: The player is resized
196 mocked_display.size.assert_called_once_with()
197 mocked_display.video_widget.resize.assert_called_once_with([1, 2, 3, 4])
198
199 @patch('openlp.core.ui.media.systemplayer.functools')
200 def test_play_is_live(self, mocked_functools):
201 """
202 Test the play() method of the SystemPlayer on the live display
203 """
204 # GIVEN: A SystemPlayer instance and a mocked display
205 mocked_functools.partial.return_value = 'function'
206 player = SystemPlayer(self)
207 mocked_display = MagicMock()
208 mocked_display.controller.is_live = True
209 mocked_display.controller.media_info.start_time = 1
210 mocked_display.controller.media_info.volume = 1
211
212 # WHEN: play() is called
213 with patch.object(player, 'get_live_state') as mocked_get_live_state, \
214 patch.object(player, 'seek') as mocked_seek, \
215 patch.object(player, 'volume') as mocked_volume, \
216 patch.object(player, 'set_state') as mocked_set_state, \
217 patch.object(player, 'disconnect_slots') as mocked_disconnect_slots:
218 mocked_get_live_state.return_value = QtMultimedia.QMediaPlayer.PlayingState
219 result = player.play(mocked_display)
220
221 # THEN: the media file is played
222 mocked_get_live_state.assert_called_once_with()
223 mocked_display.media_player.play.assert_called_once_with()
224 mocked_seek.assert_called_once_with(mocked_display, 1000)
225 mocked_volume.assert_called_once_with(mocked_display, 1)
226 mocked_disconnect_slots.assert_called_once_with(mocked_display.media_player.durationChanged)
227 mocked_display.media_player.durationChanged.connect.assert_called_once_with('function')
228 mocked_set_state.assert_called_once_with(MediaState.Playing, mocked_display)
229 mocked_display.video_widget.raise_.assert_called_once_with()
230 assert result is True
231
232 @patch('openlp.core.ui.media.systemplayer.functools')
233 def test_play_is_preview(self, mocked_functools):
234 """
235 Test the play() method of the SystemPlayer on the preview display
236 """
237 # GIVEN: A SystemPlayer instance and a mocked display
238 mocked_functools.partial.return_value = 'function'
239 player = SystemPlayer(self)
240 mocked_display = MagicMock()
241 mocked_display.controller.is_live = False
242 mocked_display.controller.media_info.start_time = 1
243 mocked_display.controller.media_info.volume = 1
244
245 # WHEN: play() is called
246 with patch.object(player, 'get_preview_state') as mocked_get_preview_state, \
247 patch.object(player, 'seek') as mocked_seek, \
248 patch.object(player, 'volume') as mocked_volume, \
249 patch.object(player, 'set_state') as mocked_set_state:
250 mocked_get_preview_state.return_value = QtMultimedia.QMediaPlayer.PlayingState
251 result = player.play(mocked_display)
252
253 # THEN: the media file is played
254 mocked_get_preview_state.assert_called_once_with()
255 mocked_display.media_player.play.assert_called_once_with()
256 mocked_seek.assert_called_once_with(mocked_display, 1000)
257 mocked_volume.assert_called_once_with(mocked_display, 1)
258 mocked_display.media_player.durationChanged.connect.assert_called_once_with('function')
259 mocked_set_state.assert_called_once_with(MediaState.Playing, mocked_display)
260 mocked_display.video_widget.raise_.assert_called_once_with()
261 assert result is True
262
263 def test_pause_is_live(self):
264 """
265 Test the pause() method of the SystemPlayer on the live display
266 """
267 # GIVEN: A SystemPlayer instance
268 player = SystemPlayer(self)
269 mocked_display = MagicMock()
270 mocked_display.controller.is_live = True
271
272 # WHEN: The pause method is called
273 with patch.object(player, 'get_live_state') as mocked_get_live_state, \
274 patch.object(player, 'set_state') as mocked_set_state:
275 mocked_get_live_state.return_value = QtMultimedia.QMediaPlayer.PausedState
276 player.pause(mocked_display)
277
278 # THEN: The video is paused
279 mocked_display.media_player.pause.assert_called_once_with()
280 mocked_get_live_state.assert_called_once_with()
281 mocked_set_state.assert_called_once_with(MediaState.Paused, mocked_display)
282
283 def test_pause_is_preview(self):
284 """
285 Test the pause() method of the SystemPlayer on the preview display
286 """
287 # GIVEN: A SystemPlayer instance
288 player = SystemPlayer(self)
289 mocked_display = MagicMock()
290 mocked_display.controller.is_live = False
291
292 # WHEN: The pause method is called
293 with patch.object(player, 'get_preview_state') as mocked_get_preview_state, \
294 patch.object(player, 'set_state') as mocked_set_state:
295 mocked_get_preview_state.return_value = QtMultimedia.QMediaPlayer.PausedState
296 player.pause(mocked_display)
297
298 # THEN: The video is paused
299 mocked_display.media_player.pause.assert_called_once_with()
300 mocked_get_preview_state.assert_called_once_with()
301 mocked_set_state.assert_called_once_with(MediaState.Paused, mocked_display)
302
303 def test_stop(self):
304 """
305 Test the stop() method of the SystemPlayer
306 """
307 # GIVEN: A SystemPlayer instance
308 player = SystemPlayer(self)
309 mocked_display = MagicMock()
310
311 # WHEN: The stop method is called
312 with patch.object(player, 'set_visible') as mocked_set_visible, \
313 patch.object(player, 'set_state') as mocked_set_state:
314 player.stop(mocked_display)
315
316 # THEN: The video is stopped
317 mocked_display.media_player.stop.assert_called_once_with()
318 mocked_set_visible.assert_called_once_with(mocked_display, False)
319 mocked_set_state.assert_called_once_with(MediaState.Stopped, mocked_display)
320
321 def test_volume(self):
322 """
323 Test the volume() method of the SystemPlayer
324 """
325 # GIVEN: A SystemPlayer instance
326 player = SystemPlayer(self)
327 mocked_display = MagicMock()
328 mocked_display.has_audio = True
329
330 # WHEN: The stop method is called
331 player.volume(mocked_display, 2)
332
333 # THEN: The video is stopped
334 mocked_display.media_player.setVolume.assert_called_once_with(2)
335
336 def test_seek(self):
337 """
338 Test the seek() method of the SystemPlayer
339 """
340 # GIVEN: A SystemPlayer instance
341 player = SystemPlayer(self)
342 mocked_display = MagicMock()
343
344 # WHEN: The stop method is called
345 player.seek(mocked_display, 2)
346
347 # THEN: The video is stopped
348 mocked_display.media_player.setPosition.assert_called_once_with(2)
349
350 def test_reset(self):
351 """
352 Test the reset() method of the SystemPlayer
353 """
354 # GIVEN: A SystemPlayer instance
355 player = SystemPlayer(self)
356 mocked_display = MagicMock()
357
358 # WHEN: reset() is called
359 with patch.object(player, 'set_state') as mocked_set_state, \
360 patch.object(player, 'set_visible') as mocked_set_visible:
361 player.reset(mocked_display)
362
363 # THEN: The media player is reset
364 mocked_display.media_player.stop()
365 mocked_display.media_player.setMedia.assert_called_once_with(QtMultimedia.QMediaContent())
366 mocked_set_visible.assert_called_once_with(mocked_display, False)
367 mocked_display.video_widget.setVisible.assert_called_once_with(False)
368 mocked_set_state.assert_called_once_with(MediaState.Off, mocked_display)
369
370 def test_set_visible(self):
371 """
372 Test the set_visible() method on the SystemPlayer
373 """
374 # GIVEN: A SystemPlayer instance and a mocked display
375 player = SystemPlayer(self)
376 player.has_own_widget = True
377 mocked_display = MagicMock()
378
379 # WHEN: set_visible() is called
380 player.set_visible(mocked_display, True)
381
382 # THEN: The widget should be visible
383 mocked_display.video_widget.setVisible.assert_called_once_with(True)
384
385 def test_set_duration(self):
386 """
387 Test the set_duration() method of the SystemPlayer
388 """
389 # GIVEN: a mocked controller
390 mocked_controller = MagicMock()
391 mocked_controller.media_info.length = 5
392
393 # WHEN: The set_duration() is called. NB: the 10 here is ignored by the code
394 SystemPlayer.set_duration(mocked_controller, 10)
395
396 # THEN: The maximum length of the slider should be set
397 mocked_controller.seek_slider.setMaximum.assert_called_once_with(5)
398
399 def test_update_ui(self):
400 """
401 Test the update_ui() method on the SystemPlayer
402 """
403 # GIVEN: A SystemPlayer instance
404 player = SystemPlayer(self)
405 player.state = [MediaState.Playing, MediaState.Playing]
406 mocked_display = MagicMock()
407 mocked_display.media_player.state.return_value = QtMultimedia.QMediaPlayer.PausedState
408 mocked_display.controller.media_info.end_time = 1
409 mocked_display.media_player.position.return_value = 2
410 mocked_display.controller.seek_slider.isSliderDown.return_value = False
411
412 # WHEN: update_ui() is called
413 with patch.object(player, 'stop') as mocked_stop, \
414 patch.object(player, 'set_visible') as mocked_set_visible:
415 player.update_ui(mocked_display)
416
417 # THEN: The UI is updated
418 expected_stop_calls = [call(mocked_display)]
419 expected_position_calls = [call(), call()]
420 expected_block_signals_calls = [call(True), call(False)]
421 mocked_display.media_player.state.assert_called_once_with()
422 assert 1 == mocked_stop.call_count
423 assert expected_stop_calls == mocked_stop.call_args_list
424 assert 2 == mocked_display.media_player.position.call_count
425 assert expected_position_calls == mocked_display.media_player.position.call_args_list
426 mocked_set_visible.assert_called_once_with(mocked_display, False)
427 mocked_display.controller.seek_slider.isSliderDown.assert_called_once_with()
428 assert expected_block_signals_calls == mocked_display.controller.seek_slider.blockSignals.call_args_list
429 mocked_display.controller.seek_slider.setSliderPosition.assert_called_once_with(2)
430
431 def test_get_media_display_css(self):
432 """
433 Test the get_media_display_css() method of the SystemPlayer
434 """
435 # GIVEN: A SystemPlayer instance
436 player = SystemPlayer(self)
437
438 # WHEN: get_media_display_css() is called
439 result = player.get_media_display_css()
440
441 # THEN: The css should be empty
442 assert '' == result
443
444 @patch('openlp.core.ui.media.systemplayer.QtMultimedia.QMediaPlayer')
445 def test_get_info(self, MockQMediaPlayer):
446 """
447 Test the get_info() method of the SystemPlayer
448 """
449 # GIVEN: A SystemPlayer instance
450 mocked_media_player = MagicMock()
451 mocked_media_player.supportedMimeTypes.return_value = []
452 MockQMediaPlayer.return_value = mocked_media_player
453 player = SystemPlayer(self)
454
455 # WHEN: get_info() is called
456 result = player.get_info()
457
458 # THEN: The info should be correct
459 expected_info = 'This media player uses your operating system to provide media capabilities.<br/> ' \
460 '<strong>Audio</strong><br/>[]<br/><strong>Video</strong><br/>[]<br/>'
461 assert expected_info == result
462
463 @patch('openlp.core.ui.media.systemplayer.CheckMediaWorker')
464 @patch('openlp.core.ui.media.systemplayer.run_thread')
465 @patch('openlp.core.ui.media.systemplayer.is_thread_finished')
466 def test_check_media(self, mocked_is_thread_finished, mocked_run_thread, MockCheckMediaWorker):
467 """
468 Test the check_media() method of the SystemPlayer
469 """
470 # GIVEN: A SystemPlayer instance and a mocked thread
471 valid_file = '/path/to/video.ogv'
472 mocked_application = MagicMock()
473 Registry().create()
474 Registry().register('application', mocked_application)
475 player = SystemPlayer(self)
476 mocked_is_thread_finished.side_effect = [False, True]
477 mocked_check_media_worker = MagicMock()
478 mocked_check_media_worker.result = True
479 MockCheckMediaWorker.return_value = mocked_check_media_worker
480
481 # WHEN: check_media() is called with a valid media file
482 result = player.check_media(valid_file)
483
484 # THEN: It should return True
485 MockCheckMediaWorker.assert_called_once_with(valid_file)
486 mocked_check_media_worker.setVolume.assert_called_once_with(0)
487 mocked_run_thread.assert_called_once_with(mocked_check_media_worker, 'check_media')
488 mocked_is_thread_finished.assert_called_with('check_media')
489 assert mocked_is_thread_finished.call_count == 2, 'is_thread_finished() should have been called twice'
490 mocked_application.processEvents.assert_called_once_with()
491 assert result is True
492
493
494class TestCheckMediaWorker(TestCase):
495 """
496 Test the CheckMediaWorker class
497 """
498 def test_constructor(self):
499 """
500 Test the constructor of the CheckMediaWorker class
501 """
502 # GIVEN: A file path
503 path = 'file.ogv'
504
505 # WHEN: The CheckMediaWorker object is instantiated
506 worker = CheckMediaWorker(path)
507
508 # THEN: The correct values should be set up
509 assert worker is not None
510
511 @patch('openlp.core.ui.media.systemplayer.functools.partial')
512 @patch('openlp.core.ui.media.systemplayer.QtMultimedia.QMediaContent')
513 def test_start(self, MockQMediaContent, mocked_partial):
514 """
515 Test the start method
516 """
517 # GIVEN: A CheckMediaWorker instance
518 worker = CheckMediaWorker('file.ogv')
519 MockQMediaContent.side_effect = lambda x: x
520 mocked_partial.side_effect = lambda x, y: y
521
522 # WHEN: start() is called
523 with patch.object(worker, 'error') as mocked_error, \
524 patch.object(worker, 'mediaStatusChanged') as mocked_status_change, \
525 patch.object(worker, 'setMedia') as mocked_set_media, \
526 patch.object(worker, 'play') as mocked_play:
527 worker.start()
528
529 # THEN: The correct methods should be called
530 mocked_error.connect.assert_called_once_with('error')
531 mocked_status_change.connect.assert_called_once_with('media')
532 mocked_set_media.assert_called_once_with(QtCore.QUrl('file:file.ogv'))
533 mocked_play.assert_called_once_with()
534
535 def test_signals_media(self):
536 """
537 Test the signals() signal of the CheckMediaWorker class with a "media" origin
538 """
539 # GIVEN: A CheckMediaWorker instance
540 worker = CheckMediaWorker('file.ogv')
541
542 # WHEN: signals() is called with media and BufferedMedia
543 with patch.object(worker, 'stop') as mocked_stop, \
544 patch.object(worker, 'quit') as mocked_quit:
545 worker.signals('media', worker.BufferedMedia)
546
547 # THEN: The worker should exit and the result should be True
548 mocked_stop.assert_called_once_with()
549 mocked_quit.emit.assert_called_once_with()
550 assert worker.result is True
551
552 def test_signals_error(self):
553 """
554 Test the signals() signal of the CheckMediaWorker class with a "error" origin
555 """
556 # GIVEN: A CheckMediaWorker instance
557 worker = CheckMediaWorker('file.ogv')
558
559 # WHEN: signals() is called with error and BufferedMedia
560 with patch.object(worker, 'stop') as mocked_stop, \
561 patch.object(worker, 'quit') as mocked_quit:
562 worker.signals('error', None)
563
564 # THEN: The worker should exit and the result should be True
565 mocked_stop.assert_called_once_with()
566 mocked_quit.emit.assert_called_once_with()
567 assert worker.result is False
5680
=== modified file 'tests/functional/openlp_core/ui/media/test_vlcplayer.py'
--- tests/functional/openlp_core/ui/media/test_vlcplayer.py 2019-02-14 15:09:09 +0000
+++ tests/functional/openlp_core/ui/media/test_vlcplayer.py 2019-04-11 20:30:04 +0000
@@ -138,25 +138,24 @@
138 mocked_vlc = MagicMock()138 mocked_vlc = MagicMock()
139 mocked_vlc.Instance.return_value = mocked_instance139 mocked_vlc.Instance.return_value = mocked_instance
140 mocked_get_vlc.return_value = mocked_vlc140 mocked_get_vlc.return_value = mocked_vlc
141 mocked_display = MagicMock()141 mocked_output_display = MagicMock()
142 mocked_display.has_audio = False142 mocked_controller = MagicMock()
143 mocked_display.controller.is_live = True143 mocked_controller.is_live = True
144 mocked_display.size.return_value = (10, 10)144 mocked_output_display.size.return_value = (10, 10)
145 vlc_player = VlcPlayer(None)145 vlc_player = VlcPlayer(None)
146146
147 # WHEN: setup() is run147 # WHEN: setup() is run
148 vlc_player.setup(mocked_display)148 vlc_player.setup(mocked_output_display, mocked_controller)
149149
150 # THEN: The VLC widget should be set up correctly150 # THEN: The VLC widget should be set up correctly
151 assert mocked_display.vlc_widget == mocked_qframe151 assert mocked_output_display.vlc_widget == mocked_qframe
152 mocked_qframe.setFrameStyle.assert_called_with(1)152 mocked_qframe.setFrameStyle.assert_called_with(1)
153 mocked_settings.value.assert_called_with('advanced/hide mouse')153 mocked_settings.value.assert_called_with('advanced/hide mouse')
154 mocked_vlc.Instance.assert_called_with('--no-video-title-show --no-audio --no-video-title-show '154 mocked_vlc.Instance.assert_called_with('--no-video-title-show --mouse-hide-timeout=0')
155 '--mouse-hide-timeout=0')155 assert mocked_output_display.vlc_instance == mocked_instance
156 assert mocked_display.vlc_instance == mocked_instance
157 mocked_instance.media_player_new.assert_called_with()156 mocked_instance.media_player_new.assert_called_with()
158 assert mocked_display.vlc_media_player == mocked_media_player_new157 assert mocked_output_display.vlc_media_player == mocked_media_player_new
159 mocked_display.size.assert_called_with()158 mocked_output_display.size.assert_called_with()
160 mocked_qframe.resize.assert_called_with((10, 10))159 mocked_qframe.resize.assert_called_with((10, 10))
161 mocked_qframe.raise_.assert_called_with()160 mocked_qframe.raise_.assert_called_with()
162 mocked_qframe.hide.assert_called_with()161 mocked_qframe.hide.assert_called_with()
@@ -188,14 +187,14 @@
188 mocked_vlc = MagicMock()187 mocked_vlc = MagicMock()
189 mocked_vlc.Instance.return_value = mocked_instance188 mocked_vlc.Instance.return_value = mocked_instance
190 mocked_get_vlc.return_value = mocked_vlc189 mocked_get_vlc.return_value = mocked_vlc
191 mocked_display = MagicMock()190 mocked_output_display = MagicMock()
192 mocked_display.has_audio = True191 mocked_controller = MagicMock()
193 mocked_display.controller.is_live = True192 mocked_controller.is_live = True
194 mocked_display.size.return_value = (10, 10)193 mocked_output_display.size.return_value = (10, 10)
195 vlc_player = VlcPlayer(None)194 vlc_player = VlcPlayer(None)
196195
197 # WHEN: setup() is run196 # WHEN: setup() is run
198 vlc_player.setup(mocked_display)197 vlc_player.setup(mocked_output_display, mocked_controller)
199198
200 # THEN: The VLC instance should be created with the correct options199 # THEN: The VLC instance should be created with the correct options
201 mocked_vlc.Instance.assert_called_with('--no-video-title-show --mouse-hide-timeout=0')200 mocked_vlc.Instance.assert_called_with('--no-video-title-show --mouse-hide-timeout=0')
@@ -226,17 +225,17 @@
226 mocked_vlc = MagicMock()225 mocked_vlc = MagicMock()
227 mocked_vlc.Instance.return_value = mocked_instance226 mocked_vlc.Instance.return_value = mocked_instance
228 mocked_get_vlc.return_value = mocked_vlc227 mocked_get_vlc.return_value = mocked_vlc
229 mocked_display = MagicMock()228 mocked_output_display = MagicMock()
230 mocked_display.has_audio = False229 mocked_controller = MagicMock()
231 mocked_display.controller.is_live = True230 mocked_controller.is_live = True
232 mocked_display.size.return_value = (10, 10)231 mocked_output_display.size.return_value = (10, 10)
233 vlc_player = VlcPlayer(None)232 vlc_player = VlcPlayer(None)
234233
235 # WHEN: setup() is run234 # WHEN: setup() is run
236 vlc_player.setup(mocked_display)235 vlc_player.setup(mocked_output_display, mocked_controller)
237236
238 # THEN: The VLC instance should be created with the correct options237 # THEN: The VLC instance should be created with the correct options
239 mocked_vlc.Instance.assert_called_with('--no-video-title-show --no-audio --no-video-title-show')238 mocked_vlc.Instance.assert_called_with('--no-video-title-show')
240239
241 @patch('openlp.core.ui.media.vlcplayer.is_win')240 @patch('openlp.core.ui.media.vlcplayer.is_win')
242 @patch('openlp.core.ui.media.vlcplayer.is_macosx')241 @patch('openlp.core.ui.media.vlcplayer.is_macosx')
@@ -263,14 +262,14 @@
263 mocked_vlc = MagicMock()262 mocked_vlc = MagicMock()
264 mocked_vlc.Instance.return_value = mocked_instance263 mocked_vlc.Instance.return_value = mocked_instance
265 mocked_get_vlc.return_value = mocked_vlc264 mocked_get_vlc.return_value = mocked_vlc
266 mocked_display = MagicMock()265 mocked_output_display = MagicMock()
267 mocked_display.has_audio = False266 mocked_controller = MagicMock()
268 mocked_display.controller.is_live = True267 mocked_controller.is_live = True
269 mocked_display.size.return_value = (10, 10)268 mocked_output_display.size.return_value = (10, 10)
270 vlc_player = VlcPlayer(None)269 vlc_player = VlcPlayer(None)
271270
272 # WHEN: setup() is run271 # WHEN: setup() is run
273 vlc_player.setup(mocked_display)272 vlc_player.setup(mocked_output_display, mocked_controller)
274273
275 # THEN: set_hwnd should be called274 # THEN: set_hwnd should be called
276 mocked_media_player_new.set_hwnd.assert_called_with(2)275 mocked_media_player_new.set_hwnd.assert_called_with(2)
@@ -300,14 +299,14 @@
300 mocked_vlc = MagicMock()299 mocked_vlc = MagicMock()
301 mocked_vlc.Instance.return_value = mocked_instance300 mocked_vlc.Instance.return_value = mocked_instance
302 mocked_get_vlc.return_value = mocked_vlc301 mocked_get_vlc.return_value = mocked_vlc
303 mocked_display = MagicMock()302 mocked_output_display = MagicMock()
304 mocked_display.has_audio = False303 mocked_controller = MagicMock()
305 mocked_display.controller.is_live = True304 mocked_controller.is_live = True
306 mocked_display.size.return_value = (10, 10)305 mocked_output_display.size.return_value = (10, 10)
307 vlc_player = VlcPlayer(None)306 vlc_player = VlcPlayer(None)
308307
309 # WHEN: setup() is run308 # WHEN: setup() is run
310 vlc_player.setup(mocked_display)309 vlc_player.setup(mocked_output_display, mocked_controller)
311310
312 # THEN: set_nsobject should be called311 # THEN: set_nsobject should be called
313 mocked_media_player_new.set_nsobject.assert_called_with(2)312 mocked_media_player_new.set_nsobject.assert_called_with(2)
@@ -353,15 +352,13 @@
353 mocked_normcase.side_effect = lambda x: x352 mocked_normcase.side_effect = lambda x: x
354 mocked_vlc = MagicMock()353 mocked_vlc = MagicMock()
355 mocked_get_vlc.return_value = mocked_vlc354 mocked_get_vlc.return_value = mocked_vlc
356 mocked_controller = MagicMock()355 mocked_display = MagicMock()
357 mocked_controller.media_info.volume = 100356 mocked_display.media_info.volume = 100
358 mocked_controller.media_info.media_type = MediaType.Video357 mocked_display.media_info.media_type = MediaType.Video
359 mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path358 mocked_display.media_info.file_info.absoluteFilePath.return_value = media_path
360 mocked_vlc_media = MagicMock()359 mocked_vlc_media = MagicMock()
361 mocked_media = MagicMock()360 mocked_media = MagicMock()
362 mocked_media.get_duration.return_value = 10000361 mocked_media.get_duration.return_value = 10000
363 mocked_display = MagicMock()
364 mocked_display.controller = mocked_controller
365 mocked_display.vlc_instance.media_new_path.return_value = mocked_vlc_media362 mocked_display.vlc_instance.media_new_path.return_value = mocked_vlc_media
366 mocked_display.vlc_media_player.get_media.return_value = mocked_media363 mocked_display.vlc_media_player.get_media.return_value = mocked_media
367 vlc_player = VlcPlayer(None)364 vlc_player = VlcPlayer(None)
@@ -392,16 +389,13 @@
392 mocked_normcase.side_effect = lambda x: x389 mocked_normcase.side_effect = lambda x: x
393 mocked_vlc = MagicMock()390 mocked_vlc = MagicMock()
394 mocked_get_vlc.return_value = mocked_vlc391 mocked_get_vlc.return_value = mocked_vlc
395 mocked_controller = MagicMock()392 mocked_display = MagicMock()
396 mocked_controller.media_info.volume = 100393 mocked_display.media_info.volume = 100
397 mocked_controller.media_info.media_type = MediaType.CD394 mocked_display.media_info.media_type = MediaType.CD
398 mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path395 mocked_display.media_info.title_track = 1
399 mocked_controller.media_info.title_track = 1
400 mocked_vlc_media = MagicMock()396 mocked_vlc_media = MagicMock()
401 mocked_media = MagicMock()397 mocked_media = MagicMock()
402 mocked_media.get_duration.return_value = 10000398 mocked_media.get_duration.return_value = 10000
403 mocked_display = MagicMock()
404 mocked_display.controller = mocked_controller
405 mocked_display.vlc_instance.media_new_location.return_value = mocked_vlc_media399 mocked_display.vlc_instance.media_new_location.return_value = mocked_vlc_media
406 mocked_display.vlc_media_player.get_media.return_value = mocked_media400 mocked_display.vlc_media_player.get_media.return_value = mocked_media
407 mocked_subitems = MagicMock()401 mocked_subitems = MagicMock()
@@ -437,16 +431,14 @@
437 mocked_normcase.side_effect = lambda x: x431 mocked_normcase.side_effect = lambda x: x
438 mocked_vlc = MagicMock()432 mocked_vlc = MagicMock()
439 mocked_get_vlc.return_value = mocked_vlc433 mocked_get_vlc.return_value = mocked_vlc
440 mocked_controller = MagicMock()434 mocked_display = MagicMock()
441 mocked_controller.media_info.volume = 100435 mocked_display.media_info.volume = 100
442 mocked_controller.media_info.media_type = MediaType.CD436 mocked_display.media_info.media_type = MediaType.CD
443 mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path437 mocked_display.media_info.file_info.absoluteFilePath.return_value = media_path
444 mocked_controller.media_info.title_track = 1438 mocked_display.media_info.title_track = 1
445 mocked_vlc_media = MagicMock()439 mocked_vlc_media = MagicMock()
446 mocked_media = MagicMock()440 mocked_media = MagicMock()
447 mocked_media.get_duration.return_value = 10000441 mocked_media.get_duration.return_value = 10000
448 mocked_display = MagicMock()
449 mocked_display.controller = mocked_controller
450 mocked_display.vlc_instance.media_new_location.return_value = mocked_vlc_media442 mocked_display.vlc_instance.media_new_location.return_value = mocked_vlc_media
451 mocked_display.vlc_media_player.get_media.return_value = mocked_media443 mocked_display.vlc_media_player.get_media.return_value = mocked_media
452 mocked_subitems = MagicMock()444 mocked_subitems = MagicMock()
@@ -482,16 +474,14 @@
482 mocked_normcase.side_effect = lambda x: x474 mocked_normcase.side_effect = lambda x: x
483 mocked_vlc = MagicMock()475 mocked_vlc = MagicMock()
484 mocked_get_vlc.return_value = mocked_vlc476 mocked_get_vlc.return_value = mocked_vlc
485 mocked_controller = MagicMock()477 mocked_display = MagicMock()
486 mocked_controller.media_info.volume = 100478 mocked_display.media_info.volume = 100
487 mocked_controller.media_info.media_type = MediaType.CD479 mocked_display.media_info.media_type = MediaType.CD
488 mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path480 mocked_display.media_info.file_info.absoluteFilePath.return_value = media_path
489 mocked_controller.media_info.title_track = 1481 mocked_display.media_info.title_track = 1
490 mocked_vlc_media = MagicMock()482 mocked_vlc_media = MagicMock()
491 mocked_media = MagicMock()483 mocked_media = MagicMock()
492 mocked_media.get_duration.return_value = 10000484 mocked_media.get_duration.return_value = 10000
493 mocked_display = MagicMock()
494 mocked_display.controller = mocked_controller
495 mocked_display.vlc_instance.media_new_location.return_value = mocked_vlc_media485 mocked_display.vlc_instance.media_new_location.return_value = mocked_vlc_media
496 mocked_display.vlc_media_player.get_media.return_value = mocked_media486 mocked_display.vlc_media_player.get_media.return_value = mocked_media
497 mocked_subitems = MagicMock()487 mocked_subitems = MagicMock()
@@ -611,29 +601,28 @@
611 mocked_threading.Thread.return_value = mocked_thread601 mocked_threading.Thread.return_value = mocked_thread
612 mocked_vlc = MagicMock()602 mocked_vlc = MagicMock()
613 mocked_get_vlc.return_value = mocked_vlc603 mocked_get_vlc.return_value = mocked_vlc
614 mocked_controller = MagicMock()604 mocked_display = MagicMock()
615 mocked_controller.media_info.start_time = 0
616 mocked_controller.media_info.media_type = MediaType.Video
617 mocked_controller.media_info.volume = 100
618 mocked_media = MagicMock()605 mocked_media = MagicMock()
619 mocked_media.get_duration.return_value = 50000606 mocked_media.get_duration.return_value = 50000
620 mocked_display = MagicMock()607 mocked_output_display = MagicMock()
621 mocked_display.controller = mocked_controller608 mocked_output_display.media_info.start_time = 0
622 mocked_display.vlc_media_player.get_media.return_value = mocked_media609 mocked_output_display.media_info.media_type = MediaType.Video
610 mocked_output_display.media_info.volume = 100
611 mocked_output_display.vlc_media_player.get_media.return_value = mocked_media
623 vlc_player = VlcPlayer(None)612 vlc_player = VlcPlayer(None)
624 vlc_player.set_state(MediaState.Paused, mocked_display)613 vlc_player.set_state(MediaState.Paused, mocked_output_display)
625614
626 # WHEN: play() is called615 # WHEN: play() is called
627 with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \616 with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \
628 patch.object(vlc_player, 'volume') as mocked_volume:617 patch.object(vlc_player, 'volume') as mocked_volume:
629 mocked_media_state_wait.return_value = True618 mocked_media_state_wait.return_value = True
630 result = vlc_player.play(mocked_display)619 result = vlc_player.play(mocked_display, mocked_output_display)
631620
632 # THEN: A bunch of things should happen to play the media621 # THEN: A bunch of things should happen to play the media
633 mocked_thread.start.assert_called_with()622 mocked_thread.start.assert_called_with()
634 mocked_volume.assert_called_with(mocked_display, 100)623 mocked_volume.assert_called_with(mocked_output_display, 100)
635 assert MediaState.Playing == vlc_player.get_live_state()624 assert MediaState.Playing == vlc_player.get_live_state()
636 mocked_display.vlc_widget.raise_.assert_called_with()625 mocked_output_display.vlc_widget.raise_.assert_called_with()
637 assert result is True, 'The value returned from play() should be True'626 assert result is True, 'The value returned from play() should be True'
638627
639 @patch('openlp.core.ui.media.vlcplayer.threading')628 @patch('openlp.core.ui.media.vlcplayer.threading')
@@ -649,16 +638,15 @@
649 mocked_get_vlc.return_value = mocked_vlc638 mocked_get_vlc.return_value = mocked_vlc
650 mocked_controller = MagicMock()639 mocked_controller = MagicMock()
651 mocked_controller.media_info.start_time = 0640 mocked_controller.media_info.start_time = 0
652 mocked_display = MagicMock()641 mocked_output_display = MagicMock()
653 mocked_display.controller = mocked_controller
654 vlc_player = VlcPlayer(None)642 vlc_player = VlcPlayer(None)
655 vlc_player.set_state(MediaState.Paused, mocked_display)643 vlc_player.set_state(MediaState.Paused, mocked_output_display)
656644
657 # WHEN: play() is called645 # WHEN: play() is called
658 with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \646 with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \
659 patch.object(vlc_player, 'volume'):647 patch.object(vlc_player, 'volume'):
660 mocked_media_state_wait.return_value = False648 mocked_media_state_wait.return_value = False
661 result = vlc_player.play(mocked_display)649 result = vlc_player.play(mocked_controller, mocked_output_display)
662650
663 # THEN: A thread should be started, but the method should return False651 # THEN: A thread should be started, but the method should return False
664 mocked_thread.start.assert_called_with()652 mocked_thread.start.assert_called_with()
@@ -676,33 +664,32 @@
676 mocked_vlc = MagicMock()664 mocked_vlc = MagicMock()
677 mocked_get_vlc.return_value = mocked_vlc665 mocked_get_vlc.return_value = mocked_vlc
678 mocked_controller = MagicMock()666 mocked_controller = MagicMock()
679 mocked_controller.media_info.start_time = 0667 mocked_output_display = MagicMock()
680 mocked_controller.media_info.end_time = 50668 mocked_output_display.media_info.start_time = 0
681 mocked_controller.media_info.media_type = MediaType.DVD669 mocked_output_display.media_info.end_time = 50
682 mocked_controller.media_info.volume = 100670 mocked_output_display.media_info.media_type = MediaType.DVD
683 mocked_controller.media_info.title_track = 1671 mocked_output_display.media_info.volume = 100
684 mocked_controller.media_info.audio_track = 1672 mocked_output_display.media_info.title_track = 1
685 mocked_controller.media_info.subtitle_track = 1673 mocked_output_display.media_info.audio_track = 1
686 mocked_display = MagicMock()674 mocked_output_display.media_info.subtitle_track = 1
687 mocked_display.controller = mocked_controller
688 vlc_player = VlcPlayer(None)675 vlc_player = VlcPlayer(None)
689 vlc_player.set_state(MediaState.Paused, mocked_display)676 vlc_player.set_state(MediaState.Paused, mocked_output_display)
690677
691 # WHEN: play() is called678 # WHEN: play() is called
692 with patch.object(vlc_player, 'media_state_wait', return_value=True), \679 with patch.object(vlc_player, 'media_state_wait', return_value=True), \
693 patch.object(vlc_player, 'volume') as mocked_volume, \680 patch.object(vlc_player, 'volume') as mocked_volume, \
694 patch.object(vlc_player, 'get_live_state', return_value=MediaState.Loaded):681 patch.object(vlc_player, 'get_live_state', return_value=MediaState.Loaded):
695 result = vlc_player.play(mocked_display)682 result = vlc_player.play(mocked_controller, mocked_output_display)
696683
697 # THEN: A bunch of things should happen to play the media684 # THEN: A bunch of things should happen to play the media
698 mocked_thread.start.assert_called_with()685 mocked_thread.start.assert_called_with()
699 mocked_display.vlc_media_player.set_title.assert_called_with(1)686 mocked_output_display.vlc_media_player.set_title.assert_called_with(1)
700 mocked_display.vlc_media_player.play.assert_called_with()687 mocked_output_display.vlc_media_player.play.assert_called_with()
701 mocked_display.vlc_media_player.audio_set_track.assert_called_with(1)688 mocked_output_display.vlc_media_player.audio_set_track.assert_called_with(1)
702 mocked_display.vlc_media_player.video_set_spu.assert_called_with(1)689 mocked_output_display.vlc_media_player.video_set_spu.assert_called_with(1)
703 mocked_volume.assert_called_with(mocked_display, 100)690 mocked_volume.assert_called_with(mocked_output_display, 100)
704 assert MediaState.Playing == vlc_player.get_live_state()691 assert MediaState.Playing == vlc_player.get_live_state()
705 mocked_display.vlc_widget.raise_.assert_called_with()692 mocked_output_display.vlc_widget.raise_.assert_called_with()
706 assert result is True, 'The value returned from play() should be True'693 assert result is True, 'The value returned from play() should be True'
707694
708 @patch('openlp.core.ui.media.vlcplayer.get_vlc')695 @patch('openlp.core.ui.media.vlcplayer.get_vlc')
@@ -937,7 +924,6 @@
937 mocked_controller.media_info.end_time = 300924 mocked_controller.media_info.end_time = 300
938 mocked_controller.seek_slider.isSliderDown.return_value = False925 mocked_controller.seek_slider.isSliderDown.return_value = False
939 mocked_display = MagicMock()926 mocked_display = MagicMock()
940 mocked_display.controller = mocked_controller
941 mocked_display.vlc_media.get_state.return_value = 1927 mocked_display.vlc_media.get_state.return_value = 1
942 mocked_display.vlc_media_player.get_time.return_value = 400000928 mocked_display.vlc_media_player.get_time.return_value = 400000
943 vlc_player = VlcPlayer(None)929 vlc_player = VlcPlayer(None)
@@ -945,7 +931,7 @@
945 # WHEN: update_ui() is called931 # WHEN: update_ui() is called
946 with patch.object(vlc_player, 'stop') as mocked_stop, \932 with patch.object(vlc_player, 'stop') as mocked_stop, \
947 patch.object(vlc_player, 'set_visible') as mocked_set_visible:933 patch.object(vlc_player, 'set_visible') as mocked_set_visible:
948 vlc_player.update_ui(mocked_display)934 vlc_player.update_ui(mocked_controller, mocked_display)
949935
950 # THEN: Certain methods should be called936 # THEN: Certain methods should be called
951 mocked_stop.assert_called_with(mocked_display)937 mocked_stop.assert_called_with(mocked_display)
@@ -970,22 +956,19 @@
970 mocked_controller.media_info.end_time = 300956 mocked_controller.media_info.end_time = 300
971 mocked_controller.seek_slider.isSliderDown.return_value = False957 mocked_controller.seek_slider.isSliderDown.return_value = False
972 mocked_display = MagicMock()958 mocked_display = MagicMock()
973 mocked_display.controller = mocked_controller
974 mocked_display.vlc_media.get_state.return_value = 1959 mocked_display.vlc_media.get_state.return_value = 1
975 mocked_display.vlc_media_player.get_time.return_value = 400960 mocked_display.vlc_media_player.get_time.return_value = 300
976 mocked_display.controller.media_info.media_type = MediaType.DVD961 mocked_display.controller.media_info.media_type = MediaType.DVD
977 vlc_player = VlcPlayer(None)962 vlc_player = VlcPlayer(None)
978963
979 # WHEN: update_ui() is called964 # WHEN: update_ui() is called
980 with patch.object(vlc_player, 'stop') as mocked_stop, \965 with patch.object(vlc_player, 'stop') as mocked_stop:
981 patch.object(vlc_player, 'set_visible') as mocked_set_visible:966 vlc_player.update_ui(mocked_controller, mocked_display)
982 vlc_player.update_ui(mocked_display)
983967
984 # THEN: Certain methods should be called968 # THEN: Certain methods should be called
985 mocked_stop.assert_called_with(mocked_display)969 mocked_stop.assert_called_with(mocked_display)
986 assert 2 == mocked_stop.call_count970 assert 1 == mocked_stop.call_count
987 mocked_display.vlc_media_player.get_time.assert_called_with()971 mocked_display.vlc_media_player.get_time.assert_called_with()
988 mocked_set_visible.assert_called_with(mocked_display, False)
989 mocked_controller.seek_slider.setSliderPosition.assert_called_with(300)972 mocked_controller.seek_slider.setSliderPosition.assert_called_with(300)
990 expected_calls = [call(True), call(False)]973 expected_calls = [call(True), call(False)]
991 assert expected_calls == mocked_controller.seek_slider.blockSignals.call_args_list974 assert expected_calls == mocked_controller.seek_slider.blockSignals.call_args_list