atagen

macltok

public
Star0
Watch0
89bc9f8d246d2a446218c05b78de187c2fdab6a0
3
ssh://brewery@pls.lobotomise.me:33/atagen/macltok.git
src/upload.rsRust182 lines6.3 KB
1
use std::time::Duration;
2
3
use matrix_sdk::{
4
    attachment::{
5
        AttachmentConfig, AttachmentInfo, BaseAudioInfo, BaseFileInfo, BaseImageInfo,
6
        BaseVideoInfo, Thumbnail,
7
    },
8
    ruma::events::{Mentions, room::message::TextMessageEventContent},
9
};
10
11
use crate::{AttachmentUpload, Error, ErrorKind, MessageMentions, RichText, UploadDetails};
12
13
pub(crate) fn sdk_mentions(mentions: MessageMentions) -> Mentions {
14
    let mut sdk = Mentions::with_user_ids(mentions.user_ids);
15
    sdk.room = mentions.room;
16
    sdk
17
}
18
19
fn caption_content(caption: RichText) -> TextMessageEventContent {
20
    match caption.formatted_body {
21
        Some(formatted_body) => TextMessageEventContent::html(caption.body, formatted_body),
22
        None => TextMessageEventContent::plain(caption.body),
23
    }
24
}
25
26
pub(crate) fn prepare_upload(
27
    mut upload: AttachmentUpload,
28
    max_media_bytes: usize,
29
) -> Result<(AttachmentUpload, mime::Mime, AttachmentConfig), Error> {
30
    if upload.bytes.len() > max_media_bytes {
31
        return Err(Error::new(
32
            ErrorKind::MediaTooLarge,
33
            "attachment exceeds the configured byte limit",
34
        ));
35
    }
36
    let mimetype: mime::Mime = upload
37
        .mimetype
38
        .parse()
39
        .map_err(|_| Error::new(ErrorKind::Invalid, "media MIME type is invalid"))?;
40
41
    let size = ruma::UInt::try_from(upload.bytes.len()).map_err(|_| {
42
        Error::new(
43
            ErrorKind::MediaTooLarge,
44
            "attachment size is not representable",
45
        )
46
    })?;
47
    let info = match upload.details.as_ref() {
48
        Some(UploadDetails::Image {
49
            width,
50
            height,
51
            blurhash,
52
        }) if mimetype.type_() == mime::IMAGE => AttachmentInfo::Image(BaseImageInfo {
53
            width: width.map(Into::into),
54
            height: height.map(Into::into),
55
            size: Some(size),
56
            blurhash: blurhash.clone(),
57
            ..Default::default()
58
        }),
59
        Some(UploadDetails::Video {
60
            width,
61
            height,
62
            duration_ms,
63
            blurhash,
64
        }) if mimetype.type_() == mime::VIDEO => AttachmentInfo::Video(BaseVideoInfo {
65
            width: width.map(Into::into),
66
            height: height.map(Into::into),
67
            duration: duration_ms.map(Duration::from_millis),
68
            size: Some(size),
69
            blurhash: blurhash.clone(),
70
        }),
71
        Some(UploadDetails::Audio { duration_ms }) if mimetype.type_() == mime::AUDIO => {
72
            AttachmentInfo::Audio(BaseAudioInfo {
73
                duration: duration_ms.map(Duration::from_millis),
74
                size: Some(size),
75
                ..Default::default()
76
            })
77
        }
78
        Some(UploadDetails::Voice {
79
            duration_ms,
80
            waveform,
81
        }) if mimetype.type_() == mime::AUDIO => {
82
            if waveform.iter().any(|sample| *sample > 1024) {
83
                return Err(Error::new(
84
                    ErrorKind::Invalid,
85
                    "voice waveform samples must be at most 1024",
86
                ));
87
            }
88
            AttachmentInfo::Voice(BaseAudioInfo {
89
                duration: Some(Duration::from_millis(*duration_ms)),
90
                size: Some(size),
91
                waveform: Some(
92
                    waveform
93
                        .iter()
94
                        .map(|sample| f32::from(*sample) / 1024.0)
95
                        .collect(),
96
                ),
97
            })
98
        }
99
        Some(UploadDetails::File)
100
            if mimetype.type_() != mime::IMAGE
101
                && mimetype.type_() != mime::VIDEO
102
                && mimetype.type_() != mime::AUDIO =>
103
        {
104
            AttachmentInfo::File(BaseFileInfo { size: Some(size) })
105
        }
106
        None => match mimetype.type_() {
107
            mime::IMAGE => AttachmentInfo::Image(BaseImageInfo {
108
                size: Some(size),
109
                ..Default::default()
110
            }),
111
            mime::VIDEO => AttachmentInfo::Video(BaseVideoInfo {
112
                size: Some(size),
113
                ..Default::default()
114
            }),
115
            mime::AUDIO => AttachmentInfo::Audio(BaseAudioInfo {
116
                size: Some(size),
117
                ..Default::default()
118
            }),
119
            _ => AttachmentInfo::File(BaseFileInfo { size: Some(size) }),
120
        },
121
        _ => {
122
            return Err(Error::new(
123
                ErrorKind::Invalid,
124
                "attachment metadata does not match its MIME type",
125
            ));
126
        }
127
    };
128
    if upload.thumbnail.is_some() && mimetype.type_() == mime::AUDIO {
129
        return Err(Error::new(
130
            ErrorKind::Invalid,
131
            "audio attachments do not support thumbnails",
132
        ));
133
    }
134
    let thumbnail = upload
135
        .thumbnail
136
        .take()
137
        .map(|thumbnail| {
138
            if thumbnail.bytes.len() > max_media_bytes
139
                || thumbnail.width == 0
140
                || thumbnail.height == 0
141
            {
142
                return Err(Error::new(
143
                    if thumbnail.bytes.len() > max_media_bytes {
144
                        ErrorKind::MediaTooLarge
145
                    } else {
146
                        ErrorKind::Invalid
147
                    },
148
                    "thumbnail size or dimensions are invalid",
149
                ));
150
            }
151
            let content_type: mime::Mime = thumbnail
152
                .mimetype
153
                .parse()
154
                .map_err(|_| Error::new(ErrorKind::Invalid, "thumbnail MIME type is invalid"))?;
155
            if content_type.type_() != mime::IMAGE {
156
                return Err(Error::new(
157
                    ErrorKind::Invalid,
158
                    "thumbnail MIME type must be an image",
159
                ));
160
            }
161
            let size = ruma::UInt::try_from(thumbnail.bytes.len()).map_err(|_| {
162
                Error::new(
163
                    ErrorKind::MediaTooLarge,
164
                    "thumbnail size is not representable",
165
                )
166
            })?;
167
            Ok(Thumbnail {
168
                data: thumbnail.bytes,
169
                content_type,
170
                width: thumbnail.width.into(),
171
                height: thumbnail.height.into(),
172
                size,
173
            })
174
        })
175
        .transpose()?;
176
    let config = AttachmentConfig::new()
177
        .info(info)
178
        .thumbnail(thumbnail)
179
        .caption(upload.caption.take().map(caption_content))
180
        .mentions(Some(sdk_mentions(std::mem::take(&mut upload.mentions))));
181
    Ok((upload, mimetype, config))
182
}