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