diff --git a/src/infrastructure/repository/downloader.rs b/src/infrastructure/repository/downloader.rs index ec5feca..e47251f 100644 --- a/src/infrastructure/repository/downloader.rs +++ b/src/infrastructure/repository/downloader.rs @@ -770,11 +770,146 @@ pub(crate) async fn fetch_snapsave(url: &str) -> Result` tag. +/// This works server-side (no auth) for active videos while the main site/API are +/// Cloudflare-blocked. Verified: returns a real downloadable MP4 (200/206, video/mp4). +async fn fetch_tiktok_embed(url: &str) -> Result { + // Resolve short URLs (vm/vt.tiktok.com) to the long form to get a video ID + let resolved_url = resolve_tiktok_url(url).await?; + + let video_id = regex::Regex::new(r"/video/(\d+)") + .unwrap() + .captures(&resolved_url) + .and_then(|c| c.get(1)) + .map(|m| m.as_str().to_string()) + .ok_or_else(|| ScrapingError::Http("Invalid TikTok URL, no video ID".to_string()))?; + + let client = http_client(); + let html = client + .client() + .get(format!("https://www.tiktok.com/embed/v2/{}", video_id)) + .header(USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36") + .header("accept-language", "en-US,en;q=0.9") + .timeout(Duration::from_secs(25)) + .send() + .await + .map_err(|e| ScrapingError::Http(format!("TikTok embed fetch failed: {}", e)))? + .text() + .await + .map_err(|e| ScrapingError::Http(format!("TikTok embed read failed: {}", e)))?; + + // Extract