Skip to main content

livekit_api/services/
ingress.rs

1// Copyright 2025 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;
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    /// Authenticates with an API key and secret, signing a short-lived token per request.
65    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    /// Authenticates with a pre-signed token, sent verbatim on every request.
74    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    /// Builds the client from an already-constructed HTTP client so the unified
79    /// [`LiveKitApi`](super::LiveKitApi) can share one connection pool across services.
80    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    /// Reads the API key and secret from the `LIVEKIT_API_KEY` and
91    /// `LIVEKIT_API_SECRET` environment variables.
92    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    /// Enables or disables region failover (enabled by default). Failover only
98    /// engages for LiveKit Cloud hosts.
99    pub fn with_failover(mut self, enabled: bool) -> Self {
100        self.client = self.client.with_failover(enabled);
101        self
102    }
103
104    /// Overrides the default per-request timeout (10s) for calls on this client.
105    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(), // TODO: support this attribute
132                },
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(), // TODO: support this attribute
161                },
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}