use reqwest::Client;
use crate::captions_extractor::CaptionsExtractor;
use crate::errors::CouldNotRetrieveTranscript;
use crate::js_var_parser::JsVarParser;
use crate::microformat_extractor::MicroformatExtractor;
use crate::models::{MicroformatData, StreamingData, VideoDetails, VideoInfos};
use crate::playability_asserter::PlayabilityAsserter;
use crate::streaming_data_extractor::StreamingDataExtractor;
use crate::transcript_list::TranscriptList;
use crate::video_details_extractor::VideoDetailsExtractor;
use crate::youtube_page_fetcher::YoutubePageFetcher;
pub struct VideoDataFetcher {
pub client: Client,
page_fetcher: YoutubePageFetcher,
}
impl VideoDataFetcher {
pub fn new(client: Client) -> Self {
let page_fetcher = YoutubePageFetcher::new(client.clone());
Self {
client,
page_fetcher,
}
}
pub async fn fetch_transcript_list(
&self,
video_id: &str,
) -> Result<TranscriptList, CouldNotRetrieveTranscript> {
let player_response = self.fetch_player_response(video_id, true).await?;
let video_captions = CaptionsExtractor::extract_captions_data(&player_response, video_id)?;
TranscriptList::build(video_id.to_string(), &video_captions)
}
pub async fn fetch_video_details(
&self,
video_id: &str,
) -> Result<VideoDetails, CouldNotRetrieveTranscript> {
let player_response = self.fetch_player_response(video_id, true).await?;
VideoDetailsExtractor::extract_video_details(&player_response, video_id)
}
pub async fn fetch_microformat(
&self,
video_id: &str,
) -> Result<MicroformatData, CouldNotRetrieveTranscript> {
let player_response = self.fetch_player_response(video_id, true).await?;
MicroformatExtractor::extract_microformat_data(&player_response, video_id)
}
pub async fn fetch_streaming_data(
&self,
video_id: &str,
) -> Result<StreamingData, CouldNotRetrieveTranscript> {
let player_response = self.fetch_player_response(video_id, true).await?;
StreamingDataExtractor::extract_streaming_data(&player_response, video_id)
}
pub async fn fetch_video_infos(
&self,
video_id: &str,
) -> Result<VideoInfos, CouldNotRetrieveTranscript> {
let player_response = self.fetch_player_response(video_id, true).await?;
let video_details =
VideoDetailsExtractor::extract_video_details(&player_response, video_id)?;
let microformat =
MicroformatExtractor::extract_microformat_data(&player_response, video_id)?;
let streaming_data =
StreamingDataExtractor::extract_streaming_data(&player_response, video_id)?;
let captions_data = CaptionsExtractor::extract_captions_data(&player_response, video_id)?;
let transcript_list = TranscriptList::build(video_id.to_string(), &captions_data)?;
Ok(VideoInfos {
video_details,
microformat,
streaming_data,
transcript_list,
})
}
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)
}
async fn fetch_player_response(
&self,
video_id: &str,
check_playability: bool,
) -> Result<serde_json::Value, CouldNotRetrieveTranscript> {
let html = self.page_fetcher.fetch_video_page(video_id).await?;
let player_response = self.extract_yt_initial_player_response(&html, video_id)?;
if check_playability {
PlayabilityAsserter::assert_playability(&player_response, video_id)?;
}
Ok(player_response)
}
}