Mercurial > libervia-backend
comparison libervia/cli/cmd_call.py @ 4206:0f8ea0768a3b
cli (call): implement GUI output:
``call`` commands now handle various output. Beside the original one (now named
``simple``), a new ``gui`` one display a full featured GUI (make with Qt).
PyQt 6 or more needs to be installed.
rel 427
author | Goffi <goffi@goffi.org> |
---|---|
date | Sun, 11 Feb 2024 23:20:24 +0100 |
parents | 849721e1563b |
children | 9218d4331bb2 |
comparison
equal
deleted
inserted
replaced
4205:17a8168966f9 | 4206:0f8ea0768a3b |
---|---|
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. | 18 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | 19 |
20 | 20 |
21 from argparse import ArgumentParser | 21 from argparse import ArgumentParser |
22 import asyncio | 22 import asyncio |
23 from dataclasses import dataclass | |
23 from functools import partial | 24 from functools import partial |
24 import logging | 25 import logging |
25 import os | 26 import os |
27 from pathlib import Path | |
28 from typing import Callable | |
26 | 29 |
27 from prompt_toolkit.input import create_input | 30 from prompt_toolkit.input import create_input |
28 from prompt_toolkit.keys import Keys | 31 from prompt_toolkit.keys import Keys |
29 | 32 |
30 from libervia.backend.core.i18n import _ | 33 from libervia.backend.core.i18n import _ |
40 from . import base | 43 from . import base |
41 | 44 |
42 __commands__ = ["Call"] | 45 __commands__ = ["Call"] |
43 | 46 |
44 | 47 |
48 @dataclass | |
49 class CallData: | |
50 callee: jid.JID | |
51 sid: str|None = None | |
52 action_id: str|None = None | |
53 | |
54 | |
45 class WebRTCCall: | 55 class WebRTCCall: |
46 def __init__(self, host, profile: str, callee: jid.JID): | 56 def __init__(self, host, profile: str, callee: jid.JID, **kwargs): |
57 """Create and setup a webRTC instance | |
58 | |
59 @param profile: profile making or receiving the call | |
60 @param callee: peer jid | |
61 @param kwargs: extra kw args to use when instantiating WebRTC | |
62 """ | |
47 from libervia.frontends.tools import webrtc | 63 from libervia.frontends.tools import webrtc |
48 | 64 |
49 aio.install_glib_asyncio_iteration() | 65 aio.install_glib_asyncio_iteration() |
50 self.host = host | 66 self.host = host |
51 self.profile = profile | 67 self.profile = profile |
52 self.webrtc = webrtc.WebRTC(host.bridge, profile) | 68 self.webrtc = webrtc.WebRTC(host.bridge, profile, **kwargs) |
53 self.webrtc.callee = callee | 69 self.webrtc.callee = callee |
54 host.bridge.register_signal( | 70 host.bridge.register_signal( |
55 "ice_candidates_new", self.on_ice_candidates_new, "plugin" | 71 "ice_candidates_new", self.on_ice_candidates_new, "plugin" |
56 ) | 72 ) |
57 host.bridge.register_signal("call_setup", self.on_call_setup, "plugin") | 73 host.bridge.register_signal("call_setup", self.on_call_setup, "plugin") |
195 await self.host.a_quit() | 211 await self.host.a_quit() |
196 | 212 |
197 | 213 |
198 class Common(base.CommandBase): | 214 class Common(base.CommandBase): |
199 | 215 |
216 | |
217 def __init__(self, *args, **kwargs): | |
218 super().__init__( | |
219 *args, | |
220 use_output=C.OUTPUT_CUSTOM, | |
221 extra_outputs={ | |
222 "default": self.auto_output, | |
223 "simple": self.simple_output, | |
224 "gui": self.gui_output, | |
225 }, | |
226 **kwargs | |
227 ) | |
228 | |
200 def add_parser_options(self): | 229 def add_parser_options(self): |
201 self.parser.add_argument( | 230 self.parser.add_argument( |
202 "--no-ui", action="store_true", help=_("disable user interface") | 231 "--no-ui", action="store_true", help=_("disable user interface") |
203 ) | 232 ) |
204 | 233 |
212 if self.verbosity >= 1: | 241 if self.verbosity >= 1: |
213 root_logger.setLevel(logging.WARNING) | 242 root_logger.setLevel(logging.WARNING) |
214 if self.verbosity >= 2: | 243 if self.verbosity >= 2: |
215 root_logger.setLevel(logging.DEBUG) | 244 root_logger.setLevel(logging.DEBUG) |
216 | 245 |
217 async def start_ui(self, webrtc_call): | 246 async def make_webrtc_call(self, call_data: CallData, **kwargs) -> WebRTCCall: |
247 """Create the webrtc_call instance | |
248 | |
249 @param call_data: Call data of the command | |
250 @param kwargs: extra args used to instanciate WebRTCCall | |
251 | |
252 """ | |
253 webrtc_call = WebRTCCall(self.host, self.profile, call_data.callee, **kwargs) | |
254 if call_data.sid is None: | |
255 # we are making the call | |
256 await webrtc_call.start() | |
257 else: | |
258 # we are receiving the call | |
259 webrtc_call.sid = call_data.sid | |
260 if call_data.action_id is not None: | |
261 await self.host.bridge.action_launch( | |
262 call_data.action_id, | |
263 data_format.serialise({"cancelled": False}), | |
264 self.profile | |
265 ) | |
266 return webrtc_call | |
267 | |
268 async def auto_output(self, call_data: CallData): | |
269 """Make a guess on the best output to use on current platform""" | |
270 # For now we just use simple output | |
271 await self.simple_output(call_data) | |
272 | |
273 async def simple_output(self, call_data: CallData): | |
274 """Run simple output, with GStreamer ``autovideosink``""" | |
275 webrtc_call = await self.make_webrtc_call(call_data) | |
218 if not self.args.no_ui: | 276 if not self.args.no_ui: |
219 ui = UI(self.host, webrtc_call.webrtc) | 277 ui = UI(self.host, webrtc_call.webrtc) |
220 await ui.start() | 278 await ui.start() |
279 | |
280 async def gui_output(self, call_data: CallData): | |
281 """Run GUI output""" | |
282 media_dir = Path(await self.host.bridge.config_get("", "media_dir")) | |
283 icons_path = media_dir / "fonts/fontello/svg" | |
284 try: | |
285 from .call_gui import AVCallGUI | |
286 await AVCallGUI.run(self, call_data, icons_path) | |
287 except Exception as e: | |
288 self.disp(f"Error starting GUI: {e}", error=True) | |
289 self.host.quit(C.EXIT_ERROR) | |
221 | 290 |
222 | 291 |
223 class Make(Common): | 292 class Make(Common): |
224 def __init__(self, host): | 293 def __init__(self, host): |
225 super().__init__( | 294 super().__init__( |
237 help=_("JIDs of entity to call"), | 306 help=_("JIDs of entity to call"), |
238 ) | 307 ) |
239 | 308 |
240 async def start(self): | 309 async def start(self): |
241 await super().start() | 310 await super().start() |
242 callee = jid.JID(self.args.entity) | 311 await super().output(CallData( |
243 webrtc_call = WebRTCCall(self.host, self.profile, callee) | 312 callee=jid.JID(self.args.entity), |
244 await webrtc_call.start() | 313 )) |
245 await super().start_ui(webrtc_call) | |
246 | 314 |
247 | 315 |
248 class Receive(Common): | 316 class Receive(Common): |
249 def __init__(self, host): | 317 def __init__(self, host): |
250 super().__init__( | 318 super().__init__( |
295 ) | 363 ) |
296 return | 364 return |
297 | 365 |
298 self.disp(_("✅ Incoming call from {caller} accepted.").format(caller=caller)) | 366 self.disp(_("✅ Incoming call from {caller} accepted.").format(caller=caller)) |
299 | 367 |
300 webrtc_call = WebRTCCall(self.host, self.profile, peer_jid) | 368 await super().output(CallData( |
301 webrtc_call.sid = action_data["session_id"] | 369 callee=peer_jid, |
302 await self.host.bridge.action_launch( | 370 sid=action_data["session_id"], |
303 action_id, data_format.serialise({"cancelled": False}), profile | 371 action_id=action_id |
304 ) | 372 )) |
305 await super().start_ui(webrtc_call) | |
306 | 373 |
307 async def start(self): | 374 async def start(self): |
308 await super().start() | 375 await super().start() |
309 self.host.bridge.register_signal("action_new", self.on_action_new, "core") | 376 self.host.bridge.register_signal("action_new", self.on_action_new, "core") |
310 | 377 |