1use 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>, }
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, }
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.create_room_request(proto::CreateRoomRequest {
76 name: name.to_owned(),
77 empty_timeout: options.empty_timeout,
78 departure_timeout: options.departure_timeout,
79 max_participants: options.max_participants,
80 node_id: options.node_id,
81 metadata: options.metadata,
82 egress: options.egress,
83 ..Default::default()
84 })
85 .await
86 }
87
88 pub async fn create_room_with_playout_delay(
90 &self,
91 name: &str,
92 options: CreateRoomOptions,
93 min_playout_delay: u32,
94 max_playout_delay: u32,
95 ) -> ServiceResult<proto::Room> {
96 self.create_room_request(proto::CreateRoomRequest {
97 name: name.to_owned(),
98 empty_timeout: options.empty_timeout,
99 departure_timeout: options.departure_timeout,
100 max_participants: options.max_participants,
101 node_id: options.node_id,
102 metadata: options.metadata,
103 egress: options.egress,
104 min_playout_delay,
105 max_playout_delay,
106 ..Default::default()
107 })
108 .await
109 }
110
111 async fn create_room_request(
112 &self,
113 request: proto::CreateRoomRequest,
114 ) -> ServiceResult<proto::Room> {
115 self.client
116 .request(
117 SVC,
118 "CreateRoom",
119 request,
120 self.base
121 .auth_header(VideoGrants { room_create: true, ..Default::default() }, None)?,
122 )
123 .await
124 .map_err(Into::into)
125 }
126
127 pub async fn list_rooms(&self, names: Vec<String>) -> ServiceResult<Vec<proto::Room>> {
128 let resp: proto::ListRoomsResponse = self
129 .client
130 .request(
131 SVC,
132 "ListRooms",
133 proto::ListRoomsRequest { names },
134 self.base
135 .auth_header(VideoGrants { room_list: true, ..Default::default() }, None)?,
136 )
137 .await?;
138
139 Ok(resp.rooms)
140 }
141
142 pub async fn delete_room(&self, room: &str) -> ServiceResult<()> {
143 self.client
144 .request(
145 SVC,
146 "DeleteRoom",
147 proto::DeleteRoomRequest { room: room.to_owned() },
148 self.base
149 .auth_header(VideoGrants { room_create: true, ..Default::default() }, None)?,
150 )
151 .await
152 .map_err(Into::into)
153 }
154
155 pub async fn update_room_metadata(
156 &self,
157 room: &str,
158 metadata: &str,
159 ) -> ServiceResult<proto::Room> {
160 self.client
161 .request(
162 SVC,
163 "UpdateRoomMetadata",
164 proto::UpdateRoomMetadataRequest {
165 room: room.to_owned(),
166 metadata: metadata.to_owned(),
167 },
168 self.base.auth_header(
169 VideoGrants { room_admin: true, room: room.to_owned(), ..Default::default() },
170 None,
171 )?,
172 )
173 .await
174 .map_err(Into::into)
175 }
176
177 pub async fn list_participants(
178 &self,
179 room: &str,
180 ) -> ServiceResult<Vec<proto::ParticipantInfo>> {
181 let resp: proto::ListParticipantsResponse = self
182 .client
183 .request(
184 SVC,
185 "ListParticipants",
186 proto::ListParticipantsRequest { room: room.to_owned() },
187 self.base.auth_header(
188 VideoGrants { room_admin: true, room: room.to_owned(), ..Default::default() },
189 None,
190 )?,
191 )
192 .await?;
193
194 Ok(resp.participants)
195 }
196
197 pub async fn get_participant(
198 &self,
199 room: &str,
200 identity: &str,
201 ) -> ServiceResult<proto::ParticipantInfo> {
202 self.client
203 .request(
204 SVC,
205 "GetParticipant",
206 proto::RoomParticipantIdentity {
207 room: room.to_owned(),
208 identity: identity.to_owned(),
209 },
210 self.base.auth_header(
211 VideoGrants { room_admin: true, room: room.to_owned(), ..Default::default() },
212 None,
213 )?,
214 )
215 .await
216 .map_err(Into::into)
217 }
218
219 pub async fn remove_participant(&self, room: &str, identity: &str) -> ServiceResult<()> {
220 self.client
221 .request(
222 SVC,
223 "RemoveParticipant",
224 proto::RoomParticipantIdentity {
225 room: room.to_owned(),
226 identity: identity.to_owned(),
227 },
228 self.base.auth_header(
229 VideoGrants { room_admin: true, room: room.to_owned(), ..Default::default() },
230 None,
231 )?,
232 )
233 .await
234 .map_err(Into::into)
235 }
236
237 pub async fn forward_participant(
238 &self,
239 room: &str,
240 identity: &str,
241 destination_room: &str,
242 ) -> ServiceResult<()> {
243 self.client
244 .request(
245 SVC,
246 "ForwardParticipant",
247 proto::ForwardParticipantRequest {
248 room: room.to_owned(),
249 identity: identity.to_owned(),
250 destination_room: destination_room.to_owned(),
251 },
252 self.base.auth_header(
253 VideoGrants {
254 room_admin: true,
255 room: room.to_owned(),
256 destination_room: destination_room.to_owned(),
257 ..Default::default()
258 },
259 None,
260 )?,
261 )
262 .await
263 .map_err(Into::into)
264 }
265
266 pub async fn move_participant(
267 &self,
268 room: &str,
269 identity: &str,
270 destination_room: &str,
271 ) -> ServiceResult<()> {
272 self.client
273 .request(
274 SVC,
275 "MoveParticipant",
276 proto::MoveParticipantRequest {
277 room: room.to_owned(),
278 identity: identity.to_owned(),
279 destination_room: destination_room.to_owned(),
280 },
281 self.base.auth_header(
282 VideoGrants {
283 room_admin: true,
284 room: room.to_owned(),
285 destination_room: destination_room.to_owned(),
286 ..Default::default()
287 },
288 None,
289 )?,
290 )
291 .await
292 .map_err(Into::into)
293 }
294
295 pub async fn mute_published_track(
296 &self,
297 room: &str,
298 identity: &str,
299 track_sid: &str,
300 muted: bool,
301 ) -> ServiceResult<proto::TrackInfo> {
302 let resp: proto::MuteRoomTrackResponse = self
303 .client
304 .request(
305 SVC,
306 "MutePublishedTrack",
307 proto::MuteRoomTrackRequest {
308 room: room.to_owned(),
309 identity: identity.to_owned(),
310 track_sid: track_sid.to_owned(),
311 muted,
312 },
313 self.base.auth_header(
314 VideoGrants { room_admin: true, room: room.to_owned(), ..Default::default() },
315 None,
316 )?,
317 )
318 .await?;
319
320 Ok(resp.track.unwrap())
321 }
322
323 pub async fn update_participant(
324 &self,
325 room: &str,
326 identity: &str,
327 options: UpdateParticipantOptions,
328 ) -> ServiceResult<proto::ParticipantInfo> {
329 self.client
330 .request(
331 SVC,
332 "UpdateParticipant",
333 proto::UpdateParticipantRequest {
334 room: room.to_owned(),
335 identity: identity.to_owned(),
336 permission: options.permission,
337 metadata: options.metadata,
338 attributes: options.attributes.to_owned(),
339 name: options.name,
340 },
341 self.base.auth_header(
342 VideoGrants { room_admin: true, room: room.to_owned(), ..Default::default() },
343 None,
344 )?,
345 )
346 .await
347 .map_err(Into::into)
348 }
349
350 pub async fn update_subscriptions(
351 &self,
352 room: &str,
353 identity: &str,
354 track_sids: Vec<String>,
355 subscribe: bool,
356 ) -> ServiceResult<()> {
357 self.client
358 .request(
359 SVC,
360 "UpdateSubscriptions",
361 proto::UpdateSubscriptionsRequest {
362 room: room.to_owned(),
363 identity: identity.to_owned(),
364 track_sids,
365 subscribe,
366 ..Default::default()
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
377 pub async fn send_data(
378 &self,
379 room: &str,
380 data: Vec<u8>,
381 options: SendDataOptions,
382 ) -> ServiceResult<()> {
383 let mut rng = rand::rng();
384 let nonce: Vec<u8> = (0..16).map(|_| rng.random::<u8>()).collect();
385 #[allow(deprecated)]
386 self.client
387 .request(
388 SVC,
389 "SendData",
390 proto::SendDataRequest {
391 room: room.to_owned(),
392 data,
393 destination_sids: options.destination_sids,
394 topic: options.topic,
395 kind: options.kind as i32,
396 destination_identities: options.destination_identities,
397 nonce,
398 },
399 self.base.auth_header(
400 VideoGrants { room_admin: true, room: room.to_owned(), ..Default::default() },
401 None,
402 )?,
403 )
404 .await
405 .map_err(Into::into)
406 }
407}