126 lines
3.7 KiB
Rust
126 lines
3.7 KiB
Rust
use serde_json::Value;
|
|
|
|
pub fn normalize_task_status(status: &str) -> String {
|
|
let normalized = status.trim().to_ascii_lowercase().replace(' ', "_");
|
|
match normalized.as_str() {
|
|
"finish" | "finished" | "complete" | "completed" | "success" | "succeeded" => {
|
|
"completed".to_string()
|
|
}
|
|
"" => "processing".to_string(),
|
|
value => value.to_string(),
|
|
}
|
|
}
|
|
|
|
pub fn is_pending_task_status(status: &str) -> bool {
|
|
matches!(
|
|
status,
|
|
"created" | "pending" | "queued" | "processing" | "running" | "submitted" | "started"
|
|
)
|
|
}
|
|
|
|
pub fn is_failed_task_status(status: &str) -> bool {
|
|
matches!(
|
|
status,
|
|
"failed" | "error" | "canceled" | "cancelled" | "rejected" | "expired"
|
|
)
|
|
}
|
|
|
|
pub fn extract_audio_urls(payload: &Value) -> Vec<String> {
|
|
let mut urls = Vec::new();
|
|
collect_audio_url_strings(payload, &mut urls);
|
|
let mut deduped = Vec::new();
|
|
for url in urls {
|
|
if !deduped.contains(&url) {
|
|
deduped.push(url);
|
|
}
|
|
}
|
|
deduped
|
|
}
|
|
|
|
pub(crate) fn find_first_string_by_key(value: &Value, target_key: &str) -> Option<String> {
|
|
match value {
|
|
Value::Object(object) => {
|
|
for (key, value) in object {
|
|
if key.eq_ignore_ascii_case(target_key)
|
|
&& let Some(text) = value.as_str()
|
|
{
|
|
return Some(text.trim().to_string());
|
|
}
|
|
if let Some(found) = find_first_string_by_key(value, target_key) {
|
|
return Some(found);
|
|
}
|
|
}
|
|
None
|
|
}
|
|
Value::Array(items) => items
|
|
.iter()
|
|
.find_map(|item| find_first_string_by_key(item, target_key)),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn extract_string_by_path(value: &Value, path: &[&str]) -> Option<String> {
|
|
let mut current = value;
|
|
for key in path {
|
|
current = current.get(*key)?;
|
|
}
|
|
current.as_str().map(str::trim).map(ToOwned::to_owned)
|
|
}
|
|
|
|
fn collect_audio_url_strings(value: &Value, output: &mut Vec<String>) {
|
|
match value {
|
|
Value::Object(object) => {
|
|
for (key, value) in object {
|
|
if let Some(raw) = value.as_str()
|
|
&& looks_like_audio_url_key(key)
|
|
&& looks_like_http_url(raw)
|
|
{
|
|
output.push(raw.trim().to_string());
|
|
}
|
|
collect_audio_url_strings(value, output);
|
|
}
|
|
}
|
|
Value::Array(items) => {
|
|
for item in items {
|
|
collect_audio_url_strings(item, output);
|
|
}
|
|
}
|
|
Value::String(raw) if looks_like_http_url(raw) && looks_like_audio_url(raw) => {
|
|
output.push(raw.trim().to_string());
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
fn looks_like_audio_url_key(key: &str) -> bool {
|
|
let normalized = key.trim().to_ascii_lowercase();
|
|
normalized.contains("audio")
|
|
|| normalized.contains("wav")
|
|
|| normalized.contains("mp3")
|
|
|| normalized.contains("fileurl")
|
|
|| normalized == "url"
|
|
|| normalized.ends_with("_url")
|
|
|| normalized.ends_with("url")
|
|
}
|
|
|
|
fn looks_like_http_url(value: &str) -> bool {
|
|
let value = value.trim().to_ascii_lowercase();
|
|
value.starts_with("http://") || value.starts_with("https://")
|
|
}
|
|
|
|
fn looks_like_audio_url(value: &str) -> bool {
|
|
let value = value
|
|
.trim()
|
|
.split('?')
|
|
.next()
|
|
.unwrap_or_default()
|
|
.to_ascii_lowercase();
|
|
value.ends_with(".mp3")
|
|
|| value.ends_with(".wav")
|
|
|| value.ends_with(".m4a")
|
|
|| value.ends_with(".aac")
|
|
|| value.ends_with(".ogg")
|
|
|| value.ends_with(".webm")
|
|
|| value.ends_with(".flac")
|
|
}
|