rust_wistia/api/upload/file_stream.rs
1use crate::api::client::UploadClient;
2use crate::constants::DEFAULT_FILENAME;
3use crate::https::tls;
4use crate::models::*;
5use crate::types::Result;
6use crate::utils::{stream_reader_from_url, stream_reader_from_url_and_arc_client};
7
8use std::io::{Cursor, Read};
9use std::sync::Arc;
10
11use hyper::client::HttpConnector;
12use hyper::{body::Bytes, Client, Request};
13use hyper_multipart::client::multipart::Form;
14use hyper_multipart_rfc7578 as hyper_multipart;
15use hyper_multipart_rfc7578::client::multipart::Body;
16
17/// Client implementation to upload *streams* (file-like objects) and
18/// *videos* via the Wistia **[Upload API]**.
19///
20/// Also check out the [`rust-wistia`] docs for usage and examples.
21///
22/// [`rust-wistia`]: https://docs.rs/rust-wistia
23/// [Upload API]: https://wistia.com/support/developers/upload-api
24///
25#[derive(Clone)]
26pub struct StreamUploader<'a, R: 'static + Read + Send + Sync, B = Body> {
27 client: UploadClient<B>,
28 req: UploadStreamRequest<'a>,
29 reader: Option<R>,
30}
31
32impl<'a> StreamUploader<'a, Cursor<Bytes>> {
33 /// Create a new `StreamUploader` which uses the *bytes* content downloaded
34 /// from a publicly accessible **url**.
35 ///
36 /// # Arguments
37 ///
38 /// * `url` - A public accessible url to the media which will be downloaded.
39 ///
40 /// # Examples
41 ///
42 /// ```
43 /// use rust_wistia::{StreamUploader, https::get_https_client};
44 ///
45 /// let mut uploader = StreamUploader::with_url("https://google.com/my/image").await?;
46 /// ```
47 pub async fn with_url(url: &str) -> Result<StreamUploader<'a, Cursor<Bytes>>> {
48 let stream = stream_reader_from_url(url, None).await?;
49 Self::new(stream)
50 }
51
52 /// Create a new `StreamUploader` which uses the *bytes* content downloaded
53 /// from a publicly accessible **url**.
54 ///
55 /// # Arguments
56 ///
57 /// * `url` - A public accessible url to the media which will be downloaded.
58 /// * `access_token` - An API access token used to make requests to the
59 /// Wistia API.
60 ///
61 /// # Examples
62 ///
63 /// ```
64 /// use rust_wistia::{StreamUploader, https::get_https_client};
65 ///
66 /// let mut uploader = StreamUploader::with_url_and_token(
67 /// "https://google.com/my/image",
68 /// "my-token"
69 /// )
70 /// .await?;
71 /// ```
72 pub async fn with_url_and_token(
73 url: &str,
74 access_token: &str,
75 ) -> Result<StreamUploader<'a, Cursor<Bytes>>> {
76 let stream = stream_reader_from_url(url, None).await?;
77 Ok(Self::with_token(access_token).stream(stream))
78 }
79
80 /// Create a new `StreamUploader` which uses the *bytes* content downloaded
81 /// from a publicly accessible **url**.
82 ///
83 /// # Arguments
84 ///
85 /// * `url` - A public accessible url to the media which will be downloaded.
86 /// * `client` - An optional HTTPS client to use for downloading the media.
87 ///
88 /// # Examples
89 ///
90 /// ```
91 /// use rust_wistia::{StreamUploader, https::get_https_client};
92 ///
93 /// let client = get_https_client();
94 /// let mut uploader = StreamUploader::with_url_and_client("https://google.com/my/image", client).await?;
95 /// ```
96 pub async fn with_url_and_client(
97 url: &str,
98 client: impl Into<Option<Client<tls::HttpsConnector<HttpConnector>>>>,
99 ) -> Result<StreamUploader<'a, Cursor<Bytes>>> {
100 let stream = stream_reader_from_url(url, client).await?;
101 Self::new(stream)
102 }
103
104 /// Create a new `StreamUploader` which uses the *bytes* content downloaded
105 /// from a publicly accessible **url**.
106 ///
107 /// # Arguments
108 ///
109 /// * `url` - A public accessible url to the media which will be downloaded.
110 /// * `client` - An optional HTTPS client to use for downloading the media.
111 ///
112 /// # Examples
113 ///
114 /// ```
115 /// use std::sync::Arc;
116 /// use rust_wistia::{StreamUploader, https::get_https_client};
117 ///
118 /// let client = Arc::new(get_https_client());
119 /// let mut uploader = StreamUploader::with_url_and_arc_client("https://google.com/my/image", client).await?;
120 /// ```
121 pub async fn with_url_and_arc_client(
122 url: &str,
123 client: Arc<Client<tls::HttpsConnector<HttpConnector>>>,
124 ) -> Result<StreamUploader<'a, Cursor<Bytes>>> {
125 let stream = stream_reader_from_url_and_arc_client(url, client).await?;
126 Self::new(stream)
127 }
128}
129
130impl<'a, R: 'static + Read + Send + Sync> StreamUploader<'a, R> {
131 /// Create a `StreamUploader` with a new HTTPS client, with the access token
132 /// retrieved from the environment.
133 ///
134 /// # Arguments
135 ///
136 /// * `stream` - A readable file-like *stream* object to upload.
137 ///
138 /// # Examples
139 ///
140 /// ```
141 /// use rust_wistia::StreamUploader;
142 /// use std::io::Cursor;
143 ///
144 /// let bytes = Cursor::new("Hello World!");
145 /// let uploader = StreamUploader::new(bytes)?;
146 ///
147 /// let res = uploader.name("My Video Name").send()?.await?;
148 /// ```
149 ///
150 pub fn new(stream: R) -> Result<Self> {
151 Self::with_stream_and_filename(stream, DEFAULT_FILENAME)
152 }
153
154 /// Create a `SteamUploader` with a new HTTPS client, with the access token
155 /// retrieved from the environment.
156 ///
157 /// # Arguments
158 ///
159 /// * `stream` - A readable file-like *stream* object to upload.
160 /// * `file_name` - The name of the media file.
161 ///
162 /// # Examples
163 ///
164 /// ```
165 /// use rust_wistia::StreamUploader;
166 /// use std::io::Cursor;
167 ///
168 /// let bytes = Cursor::new("Hello World!");
169 /// let uploader = StreamUploader::with_stream_and_filename(bytes, "my_file.mp4")?;
170 ///
171 /// let res = uploader.send()?.await?;
172 /// ```
173 ///
174 pub fn with_stream_and_filename(stream: R, file_name: &'a str) -> Result<Self> {
175 Ok(Self {
176 client: UploadClient::from_env()?,
177 req: UploadStreamRequest::new(file_name),
178 reader: Some(stream),
179 })
180 }
181
182 /// Create a `SteamUploader` with a new HTTPS client, with the access token
183 /// retrieved from the environment.
184 ///
185 /// # Arguments
186 ///
187 /// * `file_name` - The name of the media file.
188 ///
189 /// # Examples
190 ///
191 /// ```
192 /// use rust_wistia::StreamUploader;
193 /// use std::io::Cursor;
194 ///
195 /// let bytes = Cursor::new("Hello World!");
196 /// let uploader = StreamUploader::with_filename("my_file.mp4")?.stream(bytes);
197 ///
198 /// let res = uploader.send()?.await?;
199 /// ```
200 ///
201 pub fn with_filename(file_name: &'a str) -> Result<Self> {
202 Ok(Self {
203 client: UploadClient::from_env()?,
204 req: UploadStreamRequest::new(file_name),
205 reader: None,
206 })
207 }
208
209 /// Create a `SteamUploader` with a new HTTPS client and a Wistia access
210 /// token.
211 ///
212 /// # Arguments
213 ///
214 /// * `access_token` - An API access token used to make requests to the
215 /// Wistia API.
216 ///
217 /// # Examples
218 ///
219 /// ```
220 /// use rust_wistia::StreamUploader;
221 /// use std::io::Cursor;
222 ///
223 /// let bytes = Cursor::new("Hello World!");
224 /// let uploader = StreamUploader::with_token("my-token").stream(bytes);
225 ///
226 /// let res = uploader.send()?.await?;
227 /// ```
228 ///
229 pub fn with_token(access_token: &str) -> Self {
230 Self {
231 client: UploadClient::from_token(access_token),
232 req: UploadStreamRequest::new(DEFAULT_FILENAME),
233 reader: None,
234 }
235 }
236
237 /// Create a `SteamUploader` with a file path and an HTTPS client.
238 ///
239 /// # Arguments
240 ///
241 /// * `client` - The HTTPS client (UploadClient) to use for requests.
242 /// Note that the client must support multipart form requests, via a
243 /// `multipart::Body`.
244 ///
245 /// # Examples
246 ///
247 /// ```
248 /// use rust_wistia::{StreamUploader, UploadClient};
249 /// use std::io::Cursor;
250 ///
251 /// let client = UploadClient::from_env()?;
252 /// let bytes = Cursor::new("Hello World!");
253 /// let uploader = StreamUploader::with_client(client).stream(bytes);
254 ///
255 /// let res = uploader.send()?.await?;
256 /// ```
257 ///
258 pub fn with_client(client: UploadClient<Body>) -> Self {
259 Self {
260 client,
261 req: UploadStreamRequest::new(DEFAULT_FILENAME),
262 reader: None,
263 }
264 }
265
266 /// Sets the *reader stream* which will be used to upload to Wistia.
267 ///
268 /// # Examples
269 ///
270 /// ```
271 /// use std::io::Cursor;
272 ///
273 /// let bytes = Cursor::new("Hello World!");
274 /// let uploader = rust_wistia::StreamUploader::with_filename("my_file.mp4")?.stream(bytes);
275 /// ```
276 pub fn stream(mut self, stream: R) -> Self {
277 self.reader = Some(stream);
278 self
279 }
280
281 /// The hashed id of the project to upload media into. If omitted, a new
282 /// project will be created and uploaded to. The naming convention used
283 /// for such projects is `Uploads_YYYY-MM-DD`.
284 pub fn project_id(mut self, project_id: &'a str) -> Self {
285 self.req.project_id = Some(project_id);
286 self
287 }
288
289 /// A display name to use for the media in Wistia. If omitted, the filename
290 /// will be used instead. This field is limited to 255 characters.
291 pub fn name(mut self, name: &'a str) -> Self {
292 self.req.name = Some(name);
293 self
294 }
295
296 /// A description to use for the media in Wistia. You can use basic HTML
297 /// here, but note that both HTML and CSS will be sanitized.
298 pub fn description(mut self, description: &'a str) -> Self {
299 self.req.description = Some(description);
300 self
301 }
302
303 /// A Wistia contact id, an integer value. If omitted, it will default to
304 /// the contact_id of the account’s owner.
305 pub fn contact_id(mut self, contact_id: &'a str) -> Self {
306 self.req.contact_id = Some(contact_id);
307 self
308 }
309
310 /// Send the Upload File request (with the *multi-part form* data) to the
311 /// Wistia [Upload API].
312 ///
313 /// [Upload API]: https://wistia.com/support/developers/upload-api
314 ///
315 // noinspection DuplicatedCode
316 pub async fn send(self) -> Result<UploadResponse> {
317 // Build the query parameters to pass to the Upload API
318
319 let params = UploadRequest {
320 access_token: self.client.access_token.as_str(),
321 url: None,
322 project_id: self.req.project_id,
323 name: self.req.name,
324 description: None,
325 contact_id: self.req.contact_id,
326 };
327
328 let url = UploadClient::<Body>::build_url(params)?;
329
330 // Create a request instance and multipart form
331 let req_builder = Request::post(&url);
332 let mut form = Form::default();
333
334 // Add multi-part form fields
335
336 // TODO worth checking for unwrap()?
337 form.add_reader_file("file", self.reader.unwrap(), self.req.file_name);
338
339 if let Some(description) = self.req.description {
340 form.add_text("description", description);
341 }
342
343 // Update a request instance with the multipart Content-Type header
344 // and the payload data.
345 let form = form.set_body::<Body>(req_builder).unwrap();
346
347 // Send the request
348 self.client.make_request(&url, form).await
349 }
350}