1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
use anyhow::Result; use lazy_regex::{lazy_regex, Lazy, Regex}; use serde::{Deserialize, Serialize}; use std::convert::TryFrom; mod utils; pub mod yt_initial_data; pub mod yt_initial_player_response; pub mod ytcfg; use utils::match_regex_and_parse; pub use yt_initial_data::YtInitialData; pub use yt_initial_player_response::YtInitialPlayerResponse; pub use ytcfg::Ytcfg; static YTCFG_REGEX: Lazy<Regex> = lazy_regex!(r"ytInitialData\s*=\s*(\{.+?\});"); static YT_INITIAL_DATA_REGEX: Lazy<Regex> = lazy_regex!(r"ytInitialData\s*=\s*(\{.+?\});"); static YT_INITIAL_PLAYER_RESPONSE_REGEX: Lazy<Regex> = lazy_regex!(r"ytInitialPlayerResponse\s*=\s*(\{.+?\});"); #[derive(Debug, Deserialize, Serialize)] pub struct YoutubeInfo { pub yt_initial_data: Option<YtInitialData>, pub yt_initial_player_response: Option<YtInitialPlayerResponse>, pub ytcfg: Option<Ytcfg>, } #[derive(Debug, Deserialize, Serialize)] pub struct YoutubeInfoRaw { pub yt_initial_data: Option<serde_json::Value>, pub yt_initial_player_response: Option<serde_json::Value>, pub ytcfg: Option<serde_json::Value>, } impl TryFrom<YoutubeInfoRaw> for YoutubeInfo { type Error = anyhow::Error; fn try_from(value: YoutubeInfoRaw) -> Result<Self, Self::Error> { let yt_initial_data: Option<YtInitialData> = value .yt_initial_data .map(|s| serde_json::from_value(s)) .transpose()?; let yt_initial_player_response: Option<YtInitialPlayerResponse> = value .yt_initial_player_response .map(|s| serde_json::from_value(s)) .transpose()?; let ytcfg: Option<Ytcfg> = value.ytcfg.map(|s| serde_json::from_value(s)).transpose()?; Ok(YoutubeInfo { yt_initial_data, yt_initial_player_response, ytcfg, }) } } pub fn parse_info_raw(page: &str) -> Result<YoutubeInfoRaw> { Ok(YoutubeInfoRaw { yt_initial_data: match_regex_and_parse(&page, &YT_INITIAL_DATA_REGEX, 1)?, yt_initial_player_response: match_regex_and_parse( &page, &YT_INITIAL_PLAYER_RESPONSE_REGEX, 1, )?, ytcfg: match_regex_and_parse(&page, &YTCFG_REGEX, 1)?, }) } pub fn parse_info(page: &str) -> Result<YoutubeInfo> { Ok(YoutubeInfo { yt_initial_data: match_regex_and_parse(&page, &YT_INITIAL_DATA_REGEX, 1)?, yt_initial_player_response: match_regex_and_parse( &page, &YT_INITIAL_PLAYER_RESPONSE_REGEX, 1, )?, ytcfg: match_regex_and_parse(&page, &YTCFG_REGEX, 1)?, }) }