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