use reqwest::Client;
use crate::errors::{CouldNotRetrieveTranscript, CouldNotRetrieveTranscriptReason};
use crate::js_var_parser::JsVarParser;
use crate::models::VideoDetails;
use crate::playability_asserter::PlayabilityAsserter;
use crate::proxies::ProxyConfig;
use crate::transcript_list::TranscriptList;
use crate::video_details_extractor::VideoDetailsExtractor;
use crate::youtube_page_fetcher::YoutubePageFetcher;
pub struct VideoDataFetcher {
client: Client,
page_fetcher: YoutubePageFetcher,
}
impl VideoDataFetcher {
pub fn new(client: Client, proxy_config: Option<Box<dyn ProxyConfig + Send + Sync>>) -> Self {
let page_fetcher = YoutubePageFetcher::new(client.clone(), proxy_config);
Self {
client,
page_fetcher,
}
}
pub async fn fetch_transcript_list(
&self,
video_id: &str,
) -> Result<TranscriptList, CouldNotRetrieveTranscript> {
let video_captions = self.fetch_video_captions(video_id).await?;
TranscriptList::build(self.client.clone(), video_id.to_string(), &video_captions)
}
pub async fn fetch_video_captions(
&self,
video_id: &str,
) -> Result<serde_json::Value, CouldNotRetrieveTranscript> {
let html = self.page_fetcher.fetch_video_page(video_id).await?;
self.extract_captions_json(&html, video_id)
}
pub async fn fetch_video_details(
&self,
video_id: &str,
) -> Result<VideoDetails, CouldNotRetrieveTranscript> {
let html = self.page_fetcher.fetch_video_page(video_id).await?;
let player_response = self.extract_yt_initial_player_response(&html, video_id)?;
VideoDetailsExtractor::extract_video_details(&player_response, video_id)
}
fn extract_yt_initial_player_response(
&self,
html: &str,
video_id: &str,
) -> Result<serde_json::Value, CouldNotRetrieveTranscript> {
let js_var_parser = JsVarParser::new("ytInitialPlayerResponse");
let player_response = js_var_parser.parse(html, video_id)?;
Ok(player_response)
}
fn extract_captions_json(
&self,
html: &str,
video_id: &str,
) -> Result<serde_json::Value, CouldNotRetrieveTranscript> {
let player_response = self.extract_yt_initial_player_response(html, video_id)?;
PlayabilityAsserter::assert_playability(&player_response, video_id)?;
let captions_json = match player_response.get("captions") {
Some(captions) => match captions.get("playerCaptionsTracklistRenderer") {
Some(renderer) => renderer.clone(),
None => {
return Err(CouldNotRetrieveTranscript {
video_id: video_id.to_string(),
reason: Some(CouldNotRetrieveTranscriptReason::TranscriptsDisabled),
});
}
},
None => {
return Err(CouldNotRetrieveTranscript {
video_id: video_id.to_string(),
reason: Some(CouldNotRetrieveTranscriptReason::TranscriptsDisabled),
});
}
};
Ok(captions_json)
}
}