use std::error::Error; use reqwest::header; use crate::{AudioError, DownloadedAudio, MAX_GENERATED_AUDIO_BYTES}; pub fn normalize_audio_mime_type(content_type: &str, audio_url: &str) -> String { let mime_type = content_type .split(';') .next() .map(str::trim) .filter(|value| value.starts_with("audio/")) .unwrap_or(""); match mime_type { "audio/mpeg" | "audio/mp3" => "audio/mpeg".to_string(), "audio/wav" | "audio/wave" | "audio/x-wav" => "audio/wav".to_string(), "audio/ogg" => "audio/ogg".to_string(), "audio/webm" => "audio/webm".to_string(), "audio/aac" => "audio/aac".to_string(), "audio/flac" => "audio/flac".to_string(), "audio/mp4" | "audio/x-m4a" => "audio/mp4".to_string(), _ => mime_type_from_audio_url(audio_url), } } pub fn audio_mime_to_extension(mime_type: &str) -> &'static str { match mime_type { "audio/wav" => "wav", "audio/ogg" => "ogg", "audio/webm" => "webm", "audio/aac" => "aac", "audio/flac" => "flac", "audio/mp4" => "m4a", _ => "mp3", } } pub async fn download_generated_audio( http_client: &reqwest::Client, audio_url: &str, _provider: &str, ) -> Result { let response = http_client.get(audio_url).send().await.map_err(|error| { AudioError::request( format!("下载生成音频失败:{error}"), Some(audio_url.to_string()), error.is_timeout(), error.is_connect(), error.is_request(), error.is_body(), error.status().map(|status| status.as_u16()), Error::source(&error).map(ToString::to_string), ) })?; let status = response.status(); let content_type = response .headers() .get(header::CONTENT_TYPE) .and_then(|value| value.to_str().ok()) .unwrap_or("audio/mpeg") .to_string(); let body = response.bytes().await.map_err(|error| { AudioError::request( format!("读取生成音频内容失败:{error}"), Some(audio_url.to_string()), false, false, false, true, None, None, ) })?; if !status.is_success() { return Err(AudioError::upstream( format!("下载生成音频失败:HTTP {}", status.as_u16()), status.as_u16(), truncate_raw(""), )); } if body.is_empty() || body.len() > MAX_GENERATED_AUDIO_BYTES { return Err(AudioError::missing_audio("生成音频内容为空或超过大小上限")); } let mime_type = normalize_audio_mime_type(&content_type, audio_url); Ok(DownloadedAudio { extension: audio_mime_to_extension(&mime_type).to_string(), mime_type, bytes: body.to_vec(), }) } fn mime_type_from_audio_url(audio_url: &str) -> String { let path = audio_url .split('?') .next() .unwrap_or_default() .to_ascii_lowercase(); if path.ends_with(".wav") { "audio/wav".to_string() } else if path.ends_with(".ogg") { "audio/ogg".to_string() } else if path.ends_with(".webm") { "audio/webm".to_string() } else if path.ends_with(".aac") { "audio/aac".to_string() } else if path.ends_with(".flac") { "audio/flac".to_string() } else if path.ends_with(".m4a") { "audio/mp4".to_string() } else { "audio/mpeg".to_string() } } fn truncate_raw(raw_text: &str) -> String { raw_text.chars().take(800).collect() }