use crate::download;
use crate::parser;
use crate::print_handleing::*;
use crate::URL;
use futures::future::join_all;
use std::sync::Arc;
use tokio::sync::Semaphore;
use tokio::task;
lazy_static::lazy_static! {
static ref CLIENT: reqwest::Client = reqwest::Client::builder()
.cookie_store(true)
.build()
.unwrap();
}
trait HttpClient {
async fn get(&self, url: &str) -> Result<String, Box<dyn std::error::Error>>;
async fn post(
&self,
url: &str,
form: &[(&str, &str)],
) -> Result<(), Box<dyn std::error::Error>>;
}
impl HttpClient for reqwest::Client {
async fn get(&self, url: &str) -> Result<String, Box<dyn std::error::Error>> {
let response = self.get(url).send().await?.text().await?;
Ok(response)
}
async fn post(
&self,
url: &str,
form: &[(&str, &str)],
) -> Result<(), Box<dyn std::error::Error>> {
self.post(url).form(form).send().await?;
Ok(())
}
}
async fn get_csrf_token<T: HttpClient>(client: &T) -> Result<String, Box<dyn std::error::Error>> {
let login_page = client.get(&format!("{}{}", URL, "login.html")).await?;
let document = scraper::Html::parse_document(&login_page);
let selector = scraper::Selector::parse("meta[name='csrf-token']")?;
let csrf_token = document
.select(&selector)
.next()
.and_then(|element| element.value().attr("content"))
.ok_or("CSRF token not found")?;
Ok(csrf_token.to_string())
}
async fn login<T: HttpClient>(
client: &T,
csrf_token: &str,
) -> Result<(), Box<dyn std::error::Error>> {
client
.post(
&format!("{}{}", URL, "login.html"),
&[
("email", "ritosis807@exeneli.com"),
("password", "'%dWU}ZdBJ8LzAy"),
("_csrf", csrf_token),
],
)
.await?;
Ok(())
}
pub async fn get_anime_episodes_and_download_the_episodes(
anime_url_ending: String,
path: &str,
) -> Result<(), Box<dyn std::error::Error>> {
let client = initialize_client();
fetch_login_page(&client).await?;
let csrf_token = get_csrf_token(&client).await?;
login(&client, &csrf_token).await?;
let mut episode_number: u32 = 1;
let episode_string = "episode";
let mut tasks = vec![];
let semaphore = Arc::new(Semaphore::new(4));
loop {
let anime_episode = format!("EP-{:03}.mp4", episode_number);
let full_file_path = format!("Anime/{}/{}", path, anime_episode);
if process_existing_file(&full_file_path)? {
episode_number += 1;
continue;
}
let episode_url = format!(
"{}/{}-{}-{}",
URL, anime_url_ending, episode_string, episode_number
);
let response = reqwest::get(&episode_url).await?;
if response.status() != reqwest::StatusCode::OK {
break;
}
let task = create_download_task(
semaphore.clone(),
episode_url,
path.to_string(),
episode_number,
)
.await;
tasks.push(task);
episode_number += 1;
}
let results = join_all(tasks).await;
for result in results {
if let Err(e) = result {
error_print(&format!("Error downloading episode: {}", e));
}
}
Ok(())
}
async fn send_to_downloader(
episode_url: String,
path: String,
episode_number: u32,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let client = CLIENT.clone();
info_print(&format!("Downloading episode {}", episode_number));
loop {
let authenticated_content = client.get(&episode_url).send().await?.text().await?;
let video_urls = parser::get_video_url(authenticated_content);
let encoded_url = video_urls.last().ok_or("No video URL found")?;
match download::handle_redirect_and_download(encoded_url, &path, episode_number).await {
Ok(_) => break,
Err(_) => {
info_print("Download failed retrying...");
continue;
}
}
}
success_print(&format!(
"Successfully downloaded episode {}",
episode_number
));
Ok(())
}
async fn fetch_login_page(client: &reqwest::Client) -> Result<(), reqwest::Error> {
client
.get(&format!("{}{}", URL, "login.html"))
.send()
.await?;
Ok(())
}
fn initialize_client() -> reqwest::Client {
CLIENT.clone()
}
fn process_existing_file(full_file_path: &str) -> Result<bool, Box<dyn std::error::Error>> {
let path_to_file = std::path::Path::new(full_file_path);
if path_to_file.exists() {
let metadata = std::fs::metadata(full_file_path)?;
if metadata.len() > 0 {
success_print(&format!(
"File {} already exists and is not empty, skipping...",
full_file_path
));
return Ok(true);
} else {
error_print(&format!(
"File {} already exists but is empty, proceeding with download...",
full_file_path
));
}
}
Ok(false)
}
async fn create_download_task(
semaphore: Arc<Semaphore>,
episode_url: String,
path: String,
episode_number: u32,
) -> tokio::task::JoinHandle<Result<(), Box<dyn std::error::Error + Send + Sync>>> {
let permit = semaphore.clone().acquire_owned().await.unwrap();
let path_clone = path.clone();
task::spawn(async move {
let _permit = permit;
send_to_downloader(episode_url, path_clone, episode_number).await
})
}