mirror of
https://framagit.org/veretcle/oolatoocs.git
synced 2025-12-05 22:33:16 +01:00
♻️: better handling of embedded records
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -1926,7 +1926,7 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "oolatoocs"
|
||||
version = "4.5.0"
|
||||
version = "4.5.1"
|
||||
dependencies = [
|
||||
"atrium-api",
|
||||
"bsky-sdk",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "oolatoocs"
|
||||
version = "4.5.0"
|
||||
version = "4.5.1"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
158
src/bsky.rs
158
src/bsky.rs
@@ -148,9 +148,96 @@ async fn get_record(
|
||||
Ok(record)
|
||||
}
|
||||
|
||||
/// Generate a Union of embed records to be built-in into records
|
||||
/// In case an embed cannot be uploaded/created, this calling function silently gets Option instead
|
||||
/// of failing
|
||||
pub async fn generate_embed_records(
|
||||
config: &BlueskyConfig,
|
||||
bsky: &BskyAgent,
|
||||
qid: Option<&str>,
|
||||
media_attach: &[Attachment],
|
||||
card: &Option<Card>,
|
||||
) -> Result<
|
||||
Option<atrium_api::types::Union<atrium_api::app::bsky::feed::post::RecordEmbedRefs>>,
|
||||
Box<dyn Error + Send + Sync>,
|
||||
> {
|
||||
// handle quote if any
|
||||
let quote_embed = match qid {
|
||||
Some(q) => generate_quote_records(config, q).await.ok(),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// handle medias if any
|
||||
let media_embed = if media_attach.len() > usize::from(0u8) {
|
||||
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();
|
||||
|
||||
if !video_media_attach.is_empty() {
|
||||
generate_video_record(bsky, video_media_attach).await.ok()
|
||||
} else if !image_media_attach.is_empty() {
|
||||
generate_images_records(bsky, image_media_attach).await.ok()
|
||||
} else {
|
||||
return Err(OolatoocsError::new("A media attached is not an image nor a video").into());
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// handle webcard if any
|
||||
let webcard_embed = match card {
|
||||
Some(t) => generate_webcard_records(bsky, t).await.ok(),
|
||||
None => None,
|
||||
};
|
||||
|
||||
if let Some(q) = quote_embed {
|
||||
if let Some(m) = media_embed {
|
||||
let medias_mapped = match m {
|
||||
atrium_api::app::bsky::feed::post::RecordEmbedRefs::AppBskyEmbedImagesMain(a) => atrium_api::app::bsky::embed::record_with_media::MainMediaRefs::AppBskyEmbedImagesMain(a),
|
||||
atrium_api::app::bsky::feed::post::RecordEmbedRefs::AppBskyEmbedVideoMain(a) => atrium_api::app::bsky::embed::record_with_media::MainMediaRefs::AppBskyEmbedVideoMain(a),
|
||||
_ => return Err(OolatoocsError::new("Something went terribly wrong when trying to add image/video to quote record: can’t decapsulate media").into()),
|
||||
};
|
||||
let quote_mapped = match q {
|
||||
atrium_api::app::bsky::feed::post::RecordEmbedRefs::AppBskyEmbedRecordMain(
|
||||
a,
|
||||
) => a,
|
||||
_ => return Err(OolatoocsError::new("Something went terribly wrong when trying to add image/video to quote record: can’t decapsulate quote").into()),
|
||||
};
|
||||
Some(atrium_api::types::Union::Refs(
|
||||
atrium_api::app::bsky::feed::post::RecordEmbedRefs::AppBskyEmbedRecordWithMediaMain(
|
||||
Box::new(
|
||||
atrium_api::app::bsky::embed::record_with_media::MainData {
|
||||
media: atrium_api::types::Union::Refs(medias_mapped),
|
||||
record: (*quote_mapped),
|
||||
}
|
||||
.into(),
|
||||
),
|
||||
),
|
||||
))
|
||||
} else {
|
||||
Some(atrium_api::types::Union::Refs(q))
|
||||
}
|
||||
} else if media_embed.is_some() {
|
||||
media_embed.map(atrium_api::types::Union::Refs)
|
||||
} else if webcard_embed.is_some() {
|
||||
webcard_embed.map(atrium_api::types::Union::Refs)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Generate an quote embed record
|
||||
/// it is encapsulated in Option to prevent this function from failing
|
||||
pub async fn generate_quote_records(
|
||||
async fn generate_quote_records(
|
||||
config: &BlueskyConfig,
|
||||
quote_id: &str,
|
||||
) -> Result<atrium_api::app::bsky::feed::post::RecordEmbedRefs, Box<dyn Error>> {
|
||||
@@ -173,7 +260,7 @@ pub async fn generate_quote_records(
|
||||
|
||||
/// Generate an embed webcard record into Bsky
|
||||
/// If the preview image does not exist or fails to upload, it is simply ignored
|
||||
pub async fn generate_webcard_records(
|
||||
async fn generate_webcard_records(
|
||||
bsky: &BskyAgent,
|
||||
card: &Card,
|
||||
) -> Result<atrium_api::app::bsky::feed::post::RecordEmbedRefs, Box<dyn Error + Send + Sync>> {
|
||||
@@ -199,47 +286,12 @@ pub async fn generate_webcard_records(
|
||||
)
|
||||
}
|
||||
|
||||
/// Generate an array of Bsky media records
|
||||
/// As Bsky does not support multiple video in a record or mix of video and images, video has the
|
||||
/// highest priority
|
||||
pub async fn generate_media_records(
|
||||
/// Generate an array of Bsky image media records
|
||||
async fn generate_images_records(
|
||||
bsky: &BskyAgent,
|
||||
media_attach: &[Attachment],
|
||||
media_attach: Vec<Attachment>,
|
||||
) -> Result<atrium_api::app::bsky::feed::post::RecordEmbedRefs, Box<dyn Error + Send + Sync>> {
|
||||
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();
|
||||
|
||||
// Bsky only tasks 1 video per post, so we’ll 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?;
|
||||
|
||||
return Ok(
|
||||
atrium_api::app::bsky::feed::post::RecordEmbedRefs::AppBskyEmbedVideoMain(Box::new(
|
||||
atrium_api::app::bsky::embed::video::MainData {
|
||||
alt: media.description.clone(),
|
||||
aspect_ratio: convert_aspect_ratio(
|
||||
&media.meta.as_ref().and_then(|m| m.original.clone()),
|
||||
),
|
||||
captions: None,
|
||||
video: blob.data.blob,
|
||||
}
|
||||
.into(),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
// It wasn’t a video, then it’s an image or a gallery of 4 images
|
||||
let mut stream = stream::iter(image_media_attach)
|
||||
let mut stream = stream::iter(media_attach)
|
||||
.map(|media| {
|
||||
let bsky = bsky.clone();
|
||||
tokio::task::spawn(async move {
|
||||
@@ -281,6 +333,30 @@ pub async fn generate_media_records(
|
||||
Err(OolatoocsError::new("Cannot embed media").into())
|
||||
}
|
||||
|
||||
/// Generate a video Bsky media record
|
||||
async fn generate_video_record(
|
||||
bsky: &BskyAgent,
|
||||
media_attach: Vec<Attachment>,
|
||||
) -> Result<atrium_api::app::bsky::feed::post::RecordEmbedRefs, Box<dyn Error + Send + Sync>> {
|
||||
// treat only the very first video, ignore the rest
|
||||
let media = &media_attach[0];
|
||||
let blob = upload_media(false, bsky, &media.url).await?;
|
||||
|
||||
Ok(
|
||||
atrium_api::app::bsky::feed::post::RecordEmbedRefs::AppBskyEmbedVideoMain(Box::new(
|
||||
atrium_api::app::bsky::embed::video::MainData {
|
||||
alt: media.description.clone(),
|
||||
aspect_ratio: convert_aspect_ratio(
|
||||
&media.meta.as_ref().and_then(|m| m.original.clone()),
|
||||
),
|
||||
captions: None,
|
||||
video: blob.data.blob,
|
||||
}
|
||||
.into(),
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
async fn upload_media(
|
||||
is_image: bool,
|
||||
bsky: &BskyAgent,
|
||||
|
||||
80
src/lib.rs
80
src/lib.rs
@@ -18,10 +18,7 @@ mod utils;
|
||||
use utils::{generate_multi_tweets, strip_everything};
|
||||
|
||||
mod bsky;
|
||||
use bsky::{
|
||||
build_post_record, generate_media_records, generate_quote_records, generate_webcard_records,
|
||||
get_session, BskyReply,
|
||||
};
|
||||
use bsky::{build_post_record, generate_embed_records, get_session, BskyReply};
|
||||
|
||||
use rusqlite::Connection;
|
||||
|
||||
@@ -161,69 +158,24 @@ pub async fn run(config: &Config) {
|
||||
});
|
||||
};
|
||||
|
||||
// handle quote if any
|
||||
let quote_embed = match toot.reblog {
|
||||
Some(r) => {
|
||||
let quote_record = read_state(&conn, Some(r.id.parse::<u64>().unwrap()));
|
||||
match quote_record {
|
||||
Ok(Some(q)) => generate_quote_records(&config.bluesky, &q.record_uri)
|
||||
.await
|
||||
.ok(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
// get quote_id if any
|
||||
let quote_id = match toot.reblog {
|
||||
Some(r) => match read_state(&conn, Some(r.id.parse::<u64>().unwrap())) {
|
||||
Ok(q) => q.map(|x| x.record_uri.to_owned()),
|
||||
_ => None,
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
|
||||
// handle medias if any
|
||||
let media_embed = if toot.media_attachments.len() > usize::from(0u8) {
|
||||
generate_media_records(&bluesky, &toot.media_attachments)
|
||||
.await
|
||||
.ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// handle webcard if any
|
||||
let webcard_embed = match toot.card {
|
||||
Some(t) => generate_webcard_records(&bluesky, &t).await.ok(),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let record_embed = if quote_embed.is_some() {
|
||||
if media_embed.is_some() {
|
||||
let medias_mapped = match media_embed.unwrap() {
|
||||
atrium_api::app::bsky::feed::post::RecordEmbedRefs::AppBskyEmbedImagesMain(a) => atrium_api::app::bsky::embed::record_with_media::MainMediaRefs::AppBskyEmbedImagesMain(a),
|
||||
atrium_api::app::bsky::feed::post::RecordEmbedRefs::AppBskyEmbedVideoMain(a) => atrium_api::app::bsky::embed::record_with_media::MainMediaRefs::AppBskyEmbedVideoMain(a),
|
||||
_ => continue, // this should NEVER happen as Media are either Video or
|
||||
// Images at this point
|
||||
};
|
||||
let quote_mapped = match quote_embed.unwrap() {
|
||||
atrium_api::app::bsky::feed::post::RecordEmbedRefs::AppBskyEmbedRecordMain(
|
||||
a,
|
||||
) => a,
|
||||
_ => continue, // again, this should NEVER happen
|
||||
};
|
||||
Some(atrium_api::types::Union::Refs(
|
||||
atrium_api::app::bsky::feed::post::RecordEmbedRefs::AppBskyEmbedRecordWithMediaMain(
|
||||
Box::new(
|
||||
atrium_api::app::bsky::embed::record_with_media::MainData {
|
||||
media: atrium_api::types::Union::Refs(medias_mapped),
|
||||
record: (*quote_mapped),
|
||||
}.into()
|
||||
)
|
||||
)
|
||||
))
|
||||
} else {
|
||||
quote_embed.map(atrium_api::types::Union::Refs)
|
||||
}
|
||||
} else if media_embed.is_some() {
|
||||
media_embed.map(atrium_api::types::Union::Refs)
|
||||
} else if webcard_embed.is_some() {
|
||||
webcard_embed.map(atrium_api::types::Union::Refs)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let record_embed = generate_embed_records(
|
||||
&config.bluesky,
|
||||
&bluesky,
|
||||
quote_id.as_deref(),
|
||||
&toot.media_attachments,
|
||||
&toot.card,
|
||||
)
|
||||
.await
|
||||
.unwrap_or_else(|e| panic!("Cannot embed record for {}: {}", &toot.id, e));
|
||||
|
||||
// posts corresponding tweet
|
||||
let record = build_post_record(
|
||||
|
||||
Reference in New Issue
Block a user