use std::error::Error; use std::process::Command; pub mod cache; pub mod discord; pub mod flaresolverr; pub mod flow_debug; pub mod hoster_proxy; pub mod proxy; pub mod requester; pub mod time; pub mod browser; pub mod dean_edwards; pub mod playwright; pub mod webdriver; pub fn parse_abbreviated_number(s: &str) -> Option { let s = s.trim(); if s.is_empty() { return None; } let (num_part, suffix) = s .chars() .partition::(|c| c.is_ascii_digit() || *c == '.'); let multiplier = match suffix.trim().to_ascii_uppercase().as_str() { "K" => 1_000.0, "M" => 1_000_000.0, "B" => 1_000_000_000.0, "" => 1.0, _ => return None, }; num_part .parse::() .ok() .map(|n| (n * multiplier) as u32) } #[cfg(not(hottub_single_provider))] pub fn interleave(lists: &[Vec]) -> Vec { let mut result = Vec::new(); if lists.is_empty() { return result; } // Find the maximum length among the lists let max_len = lists.iter().map(|l| l.len()).max().unwrap_or(0); // Interleave elements for i in 0..max_len { for list in lists { if let Some(item) = list.get(i) { result.push(item.clone()); } } } result } pub fn get_redirect_location(url: &str) -> Result, Box> { // 1. Execute curl: // -s: Silent (no progress bar) // -I: Fetch headers only (HEAD request) let output = Command::new("curl") .arg("-sI") .arg(url) .output()?; // Check if the command executed successfully if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); return Err(format!("curl command failed: {}", stderr).into()); } // 2. Parse the stdout let stdout = String::from_utf8_lossy(&output.stdout); // HTTP headers are separated by \r\n or \n for line in stdout.lines() { // Case-insensitive check for "Location:" if line.to_lowercase().starts_with("location:") { // Split "Location: https://example.com" into ["Location", " https://example.com"] let parts: Vec<&str> = line.splitn(2, ':').collect(); if parts.len() == 2 { // Trim whitespace and potential carriage returns (\r) return Ok(Some(parts[1].trim().to_string())); } } } Ok(None) }