🎨: improve bsky image upload

This commit is contained in:
VC
2025-01-24 13:49:11 +01:00
parent cad7840f98
commit 891f46ec2f
3 changed files with 89 additions and 36 deletions

View File

@@ -8,8 +8,9 @@ use bsky_sdk::{
rich_text::RichText,
BskyAgent,
};
use futures::{stream, StreamExt};
use image::ImageReader;
use log::{debug, error};
use log::{debug, error, warn};
use megalodon::entities::attachment::{Attachment, AttachmentType};
use regex::Regex;
use std::{error::Error, fs::exists, io::Cursor};
@@ -152,39 +153,66 @@ pub async fn generate_media_records(
let mut embed: Option<
atrium_api::types::Union<atrium_api::app::bsky::feed::post::RecordEmbedRefs>,
> = None;
let mut images = Vec::new();
let mut videos: Vec<atrium_api::app::bsky::embed::video::MainData> = Vec::new();
for media in media_attach.iter() {
match media.r#type {
AttachmentType::Image => {
let blob = upload_media(true, bsky, &media.url).await.unwrap();
let image_media_attach: Vec<_> = media_attach
.iter()
.filter(|x| x.r#type == AttachmentType::Image)
.cloned()
.collect();
let video_media_attach: Vec<_> = media_attach
.iter()
.filter(|x| (x.r#type == AttachmentType::Video || x.r#type == AttachmentType::Gifv))
.cloned()
.collect();
images.push(
// Bsky only tasks 1 video per post, so well try to treat that first and exit
if !video_media_attach.is_empty() {
// treat only the very first video, ignore the rest
let media = &video_media_attach[0];
let blob = upload_media(false, bsky, &media.url).await.unwrap();
embed = Some(atrium_api::types::Union::Refs(
atrium_api::app::bsky::feed::post::RecordEmbedRefs::AppBskyEmbedVideoMain(Box::new(
atrium_api::app::bsky::embed::video::MainData {
alt: media.description.clone(),
aspect_ratio: None,
captions: None,
video: blob.data.blob,
}
.into(),
)),
));
// returns immediately, we dont want to treat the other medias
return embed;
}
let mut stream = stream::iter(image_media_attach)
.map(|media| {
let bsky = bsky.clone();
tokio::task::spawn(async move {
debug!("Treating media {}", &media.url);
upload_media(true, &bsky, &media.url).await.map(|i| {
atrium_api::app::bsky::embed::images::ImageData {
alt: media
.description
.clone()
.map_or("".to_string(), |v| v.to_owned()),
aspect_ratio: None,
image: blob.data.blob,
image: i.data.blob,
}
.into(),
);
}
AttachmentType::Gifv | AttachmentType::Video => {
let blob = upload_media(false, bsky, &media.url).await.unwrap();
})
})
})
.buffered(4);
videos.push(atrium_api::app::bsky::embed::video::MainData {
alt: media.description.clone(),
aspect_ratio: None,
captions: None,
video: blob.data.blob,
});
}
_ => {
error!("Not an image, not a video, what happened here?");
}
let mut images = Vec::new();
while let Some(result) = stream.next().await {
match result {
Ok(Ok(v)) => images.push(v.into()),
Ok(Err(e)) => warn!("Cannot treat a specific media: {}", e),
Err(e) => error!("Something went wrong when joining main thread: {}", e),
}
}
@@ -196,19 +224,14 @@ pub async fn generate_media_records(
));
}
// if a video has been uploaded, it takes priority as you can only have 1 video per post
if !videos.is_empty() {
embed = Some(atrium_api::types::Union::Refs(
atrium_api::app::bsky::feed::post::RecordEmbedRefs::AppBskyEmbedVideoMain(Box::new(
videos[0].clone().into(),
)),
))
}
embed
}
async fn upload_media(is_image: bool, bsky: &BskyAgent, u: &str) -> Result<Output, Box<dyn Error>> {
async fn upload_media(
is_image: bool,
bsky: &BskyAgent,
u: &str,
) -> Result<Output, Box<dyn Error + Send + Sync>> {
let dl = reqwest::get(u).await?;
let content_length = dl.content_length().ok_or("Content length unavailable")?;
let bytes = if content_length <= 1_000_000 || !is_image {