Skip to main content

ac_rustube/
video.rs

1use std::sync::Arc;
2
3use derive_more::Display;
4
5use crate::{Id, Stream, VideoInfo};
6use crate::video_info::player_response::video_details::VideoDetails;
7
8/// A YouTube downloader, which allows you to download all available formats and qualities of a 
9/// YouTube video. 
10/// 
11/// Each instance of [`Video`] represents an existing, available, and downloadable 
12/// video.
13///
14/// There are two ways of constructing an instance of [`Video`]:
15/// 1. By using the asynchronous `Video::from_*` methods. These methods will take some kind of 
16/// video-identifier, like an [`Url`] or an [`Id`], will then internally download the necessary video 
17/// information and finally descramble it.
18/// 2. By calling [`VideoDescrambler::descramble`]. Since a [`VideoDescrambler`] already 
19/// contains the necessary video information, and just need to descramble it, no requests are
20/// performed. (This gives you more control over the process).
21/// 
22/// # Examples
23/// - Constructing using [`Video::from_url`] (or [`Video::from_id`]) (easiest way)
24/// ```no_run
25///# use rustube::Video;
26///# use url::Url;
27///# #[tokio::main]
28///# async fn main() -> Result<(), Box<dyn std::error::Error>> {
29/// let url = Url::parse("https://youtube.com/watch?iv=5jlI4uzZGjU")?;
30/// let video: Video = Video::from_url(&url).await?;
31///# Ok(())
32///# }
33/// ``` 
34/// - Constructing using [`VideoDescrambler::descramble`]
35/// ```no_run
36///# use rustube::{Video, VideoFetcher, VideoDescrambler};
37///# use url::Url;
38///# #[tokio::main]
39///# async fn main() -> Result<(), Box<dyn std::error::Error>> {
40/// let url = Url::parse("https://youtube.com/watch?iv=5jlI4uzZGjU")?;
41/// let fetcher: VideoFetcher = VideoFetcher::from_url(&url)?;
42/// let descrambler: VideoDescrambler = fetcher.fetch().await?;  
43/// let video: Video = descrambler.descramble()?;
44///# Ok(())
45///# }
46/// ``` 
47/// - Construction using chained calls
48/// ```no_run
49///# use rustube::{Video, VideoFetcher, VideoDescrambler};
50///# use url::Url;
51///# #[tokio::main]
52///# async fn main() -> Result<(), Box<dyn std::error::Error>> {
53/// let url = Url::parse("https://youtube.com/watch?iv=5jlI4uzZGjU")?;
54/// let video: Video = VideoFetcher::from_url(&url)?
55///    .fetch()
56///    .await?
57///    .descramble()?;
58///# Ok(())
59///# }
60/// ``` 
61/// - Downloading a video using an existing [`Video`] instance
62/// ```no_run
63///# use rustube::{Video, VideoFetcher, VideoDescrambler};
64///# use url::Url;
65///# #[tokio::main]
66///# async fn main() -> Result<(), Box<dyn std::error::Error>> {
67///# let url = Url::parse("https://youtube.com/watch?iv=5jlI4uzZGjU")?; 
68///# let video: Video = Video::from_url(&url).await?;
69/// let video_path = video
70///    .streams()
71///    .iter()
72///    .filter(|stream| stream.includes_video_track && stream.includes_audio_track)
73///    .max_by_key(|stream| stream.quality_label)
74///    .unwrap()
75///    .download()
76///    .await?;
77///# Ok(())
78///# }
79/// ``` 
80/// [`Url`]: url::Url
81/// [`VideoDescrambler`]: crate::descrambler::VideoDescrambler
82/// [`VideoDescrambler::descramble`]: crate::descrambler::VideoDescrambler::descramble
83#[derive(Clone, Debug, Display, PartialEq)]
84#[display(fmt =
85"Video({}, streams: {})",
86"video_info.player_response.video_details.video_id", "streams.len()"
87)]
88pub struct Video {
89    pub(crate) video_info: VideoInfo,
90    pub(crate) streams: Vec<Stream>,
91}
92
93impl Video {
94    /// Creates a [`Video`] from an [`Url`](url::Url).
95    /// ### Errors
96    /// - When [`VideoFetcher::from_url`](crate::VideoFetcher::from_url) fails.
97    /// - When [`VideoFetcher::fetch`](crate::VideoFetcher::fetch) fails.
98    /// - When [`VideoDescrambler::descramble`](crate::VideoDescrambler::descramble) fails.
99    #[inline]
100    #[cfg(all(feature = "download", feature = "regex"))]
101    pub async fn from_url(url: &url::Url) -> crate::Result<Self> {
102        crate::VideoFetcher::from_url(url)?
103            .fetch()
104            .await?
105            .descramble()
106    }
107
108    /// Creates a [`Video`] from an [`Id`].
109    /// ### Errors
110    /// - When [`VideoFetcher::fetch`](crate::VideoFetcher::fetch) fails.
111    /// - When [`VideoDescrambler::descramble`](crate::VideoDescrambler::descramble) fails.
112    #[inline]
113    #[cfg(feature = "download")]
114    pub async fn from_id(id: crate::IdBuf) -> crate::Result<Self> {
115        crate::VideoFetcher::from_id(id)?
116            .fetch()
117            .await?
118            .descramble()
119    }
120
121    /// The [`VideoInfo`] of the video.
122    #[inline]
123    pub fn video_info(&self) -> &VideoInfo {
124        &self.video_info
125    }
126
127    /// All [`Stream`]s of the video.
128    #[inline]
129    pub fn streams(&self) -> &Vec<Stream> {
130        &self.streams
131    }
132
133    /// Takes all [`Stream`]s of the video.
134    #[inline]
135    pub fn into_streams(self) -> Vec<Stream> {
136        self.streams
137    }
138
139    /// Decomposes a `Video` into it's raw parts.
140    #[inline]
141    pub fn into_parts(self) -> (VideoInfo, Vec<Stream>) {
142        (self.video_info, self.streams)
143    }
144
145    /// The [`VideoDetails`]s of the video.
146    #[inline]
147    pub fn video_details(&self) -> Arc<VideoDetails> {
148        Arc::clone(&self.video_info.player_response.video_details)
149    }
150
151    /// The [`Id`] of the video.
152    #[inline]
153    pub fn id(&self) -> Id<'_> {
154        self.video_info.player_response.video_details.video_id.as_borrowed()
155    }
156
157    /// The title of the video.
158    #[inline]
159    pub fn title(&self) -> &str {
160        self.video_info.player_response.video_details.title.as_str()
161    }
162
163    /// Whether or not the video is age restricted.
164    #[inline]
165    pub fn is_age_restricted(&self) -> bool {
166        self.video_info.is_age_restricted
167    }
168
169    /// The [`Stream`] with the best quality.
170    /// This stream is guaranteed to contain both a video as well as an audio track. 
171    #[inline]
172    pub fn best_quality(&self) -> Option<&Stream> {
173        self
174            .streams
175            .iter()
176            .filter(|stream| stream.includes_video_track && stream.includes_audio_track)
177            .max_by_key(|stream| stream.quality_label)
178    }
179
180    /// The [`Stream`] with the worst quality.
181    /// This stream is guaranteed to contain both a video as well as an audio track.
182    #[inline]
183    pub fn worst_quality(&self) -> Option<&Stream> {
184        self
185            .streams
186            .iter()
187            .filter(|stream| stream.includes_video_track && stream.includes_audio_track)
188            .min_by_key(|stream| stream.quality_label)
189    }
190
191    /// The [`Stream`] with the best video quality.
192    /// This stream is guaranteed to contain only a video but no audio track.
193    #[inline]
194    pub fn best_video(&self) -> Option<&Stream> {
195        self
196            .streams
197            .iter()
198            .filter(|stream| stream.includes_video_track && !stream.includes_audio_track)
199            .max_by_key(|stream| stream.width)
200    }
201
202    /// The [`Stream`] with the worst video quality.
203    /// This stream is guaranteed to contain only a video but no audio track.
204    #[inline]
205    pub fn worst_video(&self) -> Option<&Stream> {
206        self
207            .streams
208            .iter()
209            .filter(|stream| stream.includes_video_track && !stream.includes_audio_track)
210            .min_by_key(|stream| stream.width)
211    }
212
213    /// The [`Stream`] with the best audio quality.
214    /// This stream is guaranteed to contain only a audio but no video track.    
215    #[inline]
216    pub fn best_audio(&self) -> Option<&Stream> {
217        self
218            .streams
219            .iter()
220            .filter(|stream| stream.includes_audio_track && !stream.includes_video_track)
221            .max_by_key(|stream| stream.bitrate)
222    }
223
224    /// The [`Stream`] with the worst audio quality.
225    /// This stream is guaranteed to contain only a audio but no video track.
226    #[inline]
227    pub fn worst_audio(&self) -> Option<&Stream> {
228        self
229            .streams
230            .iter()
231            .filter(|stream| stream.includes_audio_track && !stream.includes_video_track)
232            .min_by_key(|stream| stream.bitrate)
233    }
234}