Files
Genarrative/server-rs/crates/platform-audio/src/download.rs
2026-05-26 13:18:13 +08:00

119 lines
3.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<DownloadedAudio, AudioError> {
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()
}