1use livekit_protocol as proto;
16
17use super::{ServiceBase, ServiceResult, LIVEKIT_PACKAGE};
18use crate::services::twirp_client::TwirpClient;
19use livekit_token::{get_env_keys, VideoGrants};
20
21#[derive(Default, Clone, Debug)]
22pub struct CreateIngressOptions {
23 pub name: String,
24 pub room_name: String,
25 pub participant_metadata: String,
26 pub participant_identity: String,
27 pub participant_name: String,
28 pub audio: proto::IngressAudioOptions,
29 pub video: proto::IngressVideoOptions,
30 pub bypass_transcoding: bool,
31 pub enable_transcoding: Option<bool>,
32 pub url: String,
33}
34
35#[derive(Default, Clone, Debug)]
36pub struct UpdateIngressOptions {
37 pub name: String,
38 pub room_name: String,
39 pub participant_metadata: String,
40 pub participant_identity: String,
41 pub participant_name: String,
42 pub audio: proto::IngressAudioOptions,
43 pub video: proto::IngressVideoOptions,
44 pub bypass_transcoding: Option<bool>,
45 pub enable_transcoding: Option<bool>,
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
49pub enum IngressListFilter {
50 All,
51 Room(String),
52 IngressId(String),
53}
54
55const SVC: &str = "Ingress";
56
57#[derive(Debug)]
58pub struct IngressClient {
59 base: ServiceBase,
60 client: TwirpClient,
61}
62
63impl IngressClient {
64 pub fn with_api_key(host: &str, api_key: &str, api_secret: &str) -> Self {
66 Self::build(
67 host,
68 ServiceBase::with_api_key(api_key, api_secret),
69 crate::http_client::Client::new(),
70 )
71 }
72
73 pub fn with_token(host: &str, token: &str) -> Self {
75 Self::build(host, ServiceBase::with_token(token), crate::http_client::Client::new())
76 }
77
78 pub(crate) fn build(host: &str, base: ServiceBase, client: crate::http_client::Client) -> Self {
81 Self { base, client: TwirpClient::with_client(host, LIVEKIT_PACKAGE, None, client) }
82 }
83
84 #[cfg(test)]
85 pub(crate) fn with_default_headers(mut self, headers: http::HeaderMap) -> Self {
86 self.client = self.client.with_default_headers(headers);
87 self
88 }
89
90 pub fn new(host: &str) -> ServiceResult<Self> {
93 let (api_key, api_secret) = get_env_keys()?;
94 Ok(Self::with_api_key(host, &api_key, &api_secret))
95 }
96
97 pub fn with_failover(mut self, enabled: bool) -> Self {
100 self.client = self.client.with_failover(enabled);
101 self
102 }
103
104 pub fn with_request_timeout(mut self, timeout: std::time::Duration) -> Self {
106 self.client = self.client.with_request_timeout(timeout);
107 self
108 }
109
110 pub async fn create_ingress(
111 &self,
112 input_type: proto::IngressInput,
113 options: CreateIngressOptions,
114 ) -> ServiceResult<proto::IngressInfo> {
115 self.client
116 .request(
117 SVC,
118 "CreateIngress",
119 proto::CreateIngressRequest {
120 input_type: input_type as i32,
121 name: options.name,
122 room_name: options.room_name,
123 participant_metadata: options.participant_metadata,
124 participant_identity: options.participant_identity,
125 participant_name: options.participant_name,
126 audio: Some(options.audio),
127 video: Some(options.video),
128 bypass_transcoding: options.bypass_transcoding,
129 enable_transcoding: options.enable_transcoding,
130 url: options.url,
131 enabled: Default::default(), },
133 self.base
134 .auth_header(VideoGrants { ingress_admin: true, ..Default::default() }, None)?,
135 )
136 .await
137 .map_err(Into::into)
138 }
139
140 pub async fn update_ingress(
141 &self,
142 ingress_id: &str,
143 options: UpdateIngressOptions,
144 ) -> ServiceResult<proto::IngressInfo> {
145 self.client
146 .request(
147 SVC,
148 "UpdateIngress",
149 proto::UpdateIngressRequest {
150 ingress_id: ingress_id.to_owned(),
151 name: options.name,
152 room_name: options.room_name,
153 participant_metadata: options.participant_metadata,
154 participant_identity: options.participant_identity,
155 participant_name: options.participant_name,
156 audio: Some(options.audio),
157 video: Some(options.video),
158 bypass_transcoding: options.bypass_transcoding,
159 enable_transcoding: options.enable_transcoding,
160 enabled: Default::default(), },
162 self.base
163 .auth_header(VideoGrants { ingress_admin: true, ..Default::default() }, None)?,
164 )
165 .await
166 .map_err(Into::into)
167 }
168
169 pub async fn list_ingress(
170 &self,
171 filter: IngressListFilter,
172 ) -> ServiceResult<Vec<proto::IngressInfo>> {
173 let resp: proto::ListIngressResponse = self
174 .client
175 .request(
176 SVC,
177 "ListIngress",
178 proto::ListIngressRequest {
179 ingress_id: match filter.clone() {
180 IngressListFilter::IngressId(id) => id,
181 _ => Default::default(),
182 },
183 room_name: match filter {
184 IngressListFilter::Room(room) => room,
185 _ => Default::default(),
186 },
187 page_token: Default::default(),
188 },
189 self.base
190 .auth_header(VideoGrants { ingress_admin: true, ..Default::default() }, None)?,
191 )
192 .await?;
193
194 Ok(resp.items)
195 }
196
197 pub async fn delete_ingress(&self, ingress_id: &str) -> ServiceResult<proto::IngressInfo> {
198 self.client
199 .request(
200 SVC,
201 "DeleteIngress",
202 proto::DeleteIngressRequest { ingress_id: ingress_id.to_owned() },
203 self.base
204 .auth_header(VideoGrants { ingress_admin: true, ..Default::default() }, None)?,
205 )
206 .await
207 .map_err(Into::into)
208 }
209}