atagen

macltok

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

Blame: src/normalise/media.rs

96fdf90aacd6atagen2026-09-08 08:24 UTC1
use std::sync::atomic::{AtomicU64, Ordering};
96fdf90aacd6atagen2026-09-08 08:24 UTC2
96fdf90aacd6atagen2026-09-08 08:24 UTC3
use matrix_sdk::{media::MediaEventContent, ruma::events::room::MediaSource};
96fdf90aacd6atagen2026-09-08 08:24 UTC4
use matrix_sdk_ui::timeline::{MsgLikeKind, TimelineItem as SdkTimelineItem, TimelineItemContent};
96fdf90aacd6atagen2026-09-08 08:24 UTC5
use ruma::events::{
96fdf90aacd6atagen2026-09-08 08:24 UTC6
    room::{
96fdf90aacd6atagen2026-09-08 08:24 UTC7
        ImageInfo, ThumbnailInfo,
96fdf90aacd6atagen2026-09-08 08:24 UTC8
        message::{FileInfo, MessageType, VideoInfo},
96fdf90aacd6atagen2026-09-08 08:24 UTC9
    },
96fdf90aacd6atagen2026-09-08 08:24 UTC10
    sticker::StickerEventContent,
96fdf90aacd6atagen2026-09-08 08:24 UTC11
};
96fdf90aacd6atagen2026-09-08 08:24 UTC12
96fdf90aacd6atagen2026-09-08 08:24 UTC13
use crate::{MediaDescriptor, MediaId, MediaKind};
96fdf90aacd6atagen2026-09-08 08:24 UTC14
96fdf90aacd6atagen2026-09-08 08:24 UTC15
static NEXT_MEDIA_SCOPE: AtomicU64 = AtomicU64::new(1);
96fdf90aacd6atagen2026-09-08 08:24 UTC16
96fdf90aacd6atagen2026-09-08 08:24 UTC17
/// Per-timeline-session nonce namespacing recycled SDK unique IDs.
96fdf90aacd6atagen2026-09-08 08:24 UTC18
///
96fdf90aacd6atagen2026-09-08 08:24 UTC19
/// SDK 0.18 recycles [`SdkTimelineItem::unique_id`] across the local-to-remote
96fdf90aacd6atagen2026-09-08 08:24 UTC20
/// echo replacement, while transaction IDs disappear. Scoping the recycled ID
96fdf90aacd6atagen2026-09-08 08:24 UTC21
/// by a process-wide nonce keeps media IDs stable across replacement without
96fdf90aacd6atagen2026-09-08 08:24 UTC22
/// colliding across rooms, threads, or reopened sessions.
96fdf90aacd6atagen2026-09-08 08:24 UTC23
#[derive(Clone, Debug)]
96fdf90aacd6atagen2026-09-08 08:24 UTC24
pub(crate) struct MediaScope {
96fdf90aacd6atagen2026-09-08 08:24 UTC25
    nonce: u64,
96fdf90aacd6atagen2026-09-08 08:24 UTC26
}
96fdf90aacd6atagen2026-09-08 08:24 UTC27
96fdf90aacd6atagen2026-09-08 08:24 UTC28
impl MediaScope {
96fdf90aacd6atagen2026-09-08 08:24 UTC29
    pub(crate) fn new() -> Self {
96fdf90aacd6atagen2026-09-08 08:24 UTC30
        Self {
96fdf90aacd6atagen2026-09-08 08:24 UTC31
            nonce: NEXT_MEDIA_SCOPE.fetch_add(1, Ordering::Relaxed),
96fdf90aacd6atagen2026-09-08 08:24 UTC32
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC33
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC34
}
96fdf90aacd6atagen2026-09-08 08:24 UTC35
96fdf90aacd6atagen2026-09-08 08:24 UTC36
#[derive(Clone)]
96fdf90aacd6atagen2026-09-08 08:24 UTC37
pub(crate) struct MediaRegistration {
96fdf90aacd6atagen2026-09-08 08:24 UTC38
    pub(crate) id: MediaId,
96fdf90aacd6atagen2026-09-08 08:24 UTC39
    pub(crate) kind: MediaKind,
96fdf90aacd6atagen2026-09-08 08:24 UTC40
    pub(crate) full: MediaSourceInfo,
96fdf90aacd6atagen2026-09-08 08:24 UTC41
    pub(crate) thumbnail: Option<MediaSourceInfo>,
96fdf90aacd6atagen2026-09-08 08:24 UTC42
}
96fdf90aacd6atagen2026-09-08 08:24 UTC43
96fdf90aacd6atagen2026-09-08 08:24 UTC44
#[derive(Clone)]
96fdf90aacd6atagen2026-09-08 08:24 UTC45
pub(crate) struct MediaSourceInfo {
96fdf90aacd6atagen2026-09-08 08:24 UTC46
    pub(crate) source: MediaSource,
96fdf90aacd6atagen2026-09-08 08:24 UTC47
    pub(crate) mimetype: Option<String>,
96fdf90aacd6atagen2026-09-08 08:24 UTC48
    pub(crate) declared_size: Option<u64>,
96fdf90aacd6atagen2026-09-08 08:24 UTC49
}
96fdf90aacd6atagen2026-09-08 08:24 UTC50
96fdf90aacd6atagen2026-09-08 08:24 UTC51
pub(super) fn media_registration_for_item(
96fdf90aacd6atagen2026-09-08 08:24 UTC52
    item: &SdkTimelineItem,
96fdf90aacd6atagen2026-09-08 08:24 UTC53
    scope: &MediaScope,
96fdf90aacd6atagen2026-09-08 08:24 UTC54
) -> Option<MediaRegistration> {
96fdf90aacd6atagen2026-09-08 08:24 UTC55
    let event = item.as_event()?;
96fdf90aacd6atagen2026-09-08 08:24 UTC56
    let identity = media_identity(item, scope);
96fdf90aacd6atagen2026-09-08 08:24 UTC57
    let content = match event.content() {
96fdf90aacd6atagen2026-09-08 08:24 UTC58
        TimelineItemContent::MsgLike(content) => content,
96fdf90aacd6atagen2026-09-08 08:24 UTC59
        _ => return None,
96fdf90aacd6atagen2026-09-08 08:24 UTC60
    };
96fdf90aacd6atagen2026-09-08 08:24 UTC61
    match &content.kind {
96fdf90aacd6atagen2026-09-08 08:24 UTC62
        MsgLikeKind::Message(message) => media_registration(&identity, message.msgtype()),
96fdf90aacd6atagen2026-09-08 08:24 UTC63
        MsgLikeKind::Sticker(sticker) => media_sticker_registration(&identity, sticker.content()),
96fdf90aacd6atagen2026-09-08 08:24 UTC64
        _ => None,
96fdf90aacd6atagen2026-09-08 08:24 UTC65
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC66
}
96fdf90aacd6atagen2026-09-08 08:24 UTC67
96fdf90aacd6atagen2026-09-08 08:24 UTC68
pub(super) fn media_identity(item: &SdkTimelineItem, scope: &MediaScope) -> String {
96fdf90aacd6atagen2026-09-08 08:24 UTC69
    format!("{}#{}", scope.nonce, item.unique_id().0)
96fdf90aacd6atagen2026-09-08 08:24 UTC70
}
96fdf90aacd6atagen2026-09-08 08:24 UTC71
96fdf90aacd6atagen2026-09-08 08:24 UTC72
pub(super) fn media_from_message(identity: &str, message: &MessageType) -> Option<MediaDescriptor> {
96fdf90aacd6atagen2026-09-08 08:24 UTC73
    let (kind, filename, caption, mimetype, size_bytes, width, height) = match message {
96fdf90aacd6atagen2026-09-08 08:24 UTC74
        MessageType::Image(content) => (
96fdf90aacd6atagen2026-09-08 08:24 UTC75
            MediaKind::Image,
96fdf90aacd6atagen2026-09-08 08:24 UTC76
            content.filename().to_owned(),
96fdf90aacd6atagen2026-09-08 08:24 UTC77
            content.caption().map(str::to_owned),
96fdf90aacd6atagen2026-09-08 08:24 UTC78
            content.info.as_ref().and_then(|info| info.mimetype.clone()),
96fdf90aacd6atagen2026-09-08 08:24 UTC79
            content
96fdf90aacd6atagen2026-09-08 08:24 UTC80
                .info
96fdf90aacd6atagen2026-09-08 08:24 UTC81
                .as_ref()
96fdf90aacd6atagen2026-09-08 08:24 UTC82
                .and_then(|info| info.size)
96fdf90aacd6atagen2026-09-08 08:24 UTC83
                .map(u64::from),
96fdf90aacd6atagen2026-09-08 08:24 UTC84
            content
96fdf90aacd6atagen2026-09-08 08:24 UTC85
                .info
96fdf90aacd6atagen2026-09-08 08:24 UTC86
                .as_ref()
96fdf90aacd6atagen2026-09-08 08:24 UTC87
                .and_then(|info| info.width)
96fdf90aacd6atagen2026-09-08 08:24 UTC88
                .map(u64::from),
96fdf90aacd6atagen2026-09-08 08:24 UTC89
            content
96fdf90aacd6atagen2026-09-08 08:24 UTC90
                .info
96fdf90aacd6atagen2026-09-08 08:24 UTC91
                .as_ref()
96fdf90aacd6atagen2026-09-08 08:24 UTC92
                .and_then(|info| info.height)
96fdf90aacd6atagen2026-09-08 08:24 UTC93
                .map(u64::from),
96fdf90aacd6atagen2026-09-08 08:24 UTC94
        ),
96fdf90aacd6atagen2026-09-08 08:24 UTC95
        MessageType::Video(content) => (
96fdf90aacd6atagen2026-09-08 08:24 UTC96
            MediaKind::Video,
96fdf90aacd6atagen2026-09-08 08:24 UTC97
            content.filename().to_owned(),
96fdf90aacd6atagen2026-09-08 08:24 UTC98
            content.caption().map(str::to_owned),
96fdf90aacd6atagen2026-09-08 08:24 UTC99
            content.info.as_ref().and_then(|info| info.mimetype.clone()),
96fdf90aacd6atagen2026-09-08 08:24 UTC100
            content
96fdf90aacd6atagen2026-09-08 08:24 UTC101
                .info
96fdf90aacd6atagen2026-09-08 08:24 UTC102
                .as_ref()
96fdf90aacd6atagen2026-09-08 08:24 UTC103
                .and_then(|info| info.size)
96fdf90aacd6atagen2026-09-08 08:24 UTC104
                .map(u64::from),
96fdf90aacd6atagen2026-09-08 08:24 UTC105
            content
96fdf90aacd6atagen2026-09-08 08:24 UTC106
                .info
96fdf90aacd6atagen2026-09-08 08:24 UTC107
                .as_ref()
96fdf90aacd6atagen2026-09-08 08:24 UTC108
                .and_then(|info| info.width)
96fdf90aacd6atagen2026-09-08 08:24 UTC109
                .map(u64::from),
96fdf90aacd6atagen2026-09-08 08:24 UTC110
            content
96fdf90aacd6atagen2026-09-08 08:24 UTC111
                .info
96fdf90aacd6atagen2026-09-08 08:24 UTC112
                .as_ref()
96fdf90aacd6atagen2026-09-08 08:24 UTC113
                .and_then(|info| info.height)
96fdf90aacd6atagen2026-09-08 08:24 UTC114
                .map(u64::from),
96fdf90aacd6atagen2026-09-08 08:24 UTC115
        ),
96fdf90aacd6atagen2026-09-08 08:24 UTC116
        MessageType::Audio(content) => (
96fdf90aacd6atagen2026-09-08 08:24 UTC117
            MediaKind::Audio,
96fdf90aacd6atagen2026-09-08 08:24 UTC118
            content.filename().to_owned(),
96fdf90aacd6atagen2026-09-08 08:24 UTC119
            content.caption().map(str::to_owned),
96fdf90aacd6atagen2026-09-08 08:24 UTC120
            content.info.as_ref().and_then(|info| info.mimetype.clone()),
96fdf90aacd6atagen2026-09-08 08:24 UTC121
            content
96fdf90aacd6atagen2026-09-08 08:24 UTC122
                .info
96fdf90aacd6atagen2026-09-08 08:24 UTC123
                .as_ref()
96fdf90aacd6atagen2026-09-08 08:24 UTC124
                .and_then(|info| info.size)
96fdf90aacd6atagen2026-09-08 08:24 UTC125
                .map(u64::from),
96fdf90aacd6atagen2026-09-08 08:24 UTC126
            None,
96fdf90aacd6atagen2026-09-08 08:24 UTC127
            None,
96fdf90aacd6atagen2026-09-08 08:24 UTC128
        ),
96fdf90aacd6atagen2026-09-08 08:24 UTC129
        MessageType::File(content) => (
96fdf90aacd6atagen2026-09-08 08:24 UTC130
            MediaKind::File,
96fdf90aacd6atagen2026-09-08 08:24 UTC131
            content.filename().to_owned(),
96fdf90aacd6atagen2026-09-08 08:24 UTC132
            content.caption().map(str::to_owned),
96fdf90aacd6atagen2026-09-08 08:24 UTC133
            content.info.as_ref().and_then(|info| info.mimetype.clone()),
96fdf90aacd6atagen2026-09-08 08:24 UTC134
            content
96fdf90aacd6atagen2026-09-08 08:24 UTC135
                .info
96fdf90aacd6atagen2026-09-08 08:24 UTC136
                .as_ref()
96fdf90aacd6atagen2026-09-08 08:24 UTC137
                .and_then(|info| info.size)
96fdf90aacd6atagen2026-09-08 08:24 UTC138
                .map(u64::from),
96fdf90aacd6atagen2026-09-08 08:24 UTC139
            None,
96fdf90aacd6atagen2026-09-08 08:24 UTC140
            None,
96fdf90aacd6atagen2026-09-08 08:24 UTC141
        ),
96fdf90aacd6atagen2026-09-08 08:24 UTC142
        _ => return None,
96fdf90aacd6atagen2026-09-08 08:24 UTC143
    };
96fdf90aacd6atagen2026-09-08 08:24 UTC144
    Some(MediaDescriptor {
96fdf90aacd6atagen2026-09-08 08:24 UTC145
        id: media_id(identity, "media"),
96fdf90aacd6atagen2026-09-08 08:24 UTC146
        kind,
96fdf90aacd6atagen2026-09-08 08:24 UTC147
        filename,
96fdf90aacd6atagen2026-09-08 08:24 UTC148
        caption,
96fdf90aacd6atagen2026-09-08 08:24 UTC149
        mimetype,
96fdf90aacd6atagen2026-09-08 08:24 UTC150
        size_bytes,
96fdf90aacd6atagen2026-09-08 08:24 UTC151
        width,
96fdf90aacd6atagen2026-09-08 08:24 UTC152
        height,
96fdf90aacd6atagen2026-09-08 08:24 UTC153
    })
96fdf90aacd6atagen2026-09-08 08:24 UTC154
}
96fdf90aacd6atagen2026-09-08 08:24 UTC155
96fdf90aacd6atagen2026-09-08 08:24 UTC156
pub(super) fn media_descriptor_for_sticker(
96fdf90aacd6atagen2026-09-08 08:24 UTC157
    identity: &str,
96fdf90aacd6atagen2026-09-08 08:24 UTC158
    content: &StickerEventContent,
96fdf90aacd6atagen2026-09-08 08:24 UTC159
) -> MediaDescriptor {
96fdf90aacd6atagen2026-09-08 08:24 UTC160
    MediaDescriptor {
96fdf90aacd6atagen2026-09-08 08:24 UTC161
        id: media_id(identity, "sticker"),
96fdf90aacd6atagen2026-09-08 08:24 UTC162
        kind: MediaKind::Sticker,
96fdf90aacd6atagen2026-09-08 08:24 UTC163
        filename: content.body.clone(),
96fdf90aacd6atagen2026-09-08 08:24 UTC164
        caption: None,
96fdf90aacd6atagen2026-09-08 08:24 UTC165
        mimetype: content.info.mimetype.clone(),
96fdf90aacd6atagen2026-09-08 08:24 UTC166
        size_bytes: content.info.size.map(u64::from),
96fdf90aacd6atagen2026-09-08 08:24 UTC167
        width: content.info.width.map(u64::from),
96fdf90aacd6atagen2026-09-08 08:24 UTC168
        height: content.info.height.map(u64::from),
96fdf90aacd6atagen2026-09-08 08:24 UTC169
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC170
}
96fdf90aacd6atagen2026-09-08 08:24 UTC171
96fdf90aacd6atagen2026-09-08 08:24 UTC172
pub(super) fn media_id(identity: &str, suffix: &str) -> MediaId {
96fdf90aacd6atagen2026-09-08 08:24 UTC173
    MediaId::new(format!("{identity}#{suffix}"))
96fdf90aacd6atagen2026-09-08 08:24 UTC174
}
96fdf90aacd6atagen2026-09-08 08:24 UTC175
96fdf90aacd6atagen2026-09-08 08:24 UTC176
fn media_sticker_registration(
96fdf90aacd6atagen2026-09-08 08:24 UTC177
    identity: &str,
96fdf90aacd6atagen2026-09-08 08:24 UTC178
    content: &StickerEventContent,
96fdf90aacd6atagen2026-09-08 08:24 UTC179
) -> Option<MediaRegistration> {
96fdf90aacd6atagen2026-09-08 08:24 UTC180
    Some(MediaRegistration {
96fdf90aacd6atagen2026-09-08 08:24 UTC181
        id: media_id(identity, "sticker"),
96fdf90aacd6atagen2026-09-08 08:24 UTC182
        kind: MediaKind::Sticker,
96fdf90aacd6atagen2026-09-08 08:24 UTC183
        full: MediaSourceInfo {
96fdf90aacd6atagen2026-09-08 08:24 UTC184
            source: content.source()?,
96fdf90aacd6atagen2026-09-08 08:24 UTC185
            mimetype: content.info.mimetype.clone(),
96fdf90aacd6atagen2026-09-08 08:24 UTC186
            declared_size: content.info.size.map(u64::from),
96fdf90aacd6atagen2026-09-08 08:24 UTC187
        },
96fdf90aacd6atagen2026-09-08 08:24 UTC188
        thumbnail: thumbnail_media_source(
96fdf90aacd6atagen2026-09-08 08:24 UTC189
            content.info.thumbnail_source.clone(),
96fdf90aacd6atagen2026-09-08 08:24 UTC190
            content.info.thumbnail_info.as_deref(),
96fdf90aacd6atagen2026-09-08 08:24 UTC191
        ),
96fdf90aacd6atagen2026-09-08 08:24 UTC192
    })
96fdf90aacd6atagen2026-09-08 08:24 UTC193
}
96fdf90aacd6atagen2026-09-08 08:24 UTC194
96fdf90aacd6atagen2026-09-08 08:24 UTC195
fn media_registration(identity: &str, message: &MessageType) -> Option<MediaRegistration> {
96fdf90aacd6atagen2026-09-08 08:24 UTC196
    match message {
96fdf90aacd6atagen2026-09-08 08:24 UTC197
        MessageType::Image(content) => media_registration_with_info(
96fdf90aacd6atagen2026-09-08 08:24 UTC198
            identity,
96fdf90aacd6atagen2026-09-08 08:24 UTC199
            MediaKind::Image,
96fdf90aacd6atagen2026-09-08 08:24 UTC200
            content.source(),
96fdf90aacd6atagen2026-09-08 08:24 UTC201
            content.info.as_deref(),
96fdf90aacd6atagen2026-09-08 08:24 UTC202
        ),
96fdf90aacd6atagen2026-09-08 08:24 UTC203
        MessageType::Video(content) => media_registration_with_info(
96fdf90aacd6atagen2026-09-08 08:24 UTC204
            identity,
96fdf90aacd6atagen2026-09-08 08:24 UTC205
            MediaKind::Video,
96fdf90aacd6atagen2026-09-08 08:24 UTC206
            content.source(),
96fdf90aacd6atagen2026-09-08 08:24 UTC207
            content.info.as_deref(),
96fdf90aacd6atagen2026-09-08 08:24 UTC208
        ),
96fdf90aacd6atagen2026-09-08 08:24 UTC209
        MessageType::File(content) => media_registration_with_info(
96fdf90aacd6atagen2026-09-08 08:24 UTC210
            identity,
96fdf90aacd6atagen2026-09-08 08:24 UTC211
            MediaKind::File,
96fdf90aacd6atagen2026-09-08 08:24 UTC212
            content.source(),
96fdf90aacd6atagen2026-09-08 08:24 UTC213
            content.info.as_deref(),
96fdf90aacd6atagen2026-09-08 08:24 UTC214
        ),
96fdf90aacd6atagen2026-09-08 08:24 UTC215
        MessageType::Audio(content) => media_registration_with_parts(
96fdf90aacd6atagen2026-09-08 08:24 UTC216
            identity,
96fdf90aacd6atagen2026-09-08 08:24 UTC217
            MediaKind::Audio,
96fdf90aacd6atagen2026-09-08 08:24 UTC218
            content.source(),
96fdf90aacd6atagen2026-09-08 08:24 UTC219
            content.info.as_ref().and_then(|info| info.mimetype.clone()),
96fdf90aacd6atagen2026-09-08 08:24 UTC220
            content
96fdf90aacd6atagen2026-09-08 08:24 UTC221
                .info
96fdf90aacd6atagen2026-09-08 08:24 UTC222
                .as_ref()
96fdf90aacd6atagen2026-09-08 08:24 UTC223
                .and_then(|info| info.size)
96fdf90aacd6atagen2026-09-08 08:24 UTC224
                .map(u64::from),
96fdf90aacd6atagen2026-09-08 08:24 UTC225
            None,
96fdf90aacd6atagen2026-09-08 08:24 UTC226
        ),
96fdf90aacd6atagen2026-09-08 08:24 UTC227
        _ => None,
96fdf90aacd6atagen2026-09-08 08:24 UTC228
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC229
}
96fdf90aacd6atagen2026-09-08 08:24 UTC230
96fdf90aacd6atagen2026-09-08 08:24 UTC231
fn media_registration_with_info<T: MediaInfo>(
96fdf90aacd6atagen2026-09-08 08:24 UTC232
    identity: &str,
96fdf90aacd6atagen2026-09-08 08:24 UTC233
    kind: MediaKind,
96fdf90aacd6atagen2026-09-08 08:24 UTC234
    source: Option<MediaSource>,
96fdf90aacd6atagen2026-09-08 08:24 UTC235
    info: Option<&T>,
96fdf90aacd6atagen2026-09-08 08:24 UTC236
) -> Option<MediaRegistration> {
96fdf90aacd6atagen2026-09-08 08:24 UTC237
    media_registration_with_parts(
96fdf90aacd6atagen2026-09-08 08:24 UTC238
        identity,
96fdf90aacd6atagen2026-09-08 08:24 UTC239
        kind,
96fdf90aacd6atagen2026-09-08 08:24 UTC240
        source,
96fdf90aacd6atagen2026-09-08 08:24 UTC241
        info.and_then(MediaInfo::mimetype),
96fdf90aacd6atagen2026-09-08 08:24 UTC242
        info.and_then(MediaInfo::declared_size),
96fdf90aacd6atagen2026-09-08 08:24 UTC243
        info.and_then(MediaInfo::thumbnail),
96fdf90aacd6atagen2026-09-08 08:24 UTC244
    )
96fdf90aacd6atagen2026-09-08 08:24 UTC245
}
96fdf90aacd6atagen2026-09-08 08:24 UTC246
96fdf90aacd6atagen2026-09-08 08:24 UTC247
fn media_registration_with_parts(
96fdf90aacd6atagen2026-09-08 08:24 UTC248
    identity: &str,
96fdf90aacd6atagen2026-09-08 08:24 UTC249
    kind: MediaKind,
96fdf90aacd6atagen2026-09-08 08:24 UTC250
    source: Option<MediaSource>,
96fdf90aacd6atagen2026-09-08 08:24 UTC251
    mimetype: Option<String>,
96fdf90aacd6atagen2026-09-08 08:24 UTC252
    declared_size: Option<u64>,
96fdf90aacd6atagen2026-09-08 08:24 UTC253
    thumbnail: Option<MediaSourceInfo>,
96fdf90aacd6atagen2026-09-08 08:24 UTC254
) -> Option<MediaRegistration> {
96fdf90aacd6atagen2026-09-08 08:24 UTC255
    Some(MediaRegistration {
96fdf90aacd6atagen2026-09-08 08:24 UTC256
        id: media_id(identity, "media"),
96fdf90aacd6atagen2026-09-08 08:24 UTC257
        kind,
96fdf90aacd6atagen2026-09-08 08:24 UTC258
        full: MediaSourceInfo {
96fdf90aacd6atagen2026-09-08 08:24 UTC259
            source: source?,
96fdf90aacd6atagen2026-09-08 08:24 UTC260
            mimetype,
96fdf90aacd6atagen2026-09-08 08:24 UTC261
            declared_size,
96fdf90aacd6atagen2026-09-08 08:24 UTC262
        },
96fdf90aacd6atagen2026-09-08 08:24 UTC263
        thumbnail,
96fdf90aacd6atagen2026-09-08 08:24 UTC264
    })
96fdf90aacd6atagen2026-09-08 08:24 UTC265
}
96fdf90aacd6atagen2026-09-08 08:24 UTC266
96fdf90aacd6atagen2026-09-08 08:24 UTC267
trait MediaInfo {
96fdf90aacd6atagen2026-09-08 08:24 UTC268
    fn mimetype(&self) -> Option<String>;
96fdf90aacd6atagen2026-09-08 08:24 UTC269
    fn declared_size(&self) -> Option<u64>;
96fdf90aacd6atagen2026-09-08 08:24 UTC270
    fn thumbnail(&self) -> Option<MediaSourceInfo>;
96fdf90aacd6atagen2026-09-08 08:24 UTC271
}
96fdf90aacd6atagen2026-09-08 08:24 UTC272
96fdf90aacd6atagen2026-09-08 08:24 UTC273
macro_rules! impl_media_info {
96fdf90aacd6atagen2026-09-08 08:24 UTC274
    ($type:ty) => {
96fdf90aacd6atagen2026-09-08 08:24 UTC275
        impl MediaInfo for $type {
96fdf90aacd6atagen2026-09-08 08:24 UTC276
            fn mimetype(&self) -> Option<String> {
96fdf90aacd6atagen2026-09-08 08:24 UTC277
                self.mimetype.clone()
96fdf90aacd6atagen2026-09-08 08:24 UTC278
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC279
96fdf90aacd6atagen2026-09-08 08:24 UTC280
            fn declared_size(&self) -> Option<u64> {
96fdf90aacd6atagen2026-09-08 08:24 UTC281
                self.size.map(u64::from)
96fdf90aacd6atagen2026-09-08 08:24 UTC282
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC283
96fdf90aacd6atagen2026-09-08 08:24 UTC284
            fn thumbnail(&self) -> Option<MediaSourceInfo> {
96fdf90aacd6atagen2026-09-08 08:24 UTC285
                thumbnail_media_source(
96fdf90aacd6atagen2026-09-08 08:24 UTC286
                    self.thumbnail_source.clone(),
96fdf90aacd6atagen2026-09-08 08:24 UTC287
                    self.thumbnail_info.as_deref(),
96fdf90aacd6atagen2026-09-08 08:24 UTC288
                )
96fdf90aacd6atagen2026-09-08 08:24 UTC289
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC290
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC291
    };
96fdf90aacd6atagen2026-09-08 08:24 UTC292
}
96fdf90aacd6atagen2026-09-08 08:24 UTC293
96fdf90aacd6atagen2026-09-08 08:24 UTC294
impl_media_info!(ImageInfo);
96fdf90aacd6atagen2026-09-08 08:24 UTC295
impl_media_info!(VideoInfo);
96fdf90aacd6atagen2026-09-08 08:24 UTC296
impl_media_info!(FileInfo);
96fdf90aacd6atagen2026-09-08 08:24 UTC297
96fdf90aacd6atagen2026-09-08 08:24 UTC298
fn thumbnail_media_source(
96fdf90aacd6atagen2026-09-08 08:24 UTC299
    source: Option<MediaSource>,
96fdf90aacd6atagen2026-09-08 08:24 UTC300
    info: Option<&ThumbnailInfo>,
96fdf90aacd6atagen2026-09-08 08:24 UTC301
) -> Option<MediaSourceInfo> {
96fdf90aacd6atagen2026-09-08 08:24 UTC302
    source.map(|source| MediaSourceInfo {
96fdf90aacd6atagen2026-09-08 08:24 UTC303
        source,
96fdf90aacd6atagen2026-09-08 08:24 UTC304
        mimetype: info.and_then(|info| info.mimetype.clone()),
96fdf90aacd6atagen2026-09-08 08:24 UTC305
        declared_size: info.and_then(|info| info.size).map(u64::from),
96fdf90aacd6atagen2026-09-08 08:24 UTC306
    })
96fdf90aacd6atagen2026-09-08 08:24 UTC307
}
96fdf90aacd6atagen2026-09-08 08:24 UTC308
96fdf90aacd6atagen2026-09-08 08:24 UTC309
#[cfg(test)]
96fdf90aacd6atagen2026-09-08 08:24 UTC310
mod tests {
96fdf90aacd6atagen2026-09-08 08:24 UTC311
    use matrix_sdk::ruma::{
96fdf90aacd6atagen2026-09-08 08:24 UTC312
        events::room::{ImageInfo, message::ImageMessageEventContent},
96fdf90aacd6atagen2026-09-08 08:24 UTC313
        mxc_uri,
96fdf90aacd6atagen2026-09-08 08:24 UTC314
    };
96fdf90aacd6atagen2026-09-08 08:24 UTC315
96fdf90aacd6atagen2026-09-08 08:24 UTC316
    use super::*;
96fdf90aacd6atagen2026-09-08 08:24 UTC317
96fdf90aacd6atagen2026-09-08 08:24 UTC318
    #[test]
96fdf90aacd6atagen2026-09-08 08:24 UTC319
    fn image_messages_keep_media_metadata() {
96fdf90aacd6atagen2026-09-08 08:24 UTC320
        let mut info = ImageInfo::new();
96fdf90aacd6atagen2026-09-08 08:24 UTC321
        info.mimetype = Some("image/png".to_owned());
96fdf90aacd6atagen2026-09-08 08:24 UTC322
        info.size = Some(65_536_u32.into());
96fdf90aacd6atagen2026-09-08 08:24 UTC323
        info.width = Some(640_u32.into());
96fdf90aacd6atagen2026-09-08 08:24 UTC324
        info.height = Some(480_u32.into());
96fdf90aacd6atagen2026-09-08 08:24 UTC325
        let content = ImageMessageEventContent::plain(
96fdf90aacd6atagen2026-09-08 08:24 UTC326
            "screen.png".to_owned(),
96fdf90aacd6atagen2026-09-08 08:24 UTC327
            mxc_uri!("mxc://example.org/screen").to_owned(),
96fdf90aacd6atagen2026-09-08 08:24 UTC328
        )
96fdf90aacd6atagen2026-09-08 08:24 UTC329
        .info(Box::new(info));
96fdf90aacd6atagen2026-09-08 08:24 UTC330
96fdf90aacd6atagen2026-09-08 08:24 UTC331
        let media = media_from_message("$event:example.org", &MessageType::Image(content)).unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC332
        assert_eq!(media.id.as_str(), "$event:example.org#media");
96fdf90aacd6atagen2026-09-08 08:24 UTC333
        assert_eq!(media.kind, MediaKind::Image);
96fdf90aacd6atagen2026-09-08 08:24 UTC334
        assert_eq!(media.filename, "screen.png");
96fdf90aacd6atagen2026-09-08 08:24 UTC335
        assert_eq!(media.caption, None);
96fdf90aacd6atagen2026-09-08 08:24 UTC336
        assert_eq!(media.mimetype.as_deref(), Some("image/png"));
96fdf90aacd6atagen2026-09-08 08:24 UTC337
        assert_eq!(media.size_bytes, Some(65_536));
96fdf90aacd6atagen2026-09-08 08:24 UTC338
        assert_eq!((media.width, media.height), (Some(640), Some(480)));
96fdf90aacd6atagen2026-09-08 08:24 UTC339
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC340
}