Skip to main content

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