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}