tgbot/types/file/video/
mod.rs

1use std::{error::Error, fmt};
2
3use serde::{Deserialize, Serialize};
4
5use crate::{
6    api::{Form, Method, Payload},
7    types::{
8        ChatId,
9        InputFile,
10        Integer,
11        Message,
12        ParseMode,
13        PhotoSize,
14        ReplyMarkup,
15        ReplyMarkupError,
16        ReplyParameters,
17        ReplyParametersError,
18        TextEntities,
19        TextEntity,
20        TextEntityError,
21    },
22};
23
24#[cfg(test)]
25mod tests;
26
27/// Represents a video file.
28#[serde_with::skip_serializing_none]
29#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
30pub struct Video {
31    /// Duration in seconds as defined by sender.
32    pub duration: Integer,
33    /// Identifier of the file.
34    ///
35    /// Can be used to download or reuse the file.
36    pub file_id: String,
37    /// Unique identifier of the file.
38    ///
39    /// It is supposed to be the same over time and for different bots.
40    /// Can't be used to download or reuse the file.
41    pub file_unique_id: String,
42    /// Height as defined by sender.
43    pub height: Integer,
44    /// Width as defined by sender.
45    pub width: Integer,
46    /// Available sizes of the cover of the video in the message.
47    pub cover: Option<Vec<PhotoSize>>,
48    /// Original filename as defined by sender.
49    pub file_name: Option<String>,
50    /// File size in bytes.
51    pub file_size: Option<Integer>,
52    /// MIME type as defined by sender.
53    pub mime_type: Option<String>,
54    /// Timestamp in seconds from which the video will play in the message.
55    pub start_timestamp: Option<Integer>,
56    /// Thumbnail.
57    pub thumbnail: Option<PhotoSize>,
58}
59
60impl Video {
61    /// Creates a new `Video`.
62    ///
63    /// # Arguments
64    ///
65    /// * `duration` - Duration of the video in seconds.
66    /// * `file_id` - Identifier of the file.
67    /// * `file_unique_id` - Unique identifier of the file.
68    /// * `height` - Height of the video.
69    /// * `width` - Width of the video.
70    pub fn new<A, B>(duration: Integer, file_id: A, file_unique_id: B, height: Integer, width: Integer) -> Self
71    where
72        A: Into<String>,
73        B: Into<String>,
74    {
75        Self {
76            duration,
77            file_id: file_id.into(),
78            file_unique_id: file_unique_id.into(),
79            height,
80            width,
81            cover: None,
82            file_name: None,
83            file_size: None,
84            mime_type: None,
85            start_timestamp: None,
86            thumbnail: None,
87        }
88    }
89
90    /// Sets a new cover.
91    ///
92    /// # Arguments
93    ///
94    /// * `value` - Available sizes of the cover of the video in the message.
95    pub fn with_cover<T>(mut self, value: T) -> Self
96    where
97        T: IntoIterator<Item = PhotoSize>,
98    {
99        self.cover = Some(value.into_iter().collect());
100        self
101    }
102
103    /// Sets a new name of the file.
104    ///
105    /// # Arguments
106    ///
107    /// * `value` - The name of the file.
108    pub fn with_file_name<T>(mut self, value: T) -> Self
109    where
110        T: Into<String>,
111    {
112        self.file_name = Some(value.into());
113        self
114    }
115
116    /// Sets a new size of the file.
117    ///
118    /// # Arguments
119    ///
120    /// * `value` - The size of the file in bytes.
121    pub fn with_file_size(mut self, value: Integer) -> Self {
122        self.file_size = Some(value);
123        self
124    }
125
126    /// Sets a new MIME type.
127    ///
128    /// # Arguments
129    ///
130    /// * `value` - MIME type.
131    pub fn with_mime_type<T>(mut self, value: T) -> Self
132    where
133        T: Into<String>,
134    {
135        self.mime_type = Some(value.into());
136        self
137    }
138
139    /// Sets a new start timestamp.
140    ///
141    /// # Arguments
142    ///
143    /// * `value` - Timestamp in seconds from which the video will play in the message.
144    pub fn with_start_timestamp(mut self, value: Integer) -> Self {
145        self.start_timestamp = Some(value);
146        self
147    }
148
149    /// Sets a new thumbnail.
150    ///
151    /// # Arguments
152    ///
153    /// * `value` - Thumbnail.
154    pub fn with_thumbnail(mut self, value: PhotoSize) -> Self {
155        self.thumbnail = Some(value);
156        self
157    }
158}
159
160/// Sends a video file.
161///
162/// Telegram clients support mp4 videos (other formats may be sent as Document).
163/// Bots can currently send video files of up to 50 MB in size,
164/// this limit may be changed in the future.
165#[derive(Debug)]
166pub struct SendVideo {
167    form: Form,
168}
169
170impl SendVideo {
171    /// Creates a new `SendVideo`.
172    ///
173    /// # Arguments
174    ///
175    /// * `chat_id` - Unique identifier of the target chat.
176    /// * `video` - Video to send.
177    pub fn new<A, B>(chat_id: A, video: B) -> Self
178    where
179        A: Into<ChatId>,
180        B: Into<InputFile>,
181    {
182        Self {
183            form: Form::from([("chat_id", chat_id.into().into()), ("video", video.into().into())]),
184        }
185    }
186
187    /// Sets a new value for the `allow_paid_broadcast` flag.
188    ///
189    /// # Arguments
190    ///
191    /// * `value` - Whether to allow up to 1000 messages per second, ignoring broadcasting limits
192    ///   for a fee of 0.1 Telegram Stars per message.
193    ///   The relevant Stars will be withdrawn from the bot's balance.
194    pub fn with_allow_paid_broadcast(mut self, value: bool) -> Self {
195        self.form.insert_field("allow_paid_broadcast", value);
196        self
197    }
198
199    /// Sets a new business connection ID.
200    ///
201    /// # Arguments
202    ///
203    /// * `value` - Unique identifier of the business connection.
204    pub fn with_business_connection_id<T>(mut self, value: T) -> Self
205    where
206        T: Into<String>,
207    {
208        self.form.insert_field("business_connection_id", value.into());
209        self
210    }
211
212    /// Sets a new caption.
213    ///
214    /// # Arguments
215    ///
216    /// * `value` - Caption; 0-1024 characters.
217    ///
218    /// May also be used when resending documents by `file_id`.
219    pub fn with_caption<T>(mut self, value: T) -> Self
220    where
221        T: Into<String>,
222    {
223        self.form.insert_field("caption", value.into());
224        self
225    }
226
227    /// Sets a new list of caption entities.
228    ///
229    /// # Arguments
230    ///
231    /// * `value` - The list of special entities that appear in the caption.
232    ///
233    /// Caption parse mode will be set to [`None`] when this method is called.
234    pub fn with_caption_entities<T>(mut self, value: T) -> Result<Self, TextEntityError>
235    where
236        T: IntoIterator<Item = TextEntity>,
237    {
238        let value: TextEntities = value.into_iter().collect();
239        self.form.insert_field("caption_entities", value.serialize()?);
240        self.form.remove_field("parse_mode");
241        Ok(self)
242    }
243
244    /// Sets a new caption parse mode.
245    ///
246    /// # Arguments
247    ///
248    /// * `value` - Parse mode.
249    ///
250    /// Caption entities will be set to [`None`] when this method is called.
251    pub fn with_caption_parse_mode(mut self, value: ParseMode) -> Self {
252        self.form.insert_field("parse_mode", value);
253        self.form.remove_field("caption_entities");
254        self
255    }
256
257    /// Sets a new cover.
258    ///
259    /// # Arguments
260    ///
261    /// * `value` - Cover for the video in the message.
262    pub fn with_cover<T>(mut self, value: T) -> Self
263    where
264        T: Into<InputFile>,
265    {
266        let value = value.into();
267        self.form.insert_field("cover", value);
268        self
269    }
270
271    /// Sets a new value for the `disable_notification` flag.
272    ///
273    /// # Arguments
274    ///
275    /// * `value` - Indicates whether to send the message silently or not;
276    ///   a user will receive a notification without sound.
277    pub fn with_disable_notification(mut self, value: bool) -> Self {
278        self.form.insert_field("disable_notification", value);
279        self
280    }
281
282    /// Sets a new duration.
283    ///
284    /// # Arguments
285    ///
286    /// * `value` - Duration in seconds.
287    pub fn with_duration(mut self, value: Integer) -> Self {
288        self.form.insert_field("duration", value);
289        self
290    }
291
292    /// Sets a new value for the `has_spoiler` flag.
293    ///
294    /// # Arguments
295    ///
296    /// * `value` - Indicates whether to cover with a spoiler animation.
297    pub fn with_has_spoiler(mut self, value: bool) -> Self {
298        self.form.insert_field("has_spoiler", value);
299        self
300    }
301
302    /// Sets a new height.
303    ///
304    /// # Arguments
305    ///
306    /// * `value` - Height.
307    pub fn with_height(mut self, value: Integer) -> Self {
308        self.form.insert_field("height", value);
309        self
310    }
311
312    /// Sets a new message effect ID.
313    ///
314    /// # Arguments
315    ///
316    /// * `value` - Unique identifier of the message effect to be added to the message; for private chats only.
317    pub fn with_message_effect_id<T>(mut self, value: T) -> Self
318    where
319        T: Into<String>,
320    {
321        self.form.insert_field("message_effect_id", value.into());
322        self
323    }
324
325    /// Sets a new message thread ID.
326    ///
327    /// # Arguments
328    ///
329    /// * `value` - Unique identifier of the target message thread;
330    ///   supergroups only.
331    pub fn with_message_thread_id(mut self, value: Integer) -> Self {
332        self.form.insert_field("message_thread_id", value);
333        self
334    }
335
336    /// Sets a new value for the `protect_content` flag.
337    ///
338    /// # Arguments
339    ///
340    /// * `value` - Indicates whether to protect the contents
341    ///   of the sent message from forwarding and saving.
342    pub fn with_protect_content(mut self, value: bool) -> Self {
343        self.form.insert_field("protect_content", value.to_string());
344        self
345    }
346
347    /// Sets a new reply markup.
348    ///
349    /// # Arguments
350    ///
351    /// * `value` - Reply markup.
352    pub fn with_reply_markup<T>(mut self, value: T) -> Result<Self, ReplyMarkupError>
353    where
354        T: Into<ReplyMarkup>,
355    {
356        let value = value.into();
357        self.form.insert_field("reply_markup", value.serialize()?);
358        Ok(self)
359    }
360
361    /// Sets new reply parameters.
362    ///
363    /// # Arguments
364    ///
365    /// * `value` - Description of the message to reply to.
366    pub fn with_reply_parameters(mut self, value: ReplyParameters) -> Result<Self, ReplyParametersError> {
367        self.form.insert_field("reply_parameters", value.serialize()?);
368        Ok(self)
369    }
370
371    /// Sets a new value for the `show_caption_above_media` flag.
372    ///
373    /// # Arguments
374    ///
375    /// `value` - Whether the caption must be shown above the message media.
376    pub fn with_show_caption_above_media(mut self, value: bool) -> Self {
377        self.form.insert_field("show_caption_above_media", value);
378        self
379    }
380
381    /// Sets a new start timestamp.
382    ///
383    /// # Arguments
384    ///
385    /// * `value` - Start timestamp for the video in the message.
386    pub fn with_start_timestamp(mut self, value: Integer) -> Self {
387        self.form.insert_field("start_timestamp", value);
388        self
389    }
390
391    /// Sets a new value for the `supports_streaming` flag.
392    ///
393    /// # Arguments
394    ///
395    /// * `value` - Indicates whether the uploaded video is suitable for streaming.
396    pub fn with_supports_streaming(mut self, value: bool) -> Self {
397        self.form.insert_field("supports_streaming", value);
398        self
399    }
400
401    /// Sets a new thumbnail.
402    ///
403    /// # Arguments
404    ///
405    /// * `value` - Thumbnail.
406    ///
407    /// The thumbnail should be in JPEG format and less than 200 kB in size.
408    /// A thumbnail‘s width and height should not exceed 320.
409    /// Ignored if the file is not uploaded using `multipart/form-data`.
410    /// Thumbnails can’t be reused and can be only uploaded as a new file.
411    pub fn with_thumbnail<T>(mut self, value: T) -> Result<Self, SendVideoError>
412    where
413        T: Into<InputFile>,
414    {
415        let value = value.into();
416        if matches!(value, InputFile::Id(_)) {
417            return Err(SendVideoError::InvalidThumbnail);
418        }
419        self.form.insert_field("thumbnail", value);
420        Ok(self)
421    }
422
423    /// Sets a new width.
424    ///
425    /// # Arguments
426    ///
427    /// * `value` - Width.
428    pub fn with_width(mut self, value: Integer) -> Self {
429        self.form.insert_field("width", value);
430        self
431    }
432}
433
434impl Method for SendVideo {
435    type Response = Message;
436
437    fn into_payload(self) -> Payload {
438        Payload::form("sendVideo", self.form)
439    }
440}
441
442/// Represents an error when sending a video.
443#[derive(Debug)]
444pub enum SendVideoError {
445    /// Thumbnails can not be reused.
446    InvalidThumbnail,
447}
448
449impl fmt::Display for SendVideoError {
450    fn fmt(&self, out: &mut fmt::Formatter<'_>) -> fmt::Result {
451        match self {
452            Self::InvalidThumbnail => write!(out, "thumbnails can’t be reused and can be only uploaded as a new file"),
453        }
454    }
455}
456
457impl Error for SendVideoError {}