main
use crate::services::attachments::{attachment_stream_content_type, is_inline_image};
use cache::AppRedis;
use db::AppDatabase;
use imaging::layout::{ImageVariant, PreviewState};
use imaging::preset::PreviewPreset;
use models::chat::attachments;
use sea_orm::sea_query::Expr;
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
use std::sync::Arc;
use storage::S3Client;
#[derive(Clone)]
pub struct Media {
pub(crate) client: Arc<S3Client>,
previews: AppRedis,
database: Arc<AppDatabase>,
}
impl Media {
pub fn new(client: Arc<S3Client>, previews: AppRedis, database: Arc<AppDatabase>) -> Self {
Self {
client,
previews,
database,
}
}
pub(crate) async fn preview(
&self,
row: &attachments::Model,
recorded: Option<&PreviewState>,
) -> Option<PreviewState> {
let key_base = ::media::preview_key_base_of(&row.s3_key);
::media::ensure_preview(
self.client.as_ref(),
&self.previews,
&key_base,
&row.s3_key,
&row.content_type,
PreviewPreset::Content,
recorded,
)
.await
}
pub(crate) async fn attachment_variant_locator(
&self,
row: &attachments::Model,
variant: ImageVariant,
) -> (String, String) {
if variant == ImageVariant::Original || !is_inline_image(&row.content_type) {
return (row.s3_key.clone(), attachment_stream_content_type(row));
}
let recorded = imaging::layout::preview_from_variants(&row.variants);
let state = self.preview(row, recorded.as_ref()).await;
match state {
Some(state @ PreviewState::Ready(_)) => {
if recorded.as_ref() != Some(&state)
&& let Err(error) = self
.record_attachment_preview(row.id, row.variants.clone(), &state)
.await
{
tracing::warn!(%error, attachment_id = %row.id, "failed to record attachment preview");
}
(
{
let state = &state;
match state {
PreviewState::Ready(ready) => ready.key.clone(),
PreviewState::Skipped(_) => String::new(),
}
},
{
let state = &state;
match state {
PreviewState::Ready(ready) => ready.content_type.clone(),
PreviewState::Skipped(_) => "application/octet-stream".to_string(),
}
},
)
}
Some(PreviewState::Skipped(_)) | None => {
(row.s3_key.clone(), attachment_stream_content_type(row))
}
}
}
async fn record_attachment_preview(
&self,
attachment_id: uuid::Uuid,
mut variants: serde_json::Value,
state: &PreviewState,
) -> Result<(), sea_orm::DbErr> {
imaging::layout::preview_into_variants(&mut variants, state);
attachments::Entity::update_many()
.filter(attachments::Column::Id.eq(attachment_id))
.col_expr(attachments::Column::Variants, Expr::value(variants))
.exec(self.database.write())
.await?;
Ok(())
}
}