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