livekit_api/services/
room.rs

1// Copyright 2023 LiveKit, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use livekit_protocol as proto;
16use std::collections::HashMap;
17
18use super::{ServiceBase, ServiceResult, LIVEKIT_PACKAGE};
19use crate::{access_token::VideoGrants, get_env_keys, services::twirp_client::TwirpClient};
20use rand::Rng;
21
22const SVC: &str = "RoomService";
23
24#[derive(Debug, Clone, Default)]
25pub struct CreateRoomOptions {
26    pub empty_timeout: u32,
27    pub departure_timeout: u32,
28    pub max_participants: u32,
29    pub node_id: String,
30    pub metadata: String,
31    pub egress: Option<proto::RoomEgress>, // TODO(theomonnom): Better API?
32}
33
34#[derive(Debug, Clone, Default)]
35pub struct UpdateParticipantOptions {
36    pub metadata: String,
37    pub attributes: HashMap<String, String>,
38    pub permission: Option<proto::ParticipantPermission>,
39    pub name: String, // No effect if left empty
40}
41
42#[derive(Debug, Clone, Default)]
43pub struct SendDataOptions {
44    pub kind: proto::data_packet::Kind,
45    #[deprecated(note = "Use destination_identities instead")]
46    pub destination_sids: Vec<String>,
47    pub destination_identities: Vec<String>,
48    pub topic: Option<String>,
49}
50
51#[derive(Debug)]
52pub struct RoomClient {
53    base: ServiceBase,
54    client: TwirpClient,
55}
56
57impl RoomClient {
58    pub fn with_api_key(host: &str, api_key: &str, api_secret: &str) -> Self {
59        Self {
60            base: ServiceBase::with_api_key(api_key, api_secret),
61            client: TwirpClient::new(host, LIVEKIT_PACKAGE, None),
62        }
63    }
64
65    pub fn new(host: &str) -> ServiceResult<Self> {
66        let (api_key, api_secret) = get_env_keys()?;
67        Ok(Self::with_api_key(host, &api_key, &api_secret))
68    }
69
70    pub async fn create_room(
71        &self,
72        name: &str,
73        options: CreateRoomOptions,
74    ) -> ServiceResult<proto::Room> {
75        self.client
76            .request(
77                SVC,
78                "CreateRoom",
79                proto::CreateRoomRequest {
80                    name: name.to_owned(),
81                    empty_timeout: options.empty_timeout,
82                    departure_timeout: options.departure_timeout,
83                    max_participants: options.max_participants,
84                    node_id: options.node_id,
85                    metadata: options.metadata,
86                    egress: options.egress,
87                    ..Default::default()
88                },
89                self.base
90                    .auth_header(VideoGrants { room_create: true, ..Default::default() }, None)?,
91            )
92            .await
93            .map_err(Into::into)
94    }
95
96    pub async fn list_rooms(&self, names: Vec<String>) -> ServiceResult<Vec<proto::Room>> {
97        let resp: proto::ListRoomsResponse = self
98            .client
99            .request(
100                SVC,
101                "ListRooms",
102                proto::ListRoomsRequest { names },
103                self.base
104                    .auth_header(VideoGrants { room_list: true, ..Default::default() }, None)?,
105            )
106            .await?;
107
108        Ok(resp.rooms)
109    }
110
111    pub async fn delete_room(&self, room: &str) -> ServiceResult<()> {
112        self.client
113            .request(
114                SVC,
115                "DeleteRoom",
116                proto::DeleteRoomRequest { room: room.to_owned() },
117                self.base
118                    .auth_header(VideoGrants { room_create: true, ..Default::default() }, None)?,
119            )
120            .await
121            .map_err(Into::into)
122    }
123
124    pub async fn update_room_metadata(
125        &self,
126        room: &str,
127        metadata: &str,
128    ) -> ServiceResult<proto::Room> {
129        self.client
130            .request(
131                SVC,
132                "UpdateRoomMetadata",
133                proto::UpdateRoomMetadataRequest {
134                    room: room.to_owned(),
135                    metadata: metadata.to_owned(),
136                },
137                self.base.auth_header(
138                    VideoGrants { room_admin: true, room: room.to_owned(), ..Default::default() },
139                    None,
140                )?,
141            )
142            .await
143            .map_err(Into::into)
144    }
145
146    pub async fn list_participants(
147        &self,
148        room: &str,
149    ) -> ServiceResult<Vec<proto::ParticipantInfo>> {
150        let resp: proto::ListParticipantsResponse = self
151            .client
152            .request(
153                SVC,
154                "ListParticipants",
155                proto::ListParticipantsRequest { room: room.to_owned() },
156                self.base.auth_header(
157                    VideoGrants { room_admin: true, room: room.to_owned(), ..Default::default() },
158                    None,
159                )?,
160            )
161            .await?;
162
163        Ok(resp.participants)
164    }
165
166    pub async fn get_participant(
167        &self,
168        room: &str,
169        identity: &str,
170    ) -> ServiceResult<proto::ParticipantInfo> {
171        self.client
172            .request(
173                SVC,
174                "GetParticipant",
175                proto::RoomParticipantIdentity {
176                    room: room.to_owned(),
177                    identity: identity.to_owned(),
178                },
179                self.base.auth_header(
180                    VideoGrants { room_admin: true, room: room.to_owned(), ..Default::default() },
181                    None,
182                )?,
183            )
184            .await
185            .map_err(Into::into)
186    }
187
188    pub async fn remove_participant(&self, room: &str, identity: &str) -> ServiceResult<()> {
189        self.client
190            .request(
191                SVC,
192                "RemoveParticipant",
193                proto::RoomParticipantIdentity {
194                    room: room.to_owned(),
195                    identity: identity.to_owned(),
196                },
197                self.base.auth_header(
198                    VideoGrants { room_admin: true, room: room.to_owned(), ..Default::default() },
199                    None,
200                )?,
201            )
202            .await
203            .map_err(Into::into)
204    }
205
206    pub async fn forward_participant(
207        &self,
208        room: &str,
209        identity: &str,
210        destination_room: &str,
211    ) -> ServiceResult<()> {
212        self.client
213            .request(
214                SVC,
215                "ForwardParticipant",
216                proto::ForwardParticipantRequest {
217                    room: room.to_owned(),
218                    identity: identity.to_owned(),
219                    destination_room: destination_room.to_owned(),
220                },
221                self.base.auth_header(
222                    VideoGrants {
223                        room_admin: true,
224                        room: room.to_owned(),
225                        destination_room: destination_room.to_owned(),
226                        ..Default::default()
227                    },
228                    None,
229                )?,
230            )
231            .await
232            .map_err(Into::into)
233    }
234
235    pub async fn move_participant(
236        &self,
237        room: &str,
238        identity: &str,
239        destination_room: &str,
240    ) -> ServiceResult<()> {
241        self.client
242            .request(
243                SVC,
244                "MoveParticipant",
245                proto::MoveParticipantRequest {
246                    room: room.to_owned(),
247                    identity: identity.to_owned(),
248                    destination_room: destination_room.to_owned(),
249                },
250                self.base.auth_header(
251                    VideoGrants {
252                        room_admin: true,
253                        room: room.to_owned(),
254                        destination_room: destination_room.to_owned(),
255                        ..Default::default()
256                    },
257                    None,
258                )?,
259            )
260            .await
261            .map_err(Into::into)
262    }
263
264    pub async fn mute_published_track(
265        &self,
266        room: &str,
267        identity: &str,
268        track_sid: &str,
269        muted: bool,
270    ) -> ServiceResult<proto::TrackInfo> {
271        let resp: proto::MuteRoomTrackResponse = self
272            .client
273            .request(
274                SVC,
275                "MutePublishedTrack",
276                proto::MuteRoomTrackRequest {
277                    room: room.to_owned(),
278                    identity: identity.to_owned(),
279                    track_sid: track_sid.to_owned(),
280                    muted,
281                },
282                self.base.auth_header(
283                    VideoGrants { room_admin: true, room: room.to_owned(), ..Default::default() },
284                    None,
285                )?,
286            )
287            .await?;
288
289        Ok(resp.track.unwrap())
290    }
291
292    pub async fn update_participant(
293        &self,
294        room: &str,
295        identity: &str,
296        options: UpdateParticipantOptions,
297    ) -> ServiceResult<proto::ParticipantInfo> {
298        self.client
299            .request(
300                SVC,
301                "UpdateParticipant",
302                proto::UpdateParticipantRequest {
303                    room: room.to_owned(),
304                    identity: identity.to_owned(),
305                    permission: options.permission,
306                    metadata: options.metadata,
307                    attributes: options.attributes.to_owned(),
308                    name: options.name,
309                },
310                self.base.auth_header(
311                    VideoGrants { room_admin: true, room: room.to_owned(), ..Default::default() },
312                    None,
313                )?,
314            )
315            .await
316            .map_err(Into::into)
317    }
318
319    pub async fn update_subscriptions(
320        &self,
321        room: &str,
322        identity: &str,
323        track_sids: Vec<String>,
324        subscribe: bool,
325    ) -> ServiceResult<()> {
326        self.client
327            .request(
328                SVC,
329                "UpdateSubscriptions",
330                proto::UpdateSubscriptionsRequest {
331                    room: room.to_owned(),
332                    identity: identity.to_owned(),
333                    track_sids,
334                    subscribe,
335                    ..Default::default()
336                },
337                self.base.auth_header(
338                    VideoGrants { room_admin: true, room: room.to_owned(), ..Default::default() },
339                    None,
340                )?,
341            )
342            .await
343            .map_err(Into::into)
344    }
345
346    pub async fn send_data(
347        &self,
348        room: &str,
349        data: Vec<u8>,
350        options: SendDataOptions,
351    ) -> ServiceResult<()> {
352        let mut rng = rand::rng();
353        let nonce: Vec<u8> = (0..16).map(|_| rng.random::<u8>()).collect();
354        #[allow(deprecated)]
355        self.client
356            .request(
357                SVC,
358                "SendData",
359                proto::SendDataRequest {
360                    room: room.to_owned(),
361                    data,
362                    destination_sids: options.destination_sids,
363                    topic: options.topic,
364                    kind: options.kind as i32,
365                    destination_identities: options.destination_identities,
366                    nonce,
367                },
368                self.base.auth_header(
369                    VideoGrants { room_admin: true, room: room.to_owned(), ..Default::default() },
370                    None,
371                )?,
372            )
373            .await
374            .map_err(Into::into)
375    }
376}