videosdk/resources/
resource_pool.rs1use std::sync::Arc;
4
5use futures_util::Stream;
6use reqwest::Method;
7use serde::{Deserialize, Serialize};
8use serde_json::{Map, Value};
9
10use crate::client::{CallOptions, Client};
11use crate::common::string_enum;
12use crate::error::Result;
13use crate::pagination::{auto_page, paginate, ListParams, Page, PageFetcher};
14use crate::query::QueryBuilder;
15use crate::resources::escape;
16
17const PATH: &str = "/v2/resource";
18
19string_enum! {
20 ResourceStatus {
22 PENDING => "pending",
24 IDLE => "idle",
26 COMPOSING => "composing",
28 RELEASED => "released",
30 }
31}
32
33string_enum! {
34 ComposerType {
36 RECORDING => "recording",
38 HLS => "hls",
40 RTMP => "rtmp",
42 }
43}
44
45string_enum! {
46 ResourceMode {
48 VIDEO_AND_AUDIO => "video-and-audio",
50 AUDIO => "audio",
52 }
53}
54
55string_enum! {
56 ResourceQuality {
58 LOW => "low",
60 MED => "med",
62 HIGH => "high",
64 }
65}
66
67#[derive(Debug, Clone, Deserialize)]
69#[serde(rename_all = "camelCase")]
70pub struct ResourceUnit {
71 pub id: String,
73 pub status: Option<ResourceStatus>,
75 #[serde(rename = "type")]
77 pub kind: Option<String>,
78 pub mode: Option<ResourceMode>,
80 pub quality: Option<ResourceQuality>,
82 #[serde(default, deserialize_with = "crate::common::null_to_default")]
84 pub composer_ids: Vec<String>,
85 pub webhook_url: Option<String>,
87 #[serde(flatten)]
89 pub extra: Map<String, Value>,
90}
91
92#[derive(Debug, Clone, Default)]
94pub struct ListResourcesParams {
95 pub page: Option<u32>,
97 pub per_page: Option<u32>,
99 pub cursor: Option<String>,
101 pub status: Option<ResourceStatus>,
103}
104
105impl ListResourcesParams {
106 fn pagination(&self) -> ListParams {
107 ListParams {
108 page: self.page,
109 per_page: self.per_page,
110 cursor: self.cursor.clone(),
111 }
112 }
113}
114
115#[derive(Debug, Clone, Default, Serialize)]
117#[serde(rename_all = "camelCase")]
118pub struct AcquireResourceParams {
119 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
121 pub kind: Option<ComposerType>,
122 pub webhook_url: String,
124 #[serde(skip_serializing_if = "Option::is_none")]
126 pub units: Option<u32>,
127 #[serde(skip_serializing_if = "Option::is_none")]
129 pub mode: Option<ResourceMode>,
130 #[serde(skip_serializing_if = "Option::is_none")]
132 pub quality: Option<ResourceQuality>,
133}
134
135#[derive(Debug, Clone, Deserialize)]
137pub struct ReleaseResourceResult {
138 pub id: String,
140 pub success: bool,
142 pub msg: Option<String>,
144}
145
146#[derive(Debug, Clone, Copy)]
148pub struct ResourcePoolResource<'a> {
149 client: &'a Client,
150}
151
152impl<'a> ResourcePoolResource<'a> {
153 pub(crate) fn new(client: &'a Client) -> Self {
154 Self { client }
155 }
156
157 pub async fn list(&self, params: ListResourcesParams) -> Result<Page<ResourceUnit>> {
159 paginate(self.fetcher(¶ms), ¶ms.pagination(), "data", None).await
160 }
161
162 pub fn list_stream(
164 &self,
165 params: ListResourcesParams,
166 ) -> impl Stream<Item = Result<ResourceUnit>> + Send {
167 auto_page(self.fetcher(¶ms), params.pagination(), "data", None)
168 }
169
170 pub async fn get(&self, id: &str) -> Result<ResourceUnit> {
172 let path = format!("{PATH}/{}", escape(id));
173 self.client
174 .data(Method::GET, &path, CallOptions::new())
175 .await
176 }
177
178 pub async fn acquire(&self, params: AcquireResourceParams) -> Result<Vec<ResourceUnit>> {
180 let path = format!("{PATH}/acquire");
181 self.client
182 .data(Method::POST, &path, CallOptions::json(¶ms)?)
183 .await
184 }
185
186 pub async fn release(&self, ids: &[String]) -> Result<Vec<ReleaseResourceResult>> {
188 let body = serde_json::json!({ "ids": ids });
189 let path = format!("{PATH}/release");
190 self.client
191 .data(Method::POST, &path, CallOptions::json(&body)?)
192 .await
193 }
194
195 fn fetcher(&self, params: &ListResourcesParams) -> PageFetcher {
196 let client = self.client.clone();
197 let status = params.status.clone();
198 Arc::new(move |page, per_page| {
199 let client = client.clone();
200 let status = status.clone();
201 Box::pin(async move {
202 let query = QueryBuilder::new()
203 .opt("page", page)
204 .opt("perPage", per_page)
205 .opt_str("status", status.as_ref().map(ResourceStatus::as_str))
206 .into_pairs();
207 client
208 .json::<Value>(Method::GET, PATH, CallOptions::new().query(query))
209 .await
210 })
211 })
212 }
213}