mod config;
mod downloader;
mod extractor;
use std::collections::HashMap;
use std::path::Path;
use anyhow::Result;
pub const CHUNK_SIZE: usize = 2097152;
#[derive(Debug, Clone)]
pub struct TikTokDownloader {
headers: HashMap<String, String>,
cookie: String,
}
#[derive(Debug, Clone)]
pub struct TikTokDownloaderBuilder {
headers: HashMap<String, String>,
cookie: Option<String>,
}
impl Default for TikTokDownloaderBuilder {
fn default() -> Self {
let mut headers = HashMap::new();
headers.insert(
"User-Agent".to_string(),
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36".to_string()
);
headers.insert(
"Accept".to_string(),
"*/*".to_string()
);
headers.insert(
"Referer".to_string(),
"https://www.tiktok.com/explore".to_string()
);
Self {
headers,
cookie: None,
}
}
}
impl TikTokDownloaderBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn with_headers(mut self, headers: HashMap<String, String>) -> Self {
self.headers = headers;
self
}
pub fn with_header(mut self, key: &str, value: &str) -> Self {
self.headers.insert(key.to_string(), value.to_string());
self
}
pub fn with_cookie(mut self, cookie: &str) -> Self {
self.cookie = Some(cookie.to_string());
self
}
pub fn build(self) -> Result<TikTokDownloader> {
let cookie = self.cookie.ok_or_else(|| {
anyhow::anyhow!("Cookie is required for TikTok video downloads")
})?;
Ok(TikTokDownloader {
headers: self.headers,
cookie,
})
}
}
impl TikTokDownloader {
pub fn builder() -> TikTokDownloaderBuilder {
TikTokDownloaderBuilder::new()
}
pub async fn download_video<P: AsRef<Path>>(&self, video_url: &str, output_path: P) -> Result<()> {
let video_url = extractor::extract_tiktok_video_url(
video_url,
&self.headers,
&self.cookie
).await?;
let config = self.create_internal_config(output_path)?;
downloader::download_video(&video_url, &config).await
}
fn create_internal_config<P: AsRef<Path>>(&self, output_path: P) -> Result<config::Config> {
let output_path = output_path.as_ref();
let output_path_str = output_path.to_string_lossy().to_string();
let http_config = config::HttpConfig {
headers: self.headers.clone(),
cookie: self.cookie.clone(),
chunk_size: CHUNK_SIZE,
output_dir: output_path_str,
};
Ok(config::Config {
http: http_config,
})
}
}