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