atagen

macltok

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