ytextract/
stream.rs

1//! Streams of a YouTube video
2//!
3//! # Example
4//!
5//! ```rust
6//! # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
7//! let client = ytextract::Client::new();
8//!
9//! let streams = client.streams("nI2e-J6fsuk".parse()?).await?;
10//!
11//! for stream in streams {
12//!     println!("Duration: {:?}", stream.duration())
13//! }
14//!
15//! # Ok(())
16//! # }
17//! ```
18
19mod audio;
20mod common;
21mod video;
22
23pub use self::audio::Stream as Audio;
24pub use self::common::Stream as Common;
25pub use self::video::Stream as Video;
26use crate::{youtube::player_response::FormatType, Client};
27
28pub(crate) async fn get(
29    client: Client,
30    id: crate::video::Id,
31) -> crate::Result<impl Iterator<Item = Stream>> {
32    let player_response = client.api.streams(id).await?;
33
34    // TODO: DashManifest/HlsManifest
35    Ok(player_response
36        .streaming_data
37        .adaptive_formats
38        .into_iter()
39        .map(move |stream| Stream::new(stream, client.clone())))
40}
41
42/// A Stream of a YouTube video
43#[derive(Clone)]
44pub enum Stream {
45    /// A Stream exclusively containing [`Audio`] data
46    Audio(Audio),
47    /// A Stream exclusively containing [`Video`] data
48    Video(Video),
49}
50
51impl Stream {
52    pub(crate) fn new(format: crate::youtube::player_response::Format, client: Client) -> Self {
53        match format.ty {
54            FormatType::Audio(audio) => Self::Audio(Audio {
55                common: Common {
56                    format: format.base,
57                    client,
58                },
59                audio,
60            }),
61            FormatType::Video(video) => Self::Video(Video {
62                common: Common {
63                    format: format.base,
64                    client,
65                },
66                video,
67            }),
68        }
69    }
70
71    /// Returns `true` if the stream is [`Self::Audio`].
72    pub fn is_audio(&self) -> bool {
73        matches!(self, Self::Audio(..))
74    }
75
76    /// Returns `true` if the stream is [`Self::Video`].
77    pub fn is_video(&self) -> bool {
78        matches!(self, Self::Video(..))
79    }
80}
81
82impl std::ops::Deref for Stream {
83    type Target = Common;
84
85    fn deref(&self) -> &Self::Target {
86        match self {
87            Stream::Audio(audio) => &audio.common,
88            Stream::Video(video) => &video.common,
89        }
90    }
91}
92
93impl std::fmt::Debug for Stream {
94    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95        let mut debug = f.debug_struct("Stream");
96
97        match self {
98            Stream::Audio(audio) => {
99                audio.common.debug(&mut debug);
100                audio.debug(&mut debug);
101            }
102            Stream::Video(video) => {
103                video.common.debug(&mut debug);
104                video.debug(&mut debug);
105            }
106        }
107        debug.finish()?;
108
109        Ok(())
110    }
111}