collaboration
Collaboration rooms: the room page, its WebSocket, and token fan-out.
A collaboration session is a normal session whose container also runs a control plane that accepts a table of tokens (controller, viewers, gamepad slots, mouse/keyboard owner). This module serves the room page, relays chat and control messages between participants over a WebSocket, and pushes token changes down to every container of the session.
attributelogger= logging.getLogger(__name__)attributerouter= APIRouter()attributeROOM_CONNECTIONSdict[str, dict[str, Any]]= {}attributeROOM_ALIAS_KEYSdict[str, bytes]= {}attributeTOKEN_ENDPOINT_CACHEdict[str, str]= {}attributeGAMEPAD_SLOTS= 4func_alias(session_id, token) -> strAnother participant's token as a viewer is told it.
Stable for the room, so the room page keys that participant's streams and slots by it, and no use for joining.
paramsession_idstrparamtokenstrReturns
strfunc_as_viewer_sees(session_id, payload, own) -> dict[str, Any]payload as viewer own receives it.
A room token, the controller's above all, lets whoever holds it take that participant's
part, so only own stays itself.
paramsession_idstrparampayloaddict[str, Any]paramownstrReturns
dict[str, typing.Any]func_absolutize_room_assets(html) -> strRewrite relative asset references in the room template to /companion/ui/room/.
The page is served at /room/<session_id> while its hashed assets live
under the static /ui/room/ mount, so relative references would 404.
They are addressed through the companion lane, which a reverse proxy that
signs users in passes raw, so a guest with an invite link, who has no
account at the proxy's provider, gets the page whole.
paramhtmlstrRaw room template.
Returns
strThe template with relative src/href values made absolute.
func_join_url(session_id, token) -> strReturn the relative invite URL of a room for token.
paramsession_idstrparamtokenstrReturns
strfunccollaborative_room(request, session_id, collab_token=Query(None, alias='token')) -> ResponseServe the collaboration room page.
Authenticates the visitor as controller (session access token or controller token), existing viewer, or new participant/read-only viewer via an invite token. New viewers get a personal token and are redirected.
paramrequestRequestIncoming request.
paramsession_idUUIDCollaboration session id.
paramcollab_tokenstr | None= Query(None, alias='token')Controller, viewer, or invite token.
Returns
fastapi.responses.ResponseThe room HTML, or a redirect for newly registered viewers.
funcbroadcast_token_state(session_id, session_data) -> NonePush the current token table to every container of a session.
paramsession_idstrSession id.
paramsession_datadict[str, Any]Session record.
Returns
Nonefunc_owner_group(username) -> list[str]Return the groups of a user.
paramusernamestrReturns
list[str]funcbroadcast_to_room(session_id, payload) -> NoneSend a JSON payload to every participant of a room.
paramsession_idstrparampayloaddict[str, Any]Returns
Nonefuncbroadcast_binary_to_room(session_id, payload, sender_ws) -> NoneRelay a binary (audio/video) packet to every participant except the sender.
paramsession_idstrparampayloadbytesparamsender_wsWebSocketReturns
Nonefuncroom_websocket(websocket, session_id) -> NoneHandle a participant's WebSocket for the lifetime of the connection.
paramwebsocketWebSocketThe accepted socket.
paramsession_idUUIDCollaboration session id.
Returns
Nonefuncbroadcast_state(session_id) -> NoneSend the participant list and designated speaker to the room.
paramsession_idstrReturns
Nonefuncvalid_slot(slot) -> boolWhether slot names one of the container's gamepad slots.
paramslotAnyReturns
boolfuncgamepads_text(slots) -> strName slots for a room notice: "Gamepad 3", "Gamepads 3 and 4".
paramslotslist[int]Returns
strfunc_slot_holders(session_data) -> list[tuple[dict[str, Any], str, str, Any]]Every participant that can hold gamepad slots.
paramsession_datadict[str, Any]Returns
list(record, key, name, token) for each: the controller's slots live under
func_take_slot(session_data, slot, keep=None) -> list[str]Take slot from whoever holds it other than token keep.
paramsession_datadict[str, Any]paramslotintparamkeepAny= NoneReturns
listThe room notices saying so.
func_push_slots(session_id, session_data, notices) -> NonePush a slot change to the session's containers, keep it, and tell the room.
paramsession_idstrparamsession_datadict[str, Any]paramnoticeslist[str]Returns
Nonefunchandle_assign_slot(session_id, viewer_token, slot) -> NoneGive a participant a gamepad slot beside those it holds.
The slot is taken from whoever held it, and None clears every slot the
participant holds. A participant holding several drives one with each of
its local controllers, in slot order: Selkies hands a token's list to the
page's pads in turn.
paramsession_idstrparamviewer_tokenstrparamslotint | NoneReturns
Nonefunchandle_release_slot(session_id, slot) -> NoneReturn a gamepad slot to the pool from whoever holds it.
paramsession_idstrparamslotAnyReturns
Nonefunchandle_assign_mk(session_id, target_token) -> NoneGive mouse and keyboard control to a participant (None = controller).
paramsession_idstrparamtarget_tokenstr | NoneReturns
Nonefuncnotify_session_ended(session_id) -> NoneTell every participant that the session ended and drop the room.
paramsession_idstrReturns
None