93 lines
2.4 KiB
Rust
93 lines
2.4 KiB
Rust
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<u32> {
|
|
let s = s.trim();
|
|
if s.is_empty() {
|
|
return None;
|
|
}
|
|
let (num_part, suffix) = s
|
|
.chars()
|
|
.partition::<String, _>(|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::<f64>()
|
|
.ok()
|
|
.map(|n| (n * multiplier) as u32)
|
|
}
|
|
|
|
#[cfg(not(hottub_single_provider))]
|
|
pub fn interleave<T: Clone>(lists: &[Vec<T>]) -> Vec<T> {
|
|
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<Option<String>, Box<dyn Error>> {
|
|
// 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)
|
|
} |