atagen

macltok

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