matrix_sdk_base/media/
mod.rs1pub mod store;
18
19use ruma::{
20 MxcUri, UInt,
21 api::client::media::get_content_thumbnail::v3::Method,
22 events::{
23 room::{
24 MediaSource,
25 message::{
26 AudioMessageEventContent, FileMessageEventContent, ImageMessageEventContent,
27 LocationMessageEventContent, VideoMessageEventContent,
28 },
29 },
30 sticker::StickerEventContent,
31 },
32};
33use serde::{Deserialize, Serialize};
34
35const UNIQUE_SEPARATOR: &str = "_";
36
37pub trait UniqueKey {
39 fn unique_key(&self) -> String;
42}
43
44#[derive(Clone, Debug, Serialize, Deserialize)]
46pub enum MediaFormat {
47 File,
49
50 Thumbnail(MediaThumbnailSettings),
52}
53
54impl UniqueKey for MediaFormat {
55 fn unique_key(&self) -> String {
56 match self {
57 Self::File => "file".into(),
58 Self::Thumbnail(settings) => settings.unique_key(),
59 }
60 }
61}
62
63#[derive(Clone, Debug, Serialize, Deserialize)]
65pub struct MediaThumbnailSettings {
66 pub method: Method,
68
69 pub width: UInt,
72
73 pub height: UInt,
76
77 pub animated: bool,
84}
85
86impl MediaThumbnailSettings {
87 pub fn with_method(method: Method, width: UInt, height: UInt) -> Self {
92 Self { method, width, height, animated: false }
93 }
94
95 pub fn new(width: UInt, height: UInt) -> Self {
100 Self { method: Method::Scale, width, height, animated: false }
101 }
102}
103
104impl UniqueKey for MediaThumbnailSettings {
105 fn unique_key(&self) -> String {
106 let mut key = format!("{}{UNIQUE_SEPARATOR}{}x{}", self.method, self.width, self.height);
107
108 if self.animated {
109 key.push_str(UNIQUE_SEPARATOR);
110 key.push_str("animated");
111 }
112
113 key
114 }
115}
116
117impl UniqueKey for MediaSource {
118 fn unique_key(&self) -> String {
119 match self {
120 Self::Plain(uri) => uri.to_string(),
121 Self::Encrypted(file) => file.url.to_string(),
122 }
123 }
124}
125
126#[derive(Clone, Debug, Serialize, Deserialize)]
130pub struct MediaRequestParameters {
131 pub source: MediaSource,
133
134 pub format: MediaFormat,
136}
137
138impl MediaRequestParameters {
139 pub fn uri(&self) -> &MxcUri {
141 match &self.source {
142 MediaSource::Plain(url) => url.as_ref(),
143 MediaSource::Encrypted(file) => file.url.as_ref(),
144 }
145 }
146}
147
148impl UniqueKey for MediaRequestParameters {
149 fn unique_key(&self) -> String {
150 format!("{}{UNIQUE_SEPARATOR}{}", self.source.unique_key(), self.format.unique_key())
151 }
152}
153
154pub trait MediaEventContent {
156 fn source(&self) -> Option<MediaSource>;
160
161 fn thumbnail_source(&self) -> Option<MediaSource>;
165}
166
167impl MediaEventContent for StickerEventContent {
168 fn source(&self) -> Option<MediaSource> {
169 Some(MediaSource::from(self.source.clone()))
170 }
171
172 fn thumbnail_source(&self) -> Option<MediaSource> {
173 None
174 }
175}
176
177impl MediaEventContent for AudioMessageEventContent {
178 fn source(&self) -> Option<MediaSource> {
179 Some(self.source.clone())
180 }
181
182 fn thumbnail_source(&self) -> Option<MediaSource> {
183 None
184 }
185}
186
187impl MediaEventContent for FileMessageEventContent {
188 fn source(&self) -> Option<MediaSource> {
189 Some(self.source.clone())
190 }
191
192 fn thumbnail_source(&self) -> Option<MediaSource> {
193 self.info.as_ref()?.thumbnail_source.clone()
194 }
195}
196
197impl MediaEventContent for ImageMessageEventContent {
198 fn source(&self) -> Option<MediaSource> {
199 Some(self.source.clone())
200 }
201
202 fn thumbnail_source(&self) -> Option<MediaSource> {
203 self.info
204 .as_ref()
205 .and_then(|info| info.thumbnail_source.clone())
206 .or_else(|| Some(self.source.clone()))
207 }
208}
209
210impl MediaEventContent for VideoMessageEventContent {
211 fn source(&self) -> Option<MediaSource> {
212 Some(self.source.clone())
213 }
214
215 fn thumbnail_source(&self) -> Option<MediaSource> {
216 self.info
217 .as_ref()
218 .and_then(|info| info.thumbnail_source.clone())
219 .or_else(|| Some(self.source.clone()))
220 }
221}
222
223impl MediaEventContent for LocationMessageEventContent {
224 fn source(&self) -> Option<MediaSource> {
225 None
226 }
227
228 fn thumbnail_source(&self) -> Option<MediaSource> {
229 self.info.as_ref()?.thumbnail_source.clone()
230 }
231}
232
233#[cfg(test)]
234mod tests {
235 use ruma::mxc_uri;
236 use serde_json::json;
237
238 use super::*;
239
240 #[test]
241 fn test_media_request_url() {
242 let mxc_uri = mxc_uri!("mxc://homeserver/media");
243
244 let plain = MediaRequestParameters {
245 source: MediaSource::Plain(mxc_uri.to_owned()),
246 format: MediaFormat::File,
247 };
248
249 assert_eq!(plain.uri(), mxc_uri);
250
251 let file = MediaRequestParameters {
252 source: MediaSource::Encrypted(Box::new(
253 serde_json::from_value(json!({
254 "url": mxc_uri,
255 "key": {
256 "kty": "oct",
257 "key_ops": ["encrypt", "decrypt"],
258 "alg": "A256CTR",
259 "k": "b50ACIv6LMn9AfMCFD1POJI_UAFWIclxAN1kWrEO2X8",
260 "ext": true,
261 },
262 "iv": "AK1wyzigZtQAAAABAAAAKK",
263 "hashes": {
264 "sha256": "foobar",
265 },
266 "v": "v2",
267 }))
268 .unwrap(),
269 )),
270 format: MediaFormat::File,
271 };
272
273 assert_eq!(file.uri(), mxc_uri);
274 }
275}