use wacore::download::Downloadable;
use wacore::media_retry::MediaRetryResult;
use wacore_binary::jid::Jid;
use whatsapp_rust::client::Client;
use whatsapp_rust::features::MediaReuploadRequest;
#[derive(Clone)]
pub(crate) struct MediaContext {
pub msg_id: String,
pub chat: Jid,
pub is_from_me: bool,
pub participant: Option<Jid>,
}
pub(crate) async fn download_with_retry<T, F>(
client: &Client,
media: &T,
ctx: &MediaContext,
kind: &str,
set_direct_path: F,
) -> Option<Vec<u8>>
where
T: Downloadable + Clone,
F: Fn(&mut T, String),
{
match client.download(media).await {
Ok(bytes) => return Some(bytes),
Err(e) => tracing::debug!(
target: "whatsapp",
error = %e,
kind,
"media download failed; asking the server to re-upload"
),
}
let Some(media_key) = media.media_key() else {
tracing::warn!(target: "whatsapp", kind, "no media key; cannot request a re-upload");
return None;
};
let request = MediaReuploadRequest {
msg_id: &ctx.msg_id,
chat_jid: &ctx.chat,
media_key,
is_from_me: ctx.is_from_me,
participant: ctx.participant.as_ref(),
};
let fresh_path = match client.media_reupload().request(&request).await {
Ok(MediaRetryResult::Success { direct_path }) => direct_path,
Ok(other) => {
tracing::warn!(
target: "whatsapp",
kind,
result = ?other,
"server declined to re-upload the media; it is gone, not merely stale"
);
return None;
}
Err(e) => {
tracing::warn!(target: "whatsapp", error = %e, kind, "media re-upload request failed");
return None;
}
};
let mut recovered = media.clone();
set_direct_path(&mut recovered, fresh_path);
match client.download(&recovered).await {
Ok(bytes) => {
tracing::info!(
target: "whatsapp",
kind,
"recovered media through a server re-upload after an expired URL"
);
Some(bytes)
}
Err(e) => {
tracing::error!(
target: "whatsapp",
error = %e,
kind,
"media still would not download after a successful re-upload"
);
None
}
}
}