atagen

macltok

public
Star0
Watch0
0827ad2d24e99df0ebcf85dd19c58eb7b4ccbb8c
3
ssh://brewery@pls.lobotomise.me:33/atagen/macltok.git

Blame: src/upload.rs

96fdf90aacd6atagen2026-09-08 08:24 UTC1
use std::time::Duration;
96fdf90aacd6atagen2026-09-08 08:24 UTC2
96fdf90aacd6atagen2026-09-08 08:24 UTC3
use matrix_sdk::{
96fdf90aacd6atagen2026-09-08 08:24 UTC4
    attachment::{
96fdf90aacd6atagen2026-09-08 08:24 UTC5
        AttachmentConfig, AttachmentInfo, BaseAudioInfo, BaseFileInfo, BaseImageInfo,
96fdf90aacd6atagen2026-09-08 08:24 UTC6
        BaseVideoInfo, Thumbnail,
96fdf90aacd6atagen2026-09-08 08:24 UTC7
    },
0827ad2d24e9atagen2026-10-23 06:49 UTC8
    ruma::events::{Mentions, room::message::TextMessageEventContent},
96fdf90aacd6atagen2026-09-08 08:24 UTC9
};
96fdf90aacd6atagen2026-09-08 08:24 UTC10
0827ad2d24e9atagen2026-10-23 06:49 UTC11
use crate::{AttachmentUpload, Error, ErrorKind, MessageMentions, RichText, UploadDetails};
0827ad2d24e9atagen2026-10-23 06:49 UTC12
0827ad2d24e9atagen2026-10-23 06:49 UTC13
pub(crate) fn sdk_mentions(mentions: MessageMentions) -> Mentions {
0827ad2d24e9atagen2026-10-23 06:49 UTC14
    let mut sdk = Mentions::with_user_ids(mentions.user_ids);
0827ad2d24e9atagen2026-10-23 06:49 UTC15
    sdk.room = mentions.room;
0827ad2d24e9atagen2026-10-23 06:49 UTC16
    sdk
0827ad2d24e9atagen2026-10-23 06:49 UTC17
}
0827ad2d24e9atagen2026-10-23 06:49 UTC18
0827ad2d24e9atagen2026-10-23 06:49 UTC19
fn caption_content(caption: RichText) -> TextMessageEventContent {
0827ad2d24e9atagen2026-10-23 06:49 UTC20
    match caption.formatted_body {
0827ad2d24e9atagen2026-10-23 06:49 UTC21
        Some(formatted_body) => TextMessageEventContent::html(caption.body, formatted_body),
0827ad2d24e9atagen2026-10-23 06:49 UTC22
        None => TextMessageEventContent::plain(caption.body),
0827ad2d24e9atagen2026-10-23 06:49 UTC23
    }
0827ad2d24e9atagen2026-10-23 06:49 UTC24
}
96fdf90aacd6atagen2026-09-08 08:24 UTC25
96fdf90aacd6atagen2026-09-08 08:24 UTC26
pub(crate) fn prepare_upload(
96fdf90aacd6atagen2026-09-08 08:24 UTC27
    mut upload: AttachmentUpload,
96fdf90aacd6atagen2026-09-08 08:24 UTC28
    max_media_bytes: usize,
31ce2453c82fatagen2026-09-24 13:07 UTC29
) -> Result<(AttachmentUpload, mime::Mime, AttachmentConfig), Error> {
96fdf90aacd6atagen2026-09-08 08:24 UTC30
    if upload.bytes.len() > max_media_bytes {
31ce2453c82fatagen2026-09-24 13:07 UTC31
        return Err(Error::new(
31ce2453c82fatagen2026-09-24 13:07 UTC32
            ErrorKind::MediaTooLarge,
96fdf90aacd6atagen2026-09-08 08:24 UTC33
            "attachment exceeds the configured byte limit",
96fdf90aacd6atagen2026-09-08 08:24 UTC34
        ));
96fdf90aacd6atagen2026-09-08 08:24 UTC35
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC36
    let mimetype: mime::Mime = upload
96fdf90aacd6atagen2026-09-08 08:24 UTC37
        .mimetype
96fdf90aacd6atagen2026-09-08 08:24 UTC38
        .parse()
31ce2453c82fatagen2026-09-24 13:07 UTC39
        .map_err(|_| Error::new(ErrorKind::Invalid, "media MIME type is invalid"))?;
96fdf90aacd6atagen2026-09-08 08:24 UTC40
96fdf90aacd6atagen2026-09-08 08:24 UTC41
    let size = ruma::UInt::try_from(upload.bytes.len()).map_err(|_| {
31ce2453c82fatagen2026-09-24 13:07 UTC42
        Error::new(
31ce2453c82fatagen2026-09-24 13:07 UTC43
            ErrorKind::MediaTooLarge,
96fdf90aacd6atagen2026-09-08 08:24 UTC44
            "attachment size is not representable",
96fdf90aacd6atagen2026-09-08 08:24 UTC45
        )
96fdf90aacd6atagen2026-09-08 08:24 UTC46
    })?;
96fdf90aacd6atagen2026-09-08 08:24 UTC47
    let info = match upload.details.as_ref() {
96fdf90aacd6atagen2026-09-08 08:24 UTC48
        Some(UploadDetails::Image {
96fdf90aacd6atagen2026-09-08 08:24 UTC49
            width,
96fdf90aacd6atagen2026-09-08 08:24 UTC50
            height,
96fdf90aacd6atagen2026-09-08 08:24 UTC51
            blurhash,
96fdf90aacd6atagen2026-09-08 08:24 UTC52
        }) if mimetype.type_() == mime::IMAGE => AttachmentInfo::Image(BaseImageInfo {
96fdf90aacd6atagen2026-09-08 08:24 UTC53
            width: width.map(Into::into),
96fdf90aacd6atagen2026-09-08 08:24 UTC54
            height: height.map(Into::into),
96fdf90aacd6atagen2026-09-08 08:24 UTC55
            size: Some(size),
96fdf90aacd6atagen2026-09-08 08:24 UTC56
            blurhash: blurhash.clone(),
96fdf90aacd6atagen2026-09-08 08:24 UTC57
            ..Default::default()
96fdf90aacd6atagen2026-09-08 08:24 UTC58
        }),
96fdf90aacd6atagen2026-09-08 08:24 UTC59
        Some(UploadDetails::Video {
96fdf90aacd6atagen2026-09-08 08:24 UTC60
            width,
96fdf90aacd6atagen2026-09-08 08:24 UTC61
            height,
96fdf90aacd6atagen2026-09-08 08:24 UTC62
            duration_ms,
96fdf90aacd6atagen2026-09-08 08:24 UTC63
            blurhash,
96fdf90aacd6atagen2026-09-08 08:24 UTC64
        }) if mimetype.type_() == mime::VIDEO => AttachmentInfo::Video(BaseVideoInfo {
96fdf90aacd6atagen2026-09-08 08:24 UTC65
            width: width.map(Into::into),
96fdf90aacd6atagen2026-09-08 08:24 UTC66
            height: height.map(Into::into),
96fdf90aacd6atagen2026-09-08 08:24 UTC67
            duration: duration_ms.map(Duration::from_millis),
96fdf90aacd6atagen2026-09-08 08:24 UTC68
            size: Some(size),
96fdf90aacd6atagen2026-09-08 08:24 UTC69
            blurhash: blurhash.clone(),
96fdf90aacd6atagen2026-09-08 08:24 UTC70
        }),
96fdf90aacd6atagen2026-09-08 08:24 UTC71
        Some(UploadDetails::Audio { duration_ms }) if mimetype.type_() == mime::AUDIO => {
96fdf90aacd6atagen2026-09-08 08:24 UTC72
            AttachmentInfo::Audio(BaseAudioInfo {
96fdf90aacd6atagen2026-09-08 08:24 UTC73
                duration: duration_ms.map(Duration::from_millis),
96fdf90aacd6atagen2026-09-08 08:24 UTC74
                size: Some(size),
96fdf90aacd6atagen2026-09-08 08:24 UTC75
                ..Default::default()
96fdf90aacd6atagen2026-09-08 08:24 UTC76
            })
96fdf90aacd6atagen2026-09-08 08:24 UTC77
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC78
        Some(UploadDetails::Voice {
96fdf90aacd6atagen2026-09-08 08:24 UTC79
            duration_ms,
96fdf90aacd6atagen2026-09-08 08:24 UTC80
            waveform,
96fdf90aacd6atagen2026-09-08 08:24 UTC81
        }) if mimetype.type_() == mime::AUDIO => {
96fdf90aacd6atagen2026-09-08 08:24 UTC82
            if waveform.iter().any(|sample| *sample > 1024) {
31ce2453c82fatagen2026-09-24 13:07 UTC83
                return Err(Error::new(
31ce2453c82fatagen2026-09-24 13:07 UTC84
                    ErrorKind::Invalid,
96fdf90aacd6atagen2026-09-08 08:24 UTC85
                    "voice waveform samples must be at most 1024",
96fdf90aacd6atagen2026-09-08 08:24 UTC86
                ));
96fdf90aacd6atagen2026-09-08 08:24 UTC87
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC88
            AttachmentInfo::Voice(BaseAudioInfo {
96fdf90aacd6atagen2026-09-08 08:24 UTC89
                duration: Some(Duration::from_millis(*duration_ms)),
96fdf90aacd6atagen2026-09-08 08:24 UTC90
                size: Some(size),
96fdf90aacd6atagen2026-09-08 08:24 UTC91
                waveform: Some(
96fdf90aacd6atagen2026-09-08 08:24 UTC92
                    waveform
96fdf90aacd6atagen2026-09-08 08:24 UTC93
                        .iter()
96fdf90aacd6atagen2026-09-08 08:24 UTC94
                        .map(|sample| f32::from(*sample) / 1024.0)
96fdf90aacd6atagen2026-09-08 08:24 UTC95
                        .collect(),
96fdf90aacd6atagen2026-09-08 08:24 UTC96
                ),
96fdf90aacd6atagen2026-09-08 08:24 UTC97
            })
96fdf90aacd6atagen2026-09-08 08:24 UTC98
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC99
        Some(UploadDetails::File)
96fdf90aacd6atagen2026-09-08 08:24 UTC100
            if mimetype.type_() != mime::IMAGE
96fdf90aacd6atagen2026-09-08 08:24 UTC101
                && mimetype.type_() != mime::VIDEO
96fdf90aacd6atagen2026-09-08 08:24 UTC102
                && mimetype.type_() != mime::AUDIO =>
96fdf90aacd6atagen2026-09-08 08:24 UTC103
        {
96fdf90aacd6atagen2026-09-08 08:24 UTC104
            AttachmentInfo::File(BaseFileInfo { size: Some(size) })
96fdf90aacd6atagen2026-09-08 08:24 UTC105
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC106
        None => match mimetype.type_() {
96fdf90aacd6atagen2026-09-08 08:24 UTC107
            mime::IMAGE => AttachmentInfo::Image(BaseImageInfo {
96fdf90aacd6atagen2026-09-08 08:24 UTC108
                size: Some(size),
96fdf90aacd6atagen2026-09-08 08:24 UTC109
                ..Default::default()
96fdf90aacd6atagen2026-09-08 08:24 UTC110
            }),
96fdf90aacd6atagen2026-09-08 08:24 UTC111
            mime::VIDEO => AttachmentInfo::Video(BaseVideoInfo {
96fdf90aacd6atagen2026-09-08 08:24 UTC112
                size: Some(size),
96fdf90aacd6atagen2026-09-08 08:24 UTC113
                ..Default::default()
96fdf90aacd6atagen2026-09-08 08:24 UTC114
            }),
96fdf90aacd6atagen2026-09-08 08:24 UTC115
            mime::AUDIO => AttachmentInfo::Audio(BaseAudioInfo {
96fdf90aacd6atagen2026-09-08 08:24 UTC116
                size: Some(size),
96fdf90aacd6atagen2026-09-08 08:24 UTC117
                ..Default::default()
96fdf90aacd6atagen2026-09-08 08:24 UTC118
            }),
96fdf90aacd6atagen2026-09-08 08:24 UTC119
            _ => AttachmentInfo::File(BaseFileInfo { size: Some(size) }),
96fdf90aacd6atagen2026-09-08 08:24 UTC120
        },
96fdf90aacd6atagen2026-09-08 08:24 UTC121
        _ => {
31ce2453c82fatagen2026-09-24 13:07 UTC122
            return Err(Error::new(
31ce2453c82fatagen2026-09-24 13:07 UTC123
                ErrorKind::Invalid,
96fdf90aacd6atagen2026-09-08 08:24 UTC124
                "attachment metadata does not match its MIME type",
96fdf90aacd6atagen2026-09-08 08:24 UTC125
            ));
96fdf90aacd6atagen2026-09-08 08:24 UTC126
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC127
    };
96fdf90aacd6atagen2026-09-08 08:24 UTC128
    if upload.thumbnail.is_some() && mimetype.type_() == mime::AUDIO {
31ce2453c82fatagen2026-09-24 13:07 UTC129
        return Err(Error::new(
31ce2453c82fatagen2026-09-24 13:07 UTC130
            ErrorKind::Invalid,
96fdf90aacd6atagen2026-09-08 08:24 UTC131
            "audio attachments do not support thumbnails",
96fdf90aacd6atagen2026-09-08 08:24 UTC132
        ));
96fdf90aacd6atagen2026-09-08 08:24 UTC133
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC134
    let thumbnail = upload
96fdf90aacd6atagen2026-09-08 08:24 UTC135
        .thumbnail
96fdf90aacd6atagen2026-09-08 08:24 UTC136
        .take()
96fdf90aacd6atagen2026-09-08 08:24 UTC137
        .map(|thumbnail| {
96fdf90aacd6atagen2026-09-08 08:24 UTC138
            if thumbnail.bytes.len() > max_media_bytes
96fdf90aacd6atagen2026-09-08 08:24 UTC139
                || thumbnail.width == 0
96fdf90aacd6atagen2026-09-08 08:24 UTC140
                || thumbnail.height == 0
96fdf90aacd6atagen2026-09-08 08:24 UTC141
            {
31ce2453c82fatagen2026-09-24 13:07 UTC142
                return Err(Error::new(
5a52012c9b1datagen2026-09-24 23:45 UTC143
                    if thumbnail.bytes.len() > max_media_bytes {
5a52012c9b1datagen2026-09-24 23:45 UTC144
                        ErrorKind::MediaTooLarge
5a52012c9b1datagen2026-09-24 23:45 UTC145
                    } else {
5a52012c9b1datagen2026-09-24 23:45 UTC146
                        ErrorKind::Invalid
5a52012c9b1datagen2026-09-24 23:45 UTC147
                    },
96fdf90aacd6atagen2026-09-08 08:24 UTC148
                    "thumbnail size or dimensions are invalid",
96fdf90aacd6atagen2026-09-08 08:24 UTC149
                ));
96fdf90aacd6atagen2026-09-08 08:24 UTC150
            }
31ce2453c82fatagen2026-09-24 13:07 UTC151
            let content_type: mime::Mime = thumbnail
31ce2453c82fatagen2026-09-24 13:07 UTC152
                .mimetype
31ce2453c82fatagen2026-09-24 13:07 UTC153
                .parse()
31ce2453c82fatagen2026-09-24 13:07 UTC154
                .map_err(|_| Error::new(ErrorKind::Invalid, "thumbnail MIME type is invalid"))?;
96fdf90aacd6atagen2026-09-08 08:24 UTC155
            if content_type.type_() != mime::IMAGE {
31ce2453c82fatagen2026-09-24 13:07 UTC156
                return Err(Error::new(
31ce2453c82fatagen2026-09-24 13:07 UTC157
                    ErrorKind::Invalid,
96fdf90aacd6atagen2026-09-08 08:24 UTC158
                    "thumbnail MIME type must be an image",
96fdf90aacd6atagen2026-09-08 08:24 UTC159
                ));
96fdf90aacd6atagen2026-09-08 08:24 UTC160
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC161
            let size = ruma::UInt::try_from(thumbnail.bytes.len()).map_err(|_| {
31ce2453c82fatagen2026-09-24 13:07 UTC162
                Error::new(
31ce2453c82fatagen2026-09-24 13:07 UTC163
                    ErrorKind::MediaTooLarge,
96fdf90aacd6atagen2026-09-08 08:24 UTC164
                    "thumbnail size is not representable",
96fdf90aacd6atagen2026-09-08 08:24 UTC165
                )
96fdf90aacd6atagen2026-09-08 08:24 UTC166
            })?;
96fdf90aacd6atagen2026-09-08 08:24 UTC167
            Ok(Thumbnail {
96fdf90aacd6atagen2026-09-08 08:24 UTC168
                data: thumbnail.bytes,
96fdf90aacd6atagen2026-09-08 08:24 UTC169
                content_type,
96fdf90aacd6atagen2026-09-08 08:24 UTC170
                width: thumbnail.width.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC171
                height: thumbnail.height.into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC172
                size,
96fdf90aacd6atagen2026-09-08 08:24 UTC173
            })
96fdf90aacd6atagen2026-09-08 08:24 UTC174
        })
96fdf90aacd6atagen2026-09-08 08:24 UTC175
        .transpose()?;
96fdf90aacd6atagen2026-09-08 08:24 UTC176
    let config = AttachmentConfig::new()
96fdf90aacd6atagen2026-09-08 08:24 UTC177
        .info(info)
96fdf90aacd6atagen2026-09-08 08:24 UTC178
        .thumbnail(thumbnail)
0827ad2d24e9atagen2026-10-23 06:49 UTC179
        .caption(upload.caption.take().map(caption_content))
0827ad2d24e9atagen2026-10-23 06:49 UTC180
        .mentions(Some(sdk_mentions(std::mem::take(&mut upload.mentions))));
96fdf90aacd6atagen2026-09-08 08:24 UTC181
    Ok((upload, mimetype, config))
96fdf90aacd6atagen2026-09-08 08:24 UTC182
}