changeset 4294:a0ed5c976bf8

component conferences, plugin XEP-0167, XEP-0298: add stream user metadata: A/V conference now adds user metadata about the stream it is forwarding through XEP-0298. This is parsed and added to metadata during confirmation on client side. rel 448
author Goffi <goffi@goffi.org>
date Tue, 06 Aug 2024 23:43:11 +0200
parents 9447796408f6
children 7d98d894933c
files libervia/backend/plugins/plugin_comp_conferences/__init__.py libervia/backend/plugins/plugin_xep_0060.py libervia/backend/plugins/plugin_xep_0167/__init__.py libervia/backend/plugins/plugin_xep_0298.py
diffstat 4 files changed, 86 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/libervia/backend/plugins/plugin_comp_conferences/__init__.py	Mon Jul 29 03:49:26 2024 +0200
+++ b/libervia/backend/plugins/plugin_comp_conferences/__init__.py	Tue Aug 06 23:43:11 2024 +0200
@@ -236,10 +236,22 @@
 
     async def a_on_offer(self, data: dict) -> None:
         client: SatXMPPEntity = self.client.get_virtual_client(self.session["local_jid"])
+        call_data = {"sdp": data["sdp"]}
+        try:
+            # FIXME: This assume that all username are coming from XMPP, "source" should
+            #   be used instead to be sure to match XMPP users, and other ones should use
+            #   a non "xmpp:" URL scheme.
+            user_jid = jid.JID(data["username"])
+            if not user_jid.user:
+                raise ValueError("Missing user part.")
+        except Exception as e:
+            log.warning("Username is not a JID: {data['username']=}, {e}")
+        else:
+            call_data["metadata"] = {"user": user_jid}
         sid = await self._rtp.call_start(
             client,
             self.session["peer_jid"],
-            {"sdp": data["sdp"]},
+            call_data,
             session_id=f"{self.client_id}-{data['id']}",
         )
         session = self._j.get_session(client, sid)
@@ -547,7 +559,6 @@
         log.error(f"Can't run Galene process: {failure_.value}")
 
 
-
 @implementer(iwokkel.IDisco)
 class ConferencesHandler(XMPPHandler):
 
--- a/libervia/backend/plugins/plugin_xep_0060.py	Mon Jul 29 03:49:26 2024 +0200
+++ b/libervia/backend/plugins/plugin_xep_0060.py	Tue Aug 06 23:43:11 2024 +0200
@@ -1539,7 +1539,7 @@
         try:
             return {
                 jid.JID(s["jid"]): s["subscription"]
-                for s in subscriptions_elt.elements((pubsub.NS_PUBSUB, "subscription"))
+                for s in subscriptions_elt.elements(pubsub.NS_PUBSUB, "subscription")
             }
         except KeyError:
             raise ValueError(
--- a/libervia/backend/plugins/plugin_xep_0167/__init__.py	Mon Jul 29 03:49:26 2024 +0200
+++ b/libervia/backend/plugins/plugin_xep_0167/__init__.py	Tue Aug 06 23:43:11 2024 +0200
@@ -41,7 +41,7 @@
     NS_JINGLE_RTP_AUDIO,
     NS_JINGLE_RTP_INFO,
     NS_JINGLE_RTP_VIDEO,
-    NS_AV_CONFERENCES
+    NS_AV_CONFERENCES,
 )
 
 
@@ -419,17 +419,22 @@
 
         session["call_type"] = call_type
         cancellable_deferred = session.setdefault("cancellable_deferred", [])
+        action_extra = {
+            "session_id": session["id"],
+            "from_jid": peer_jid.full(),
+            "type": C.META_TYPE_CALL,
+            "sub_type": call_type,
+        }
+        try:
+            action_extra["metadata"] = {"user": session["metadata"]["peer_user"].full()}
+        except KeyError:
+            pass
 
         dialog_d = xml_tools.defer_dialog(
             self.host,
             _(CONFIRM).format(peer=peer_jid.userhost(), call_type=call_type),
             _(CONFIRM_TITLE),
-            action_extra={
-                "session_id": session["id"],
-                "from_jid": peer_jid.full(),
-                "type": C.META_TYPE_CALL,
-                "sub_type": call_type,
-            },
+            action_extra=action_extra,
             security_limit=SECURITY_LIMIT,
             profile=client.profile,
         )
@@ -596,26 +601,22 @@
         answer_sdp_d.addTimeout(2 * 60, reactor)
 
         call_setup = session.get("call_setup_cb")
+        call_data = {
+            "role": session["role"],
+            "sdp": sdp,
+        }
 
         if call_setup is None:
             self.host.bridge.call_setup(
                 session["id"],
-                data_format.serialise(
-                    {
-                        "role": session["role"],
-                        "sdp": sdp,
-                    }
-                ),
+                data_format.serialise(call_data),
                 client.profile,
             )
         else:
             await call_setup(
                 client,
                 session,
-                {
-                    "role": session["role"],
-                    "sdp": sdp,
-                },
+                call_data,
             )
 
         answer_sdp = await answer_sdp_d
@@ -681,7 +682,7 @@
             content_data = session["contents"][content_name]
         application_data = content_data["application_data"]
         if action == self._j.A_PREPARE_CONFIRMATION:
-            session["metadata"] = {}
+            session.setdefault("metadata", {})
             session.setdefault("peer_metadata", {})
             try:
                 media = application_data["media"] = desc_elt["media"]
--- a/libervia/backend/plugins/plugin_xep_0298.py	Mon Jul 29 03:49:26 2024 +0200
+++ b/libervia/backend/plugins/plugin_xep_0298.py	Tue Aug 06 23:43:11 2024 +0200
@@ -20,6 +20,7 @@
 from urllib.parse import quote, unquote
 from twisted.words.protocols.jabber import jid
 from twisted.words.xish import domish
+from libervia.backend.core.core_types import SatXMPPEntity
 from libervia.backend.core.i18n import _
 from libervia.backend.core.constants import Const as C
 from libervia.backend.core.log import getLogger
@@ -27,6 +28,8 @@
 from zope.interface import implementer
 from twisted.words.protocols.jabber.xmlstream import XMPPHandler
 
+from libervia.backend.plugins.plugin_xep_0166 import XEP_0166
+
 log = getLogger(__name__)
 
 
@@ -36,7 +39,7 @@
     C.PI_TYPE: "XEP",
     C.PI_MODES: C.PLUG_MODE_BOTH,
     C.PI_PROTOCOLS: [],
-    C.PI_DEPENDENCIES: [],
+    C.PI_DEPENDENCIES: ["XEP-0167"],
     C.PI_MAIN: "XEP_0298",
     C.PI_HANDLER: "yes",
     C.PI_DESCRIPTION: _(
@@ -55,10 +58,58 @@
     def __init__(self, host):
         log.info(f"plugin {PLUGIN_INFO[C.PI_NAME]!r} initialization")
         self.host = host
+        self._j: XEP_0166 = host.plugins["XEP-0166"]
+        host.trigger.add(
+            "XEP-0167_jingle_session_init", self._jingle_session_init_trigger
+        )
+        host.trigger.add("XEP-0167_jingle_handler", self._jingle_handler_trigger)
 
     def get_handler(self, client):
         return CoinHandler(self)
 
+    def _jingle_session_init_trigger(
+        self,
+        client: SatXMPPEntity,
+        session: dict,
+        content_name: str,
+        media: str,
+        media_data: dict,
+        desc_elt: domish.Element,
+    ) -> None:
+        """Check for the presence of "user" metadata, and add relevant elements."""
+        if client.profile == "conferences":
+            if content_name != next(iter(session["contents"].keys())):
+                # We only apply metadata for the first content, as it is global.
+                return
+            try:
+                user = session["metadata"]["user"]
+            except KeyError:
+                return
+            jingle_elt = session["jingle_elt"]
+            conference_info_elt = self.add_conference_info(jingle_elt, True)
+            self.add_user(conference_info_elt, user)
+
+    async def _jingle_handler_trigger(
+        self,
+        client: SatXMPPEntity,
+        action: str,
+        session: dict,
+        content_name: str,
+        desc_elt: domish.Element,
+    ) -> None:
+        # this is a session metadata, so we only generate it on the first content
+        if action == self._j.A_PREPARE_CONFIRMATION and content_name == next(
+            iter(session["contents"])
+        ):
+            jingle_elt = session["jingle_elt"]
+            infos = self.parse(jingle_elt)
+            try:
+                user = infos["info"]["users"][0]
+            except (KeyError, IndexError):
+                pass
+            else:
+                session.setdefault("metadata", {})["peer_user"] = user
+
     def add_conference_info(
         self, jingle_elt: domish.Element, is_focus: bool = False, **kwargs
     ) -> domish.Element:
@@ -104,7 +155,7 @@
         """
         try:
             conference_info_elt = next(
-                jingle_elt.elements((NS_CONFERENCE_INFO, "conference-info"))
+                jingle_elt.elements(NS_CONFERENCE_INFO, "conference-info")
             )
         except StopIteration:
             return {}