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}