comparison libervia/web/pages/calls/_browser/__init__.py @ 1564:bd3c880f4a47

browser (calls): add camera switching: - devices are listed on startup, and a `has_multiple_cameras` boolean is set - based on this data, switch camera button is hidden or not - a click/touch on the button will switch to the next available camera, and loop back to first one at the end
author Goffi <goffi@goffi.org>
date Fri, 18 Aug 2023 17:43:01 +0200
parents e3449beac8d8
children d282dbdd5ffd
comparison
equal deleted inserted replaced
1563:e3449beac8d8 1564:bd3c880f4a47
32 SCREEN_OFF_CLASS = "screen-off" 32 SCREEN_OFF_CLASS = "screen-off"
33 33
34 34
35 class CallUI: 35 class CallUI:
36 def __init__(self): 36 def __init__(self):
37 self.webrtc = WebRTC() 37 self.webrtc = WebRTC(
38 self.webrtc.screen_sharing_cb = self.on_sharing_screen 38 screen_sharing_cb=self.on_sharing_screen,
39 self.webrtc.on_connection_established_cb = self.on_connection_established 39 on_connection_established_cb=self.on_connection_established,
40 self.webrtc.on_reconnect_cb = self.on_reconnect 40 on_reconnect_cb=self.on_reconnect,
41 self.webrtc.on_connection_lost_cb = self.on_connection_lost 41 on_connection_lost_cb=self.on_connection_lost,
42 on_video_devices=self.on_video_devices,
43 )
42 self.mode = "search" 44 self.mode = "search"
43 self._status = None 45 self._status = None
44 self._callee = None 46 self._callee = None
45 self.contacts_elt = document["contacts"] 47 self.contacts_elt = document["contacts"]
46 self.search_container_elt = document["search_container"] 48 self.search_container_elt = document["search_container"]
69 document["exit_full_screen_btn"].bind( 71 document["exit_full_screen_btn"].bind(
70 "click", lambda __: self.toggle_fullscreen() 72 "click", lambda __: self.toggle_fullscreen()
71 ) 73 )
72 document["mute_audio_btn"].bind("click", self.toggle_audio_mute) 74 document["mute_audio_btn"].bind("click", self.toggle_audio_mute)
73 document["mute_video_btn"].bind("click", self.toggle_video_mute) 75 document["mute_video_btn"].bind("click", self.toggle_video_mute)
74 self.share_desktop_btn_elt = document["share_desktop_btn"] 76 self.share_desktop_col_elt = document["share_desktop_column"]
75 if hasattr(window.navigator.mediaDevices, "getDisplayMedia"): 77 if hasattr(window.navigator.mediaDevices, "getDisplayMedia"):
76 self.share_desktop_btn_elt.classList.remove("is-hidden-touch") 78 self.share_desktop_col_elt.classList.remove("is-hidden-touch")
77 # screen sharing is supported 79 # screen sharing is supported
78 self.share_desktop_btn_elt.bind("click", self.toggle_screen_sharing) 80 document["share_desktop_btn"].bind("click", self.toggle_screen_sharing)
79 else: 81 else:
80 self.share_desktop_btn_elt.classList.add("is-hidden") 82 self.share_desktop_col_elt.classList.add("is-hidden")
83 document["switch_camera_btn"].bind("click", self.on_switch_camera)
81 84
82 # search 85 # search
83 self.search_elt = document["search"] 86 self.search_elt = document["search"]
84 self.jid_search = JidSearch( 87 self.jid_search = JidSearch(
85 self.search_elt, 88 self.search_elt,
307 self.status = "reconnecting" 310 self.status = "reconnecting"
308 311
309 def on_connection_lost(self): 312 def on_connection_lost(self):
310 self.status = "connection-lost" 313 self.status = "connection-lost"
311 314
315 def on_video_devices(self, has_multiple_cameras: bool) -> None:
316 switch_camera_col_elt = document["switch_camera_column"]
317 if has_multiple_cameras:
318 switch_camera_col_elt.classList.remove("is-hidden", "is-hidden-desktop")
319 else:
320 switch_camera_col_elt.classList.add("is-hidden")
321
312 async def make_call(self, audio: bool = True, video: bool = True) -> None: 322 async def make_call(self, audio: bool = True, video: bool = True) -> None:
313 """Start a WebRTC call 323 """Start a WebRTC call
314 324
315 @param audio: True if an audio flux is required 325 @param audio: True if an audio flux is required
316 @param video: True if a video flux is required 326 @param video: True if a video flux is required
442 @param ev: the event object from the button click. 452 @param ev: the event object from the button click.
443 """ 453 """
444 if not self.search_elt.value: 454 if not self.search_elt.value:
445 return 455 return
446 # clear the search field 456 # clear the search field
447 self.search_elt.value = '' 457 self.search_elt.value = ""
448 # and dispatch the input event so items are updated 458 # and dispatch the input event so items are updated
449 self.search_elt.dispatchEvent(window.Event.new("input")) 459 self.search_elt.dispatchEvent(window.Event.new("input"))
450 460
451 def toggle_fullscreen(self, fullscreen: bool | None = None): 461 def toggle_fullscreen(self, fullscreen: bool | None = None):
452 """Toggle fullscreen mode for video elements. 462 """Toggle fullscreen mode for video elements.
508 def toggle_screen_sharing(self, evt): 518 def toggle_screen_sharing(self, evt):
509 aio.run(self.webrtc.toggle_screen_sharing()) 519 aio.run(self.webrtc.toggle_screen_sharing())
510 520
511 def on_sharing_screen(self, sharing: bool) -> None: 521 def on_sharing_screen(self, sharing: bool) -> None:
512 """Called when screen sharing state changes""" 522 """Called when screen sharing state changes"""
513 share_desktop_btn_elt = self.share_desktop_btn_elt 523 share_desktop_btn_elt = document["share_desktop_btn"]
514 if sharing: 524 if sharing:
515 share_desktop_btn_elt.classList.add("is-danger") 525 share_desktop_btn_elt.classList.add("is-danger")
516 share_desktop_btn_elt.classList.remove(INACTIVE_CLASS, SCREEN_OFF_CLASS) 526 share_desktop_btn_elt.classList.remove(INACTIVE_CLASS, SCREEN_OFF_CLASS)
517 else: 527 else:
518 share_desktop_btn_elt.classList.remove("is-danger") 528 share_desktop_btn_elt.classList.remove("is-danger")
519 share_desktop_btn_elt.classList.add(INACTIVE_CLASS, SCREEN_OFF_CLASS) 529 share_desktop_btn_elt.classList.add(INACTIVE_CLASS, SCREEN_OFF_CLASS)
530
531 def on_switch_camera(self, __) -> None:
532 aio.run(self.webrtc.switch_camera())
520 533
521 def _on_entity_click(self, item: dict) -> None: 534 def _on_entity_click(self, item: dict) -> None:
522 aio.run(self.on_entity_click(item)) 535 aio.run(self.on_entity_click(item))
523 536
524 async def on_entity_click(self, item: dict) -> None: 537 async def on_entity_click(self, item: dict) -> None: