diff options
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py')
| -rw-r--r-- | packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py | 50 |
1 files changed, 50 insertions, 0 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py index 1629801..a5cccaa 100644 --- a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py +++ b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py @@ -72,6 +72,7 @@ from meshbay_common.adminop import ( OP_TMDB_OVERRIDE, OP_MUSICBRAINZ_CONFIG, OP_MUSICBRAINZ_ENABLED, + OP_AUDIO_ROOT, OP_ROOT_ADD, OP_ROOT_REMOVE, OP_GROUP_ATTACH, @@ -398,6 +399,8 @@ class WebRTCPeerSession: self._do_tmdb_enabled(msg) elif mtype == MNP.VIDEO_ROOT: self._do_video_root(msg) + elif mtype == MNP.AUDIO_ROOT: + self._do_audio_root(msg) elif mtype == MNP.MEDIA_META_REQ: self._spawn(self._do_media_meta_request(msg)) elif mtype == MNP.SEASON_META_REQ: @@ -685,6 +688,10 @@ class WebRTCPeerSession: "musicbrainz_enabled": bool(self._group_ctx().get("musicbrainz_enabled", True)), "musicbrainz_contact_configured": bool( self._ctx.get("daemon_state", {}).get("musicbrainz_contact_configured", False)), + # Which folder the Music app treats as its entry point for this + # group — same shape as video_root above, "" means unset (the + # Music tab shows nothing yet). + "audio_root": self._group_ctx().get("audio_root") or "", # So a client that connects mid-scan shows the indexing state # immediately, instead of waiting for the next periodic # INDEX_PROGRESS push. Never a path or filename — see @@ -1852,6 +1859,46 @@ class WebRTCPeerSession: except Exception: pass + def _do_audio_root(self, msg: dict) -> None: + """Same shape as _do_video_root above — the Music app's own entry point.""" + path = msg.get("path") + if not isinstance(path, str): + self._send({"type": "error", "detail": "Missing or invalid 'path'"}) + return + path = path.strip("/") + if path: + ctx = self._group_ctx() + resolved = ctx["roots"].resolve(path) if ctx.get("roots") else None + if not resolved or not resolved.is_dir(): + self._send({"type": "error", "detail": "Not a directory in this group"}) + return + if not self._has_admin_authority(): + self._send({"type": "error", "detail": "No authorized key for this"}) + return + self._issue_admin_challenge(OP_AUDIO_ROOT, path) + + async def _admin_exec_audio_root( + self, pending: dict, transcript: bytes, sig: bytes, + ) -> None: + path = pending["subject"] + if not await self._verify_admin_sig(transcript, sig): + self._send({"type": "error", "detail": "Signature verification failed"}) + self._audit("admin_auth_failed", f"audio_root:{path}") + return + try: + await self._run_op(ops.set_audio_root, self._group_id or "", path) + except ops.OpError as e: + self._send({"type": "error", "detail": e.message}) + return + self._audit("audio_root", path) + + notice = {"type": MNP.AUDIO_ROOT_ACK, "v": MNP_VERSION, "path": path} + for uid, session in list(self._peer_registry().items()): + try: + session._send(notice) + except Exception: + pass + def _do_musicbrainz_config(self, msg: dict) -> None: """ Set (or clear) the node-wide MusicBrainz User-Agent contact string @@ -3551,6 +3598,9 @@ class WebRTCPeerSession: elif pending["op"] == OP_MUSICBRAINZ_ENABLED: self._spawn( self._admin_exec_musicbrainz_enabled(pending, transcript, sig_bytes)) + elif pending["op"] == OP_AUDIO_ROOT: + self._spawn( + self._admin_exec_audio_root(pending, transcript, sig_bytes)) elif pending["op"] == OP_ROOT_ADD: self._spawn( self._admin_exec_root_add(pending, transcript, sig_bytes)) |