From df883d33280a7c1b72686d1519294fa78de26cbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Tue, 25 May 2021 21:52:27 +0200 Subject: [PATCH] Add MediaEventContent trait and implement it for corresponding room events Add helper methods in Client --- matrix_sdk/src/client.rs | 118 ++++++++++++++++++++++++++++++- matrix_sdk_base/src/media.rs | 132 ++++++++++++++++++++++++++++++++++- 2 files changed, 245 insertions(+), 5 deletions(-) diff --git a/matrix_sdk/src/client.rs b/matrix_sdk/src/client.rs index 6a64a18f..4a5583f3 100644 --- a/matrix_sdk/src/client.rs +++ b/matrix_sdk/src/client.rs @@ -45,7 +45,7 @@ use matrix_sdk_base::{ deserialized_responses::SyncResponse, events::AnyMessageEventContent, identifiers::MxcUri, - media::{MediaFormat, MediaRequest, MediaType}, + media::{MediaEventContent, MediaFormat, MediaRequest, MediaThumbnailSize, MediaType}, BaseClient, BaseClientConfig, SendAccessToken, Session, Store, }; use mime::{self, Mime}; @@ -2503,7 +2503,7 @@ impl Client { let content = { let mut cursor = Cursor::new(content); let mut reader = - AttachmentDecryptor::new(&mut cursor, file.clone().into())?; + AttachmentDecryptor::new(&mut cursor, file.as_ref().clone().into())?; let mut decrypted = Vec::new(); reader.read_to_end(&mut decrypted)?; @@ -2557,6 +2557,120 @@ impl Client { pub async fn remove_media_content_for_uri(&self, uri: &MxcUri) -> Result<()> { Ok(self.base_client.store().remove_media_content_for_uri(&uri).await?) } + + /// Get the file of the given media event content. + /// + /// If the content is encrypted and encryption is enabled, the content will + /// be decrypted. + /// + /// Returns `Ok(None)` if the event content has no file. + /// + /// This is a convenience method that calls the + /// [`get_media_content`](#method.get_media_content) method. + /// + /// # Arguments + /// + /// * `event_content` - The media event content. + /// + /// * `use_cache` - If we should use the media cache for this file. + pub async fn get_file( + &self, + event_content: impl MediaEventContent, + use_cache: bool, + ) -> Result>> { + if let Some(media_type) = event_content.file() { + Ok(Some( + self.get_media_content( + &MediaRequest { media_type, format: MediaFormat::File }, + use_cache, + ) + .await?, + )) + } else { + Ok(None) + } + } + + /// Remove the file of the given media event content from the cache. + /// + /// This is a convenience method that calls the + /// [`remove_media_content`](#method.remove_media_content) method. + /// + /// # Arguments + /// + /// * `event_content` - The media event content. + pub async fn remove_file(&self, event_content: impl MediaEventContent) -> Result<()> { + if let Some(media_type) = event_content.file() { + self.remove_media_content(&MediaRequest { media_type, format: MediaFormat::File }) + .await? + } + + Ok(()) + } + + /// Get a thumbnail of the given media event content. + /// + /// If the content is encrypted and encryption is enabled, the content will + /// be decrypted. + /// + /// Returns `Ok(None)` if the event content has no thumbnail. + /// + /// This is a convenience method that calls the + /// [`get_media_content`](#method.get_media_content) method. + /// + /// # Arguments + /// + /// * `event_content` - The media event content. + /// + /// * `size` - The _desired_ size of the thumbnail. The actual thumbnail may + /// not match the size specified. + /// + /// * `use_cache` - If we should use the media cache for this thumbnail. + pub async fn get_thumbnail( + &self, + event_content: impl MediaEventContent, + size: MediaThumbnailSize, + use_cache: bool, + ) -> Result>> { + if let Some(media_type) = event_content.thumbnail() { + Ok(Some( + self.get_media_content( + &MediaRequest { media_type, format: MediaFormat::Thumbnail(size) }, + use_cache, + ) + .await?, + )) + } else { + Ok(None) + } + } + + /// Remove the thumbnail of the given media event content from the cache. + /// + /// This is a convenience method that calls the + /// [`remove_media_content`](#method.remove_media_content) method. + /// + /// # Arguments + /// + /// * `event_content` - The media event content. + /// + /// * `size` - The _desired_ size of the thumbnail. Must match the size + /// requested with [`get_thumbnail`](#method.get_thumbnail). + pub async fn remove_thumbnail( + &self, + event_content: impl MediaEventContent, + size: MediaThumbnailSize, + ) -> Result<()> { + if let Some(media_type) = event_content.file() { + self.remove_media_content(&MediaRequest { + media_type, + format: MediaFormat::Thumbnail(size), + }) + .await? + } + + Ok(()) + } } #[cfg(test)] diff --git a/matrix_sdk_base/src/media.rs b/matrix_sdk_base/src/media.rs index 3e8f0939..9d075e63 100644 --- a/matrix_sdk_base/src/media.rs +++ b/matrix_sdk_base/src/media.rs @@ -1,8 +1,19 @@ //! Common types for [media content](https://matrix.org/docs/spec/client_server/r0.6.1#id66). use matrix_sdk_common::{ - api::r0::media::get_content_thumbnail::Method, events::room::EncryptedFile, - identifiers::MxcUri, UInt, + api::r0::media::get_content_thumbnail::Method, + events::{ + room::{ + message::{ + AudioMessageEventContent, FileMessageEventContent, ImageMessageEventContent, + LocationMessageEventContent, VideoMessageEventContent, + }, + EncryptedFile, + }, + sticker::StickerEventContent, + }, + identifiers::MxcUri, + UInt, }; const UNIQUE_SEPARATOR: &str = "_"; @@ -61,7 +72,7 @@ pub enum MediaType { Uri(MxcUri), /// An encrypted media content. - Encrypted(EncryptedFile), + Encrypted(Box), } impl UniqueKey for MediaType { @@ -88,3 +99,118 @@ impl UniqueKey for MediaRequest { format!("{}{}{}", self.media_type.unique_key(), UNIQUE_SEPARATOR, self.format.unique_key()) } } + +/// Trait for media event content. +pub trait MediaEventContent { + /// Get the type of the file for `Self`. + /// + /// Returns `None` if `Self` has no file. + fn file(&self) -> Option; + + /// Get the type of the thumbnail for `Self`. + /// + /// Returns `None` if `Self` has no thumbnail. + fn thumbnail(&self) -> Option; +} + +impl MediaEventContent for StickerEventContent { + fn file(&self) -> Option { + Some(MediaType::Uri(self.url.clone())) + } + + fn thumbnail(&self) -> Option { + None + } +} + +impl MediaEventContent for AudioMessageEventContent { + fn file(&self) -> Option { + self.url + .as_ref() + .map(|uri| MediaType::Uri(uri.clone())) + .or_else(|| self.file.as_ref().map(|e| MediaType::Encrypted(e.clone()))) + } + + fn thumbnail(&self) -> Option { + None + } +} + +impl MediaEventContent for FileMessageEventContent { + fn file(&self) -> Option { + self.url + .as_ref() + .map(|uri| MediaType::Uri(uri.clone())) + .or_else(|| self.file.as_ref().map(|e| MediaType::Encrypted(e.clone()))) + } + + fn thumbnail(&self) -> Option { + self.info.as_ref().and_then(|info| { + if let Some(uri) = info.thumbnail_url.as_ref() { + Some(MediaType::Uri(uri.clone())) + } else { + info.thumbnail_file.as_ref().map(|file| MediaType::Encrypted(file.clone())) + } + }) + } +} + +impl MediaEventContent for ImageMessageEventContent { + fn file(&self) -> Option { + self.url + .as_ref() + .map(|uri| MediaType::Uri(uri.clone())) + .or_else(|| self.file.as_ref().map(|e| MediaType::Encrypted(e.clone()))) + } + + fn thumbnail(&self) -> Option { + self.info + .as_ref() + .and_then(|info| { + if let Some(uri) = info.thumbnail_url.as_ref() { + Some(MediaType::Uri(uri.clone())) + } else { + info.thumbnail_file.as_ref().map(|file| MediaType::Encrypted(file.clone())) + } + }) + .or_else(|| self.url.as_ref().map(|uri| MediaType::Uri(uri.clone()))) + } +} + +impl MediaEventContent for VideoMessageEventContent { + fn file(&self) -> Option { + self.url + .as_ref() + .map(|uri| MediaType::Uri(uri.clone())) + .or_else(|| self.file.as_ref().map(|e| MediaType::Encrypted(e.clone()))) + } + + fn thumbnail(&self) -> Option { + self.info + .as_ref() + .and_then(|info| { + if let Some(uri) = info.thumbnail_url.as_ref() { + Some(MediaType::Uri(uri.clone())) + } else { + info.thumbnail_file.as_ref().map(|file| MediaType::Encrypted(file.clone())) + } + }) + .or_else(|| self.url.as_ref().map(|uri| MediaType::Uri(uri.clone()))) + } +} + +impl MediaEventContent for LocationMessageEventContent { + fn file(&self) -> Option { + None + } + + fn thumbnail(&self) -> Option { + self.info.as_ref().and_then(|info| { + if let Some(uri) = info.thumbnail_url.as_ref() { + Some(MediaType::Uri(uri.clone())) + } else { + info.thumbnail_file.as_ref().map(|file| MediaType::Encrypted(file.clone())) + } + }) + } +}