send: keep video thumbnails in the download-and-reupload fallback

media_from_file/media_from_url never attached the thumbnail, so any
video that tripped the fallback lost its cover frame. Both now take
item.thumbnail_url() and apply it, matching build_media_group.
This commit is contained in:
2026-08-08 20:19:19 +08:00
parent c093dfe5ac
commit a8156697fa
+30 -9
View File
@@ -61,6 +61,15 @@ impl MediaItemPayload {
MediaItemPayload::Animation { .. } => None, MediaItemPayload::Animation { .. } => None,
} }
} }
/// The cover-frame URL for videos (used by the upload fallback, which
/// otherwise drops the thumbnail the URL-send path applies).
fn thumbnail_url(&self) -> Option<&str> {
match self {
MediaItemPayload::Video { thumbnail, .. } => thumbnail.as_deref(),
MediaItemPayload::Photo { .. } | MediaItemPayload::Animation { .. } => None,
}
}
} }
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
@@ -522,8 +531,9 @@ fn media_from_file(
item: &MediaItemPayload, item: &MediaItemPayload,
path: std::path::PathBuf, path: std::path::PathBuf,
caption: Option<&str>, caption: Option<&str>,
) -> InputMedia { thumbnail: Option<&str>,
match item { ) -> Result<InputMedia, String> {
let mut media = match item {
MediaItemPayload::Photo { has_spoiler, .. } => { MediaItemPayload::Photo { has_spoiler, .. } => {
photo_media(InputFile::file(path), caption, *has_spoiler) photo_media(InputFile::file(path), caption, *has_spoiler)
} }
@@ -533,7 +543,11 @@ fn media_from_file(
MediaItemPayload::Animation { has_spoiler, .. } => { MediaItemPayload::Animation { has_spoiler, .. } => {
animation_media(InputFile::file(path), caption, *has_spoiler) animation_media(InputFile::file(path), caption, *has_spoiler)
} }
};
if let (Some(thumb), InputMedia::Video(v)) = (thumbnail, &mut media) {
*v = v.clone().thumbnail(input_file_for(thumb)?);
} }
Ok(media)
} }
/// Builds the media group item from a (smaller) URL. /// Builds the media group item from a (smaller) URL.
@@ -541,8 +555,9 @@ fn media_from_url(
item: &MediaItemPayload, item: &MediaItemPayload,
url: &str, url: &str,
caption: Option<&str>, caption: Option<&str>,
thumbnail: Option<&str>,
) -> Result<InputMedia, String> { ) -> Result<InputMedia, String> {
Ok(match item { let mut media = match item {
MediaItemPayload::Photo { has_spoiler, .. } => { MediaItemPayload::Photo { has_spoiler, .. } => {
photo_media(input_file_for(url)?, caption, *has_spoiler) photo_media(input_file_for(url)?, caption, *has_spoiler)
} }
@@ -552,7 +567,11 @@ fn media_from_url(
MediaItemPayload::Animation { has_spoiler, .. } => { MediaItemPayload::Animation { has_spoiler, .. } => {
animation_media(input_file_for(url)?, caption, *has_spoiler) animation_media(input_file_for(url)?, caption, *has_spoiler)
} }
}) };
if let (Some(thumb), InputMedia::Video(v)) = (thumbnail, &mut media) {
*v = v.clone().thumbnail(input_file_for(thumb)?);
}
Ok(media)
} }
/// Download-and-reupload fallback for one media batch. Files over the upload /// Download-and-reupload fallback for one media batch. Files over the upload
@@ -580,7 +599,7 @@ async fn send_batch_via_upload(
let too_large = too_large && !matches!(item, MediaItemPayload::Photo { .. }); let too_large = too_large && !matches!(item, MediaItemPayload::Photo { .. });
let media = if too_large { let media = if too_large {
match item.fallback_url() { match item.fallback_url() {
Some(url) => match media_from_url(item, url, item_caption) { Some(url) => match media_from_url(item, url, item_caption, item.thumbnail_url()) {
Ok(media) => media, Ok(media) => media,
Err(message) => { Err(message) => {
return Err(FallbackError::Permanent { message }); return Err(FallbackError::Permanent { message });
@@ -612,10 +631,11 @@ async fn send_batch_via_upload(
PhotoPrep::Upload(upload) => { PhotoPrep::Upload(upload) => {
let path = upload.path().to_path_buf(); let path = upload.path().to_path_buf();
files.push(upload); files.push(upload);
media_from_file(item, path, item_caption) media_from_file(item, path, item_caption, item.thumbnail_url())
.map_err(|message| FallbackError::Permanent { message })?
} }
PhotoPrep::UseFallback => match item.fallback_url() { PhotoPrep::UseFallback => match item.fallback_url() {
Some(url) => match media_from_url(item, url, item_caption) { Some(url) => match media_from_url(item, url, item_caption, item.thumbnail_url()) {
Ok(media) => media, Ok(media) => media,
Err(message) => { Err(message) => {
return Err(FallbackError::Permanent { message }); return Err(FallbackError::Permanent { message });
@@ -633,11 +653,12 @@ async fn send_batch_via_upload(
} else { } else {
let path = file.path().to_path_buf(); let path = file.path().to_path_buf();
files.push(file); files.push(file);
media_from_file(item, path, item_caption) media_from_file(item, path, item_caption, item.thumbnail_url())
.map_err(|message| FallbackError::Permanent { message })?
} }
} }
Err(FallbackError::MediaTooLarge) => match item.fallback_url() { Err(FallbackError::MediaTooLarge) => match item.fallback_url() {
Some(url) => match media_from_url(item, url, item_caption) { Some(url) => match media_from_url(item, url, item_caption, item.thumbnail_url()) {
Ok(media) => media, Ok(media) => media,
Err(message) => { Err(message) => {
return Err(FallbackError::Permanent { message }); return Err(FallbackError::Permanent { message });