Skip to content

Commit

Permalink
feat(media)!: optionally cache a media after upload
Browse files Browse the repository at this point in the history
Changelog: Uploaded medias can now be cached in multiple
attachment-related methods like `Room::send_attachment`.
  • Loading branch information
bnjbvr committed Oct 22, 2024
1 parent 08152bd commit 92de0e2
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 30 deletions.
65 changes: 55 additions & 10 deletions bindings/matrix-sdk-ffi/src/timeline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,18 @@ impl Timeline {
mime_type: Option<String>,
attachment_config: AttachmentConfig,
progress_watcher: Option<Box<dyn ProgressWatcher>>,
store_in_cache: bool,
) -> Result<(), RoomError> {
let mime_str = mime_type.as_ref().ok_or(RoomError::InvalidAttachmentMimeType)?;
let mime_type =
mime_str.parse::<Mime>().map_err(|_| RoomError::InvalidAttachmentMimeType)?;

let request = self.inner.send_attachment(filename, mime_type, attachment_config);
let mut request = self.inner.send_attachment(filename, mime_type, attachment_config);

if store_in_cache {
request.store_in_cache();
}

if let Some(progress_watcher) = progress_watcher {
let mut subscriber = request.subscribe_to_send_progress();
RUNTIME.spawn(async move {
Expand Down Expand Up @@ -270,6 +276,7 @@ impl Timeline {
}
}

#[allow(clippy::too_many_arguments)]
pub fn send_image(
self: Arc<Self>,
url: String,
Expand All @@ -278,6 +285,7 @@ impl Timeline {
caption: Option<String>,
formatted_caption: Option<FormattedBody>,
progress_watcher: Option<Box<dyn ProgressWatcher>>,
store_in_cache: bool,
) -> Arc<SendAttachmentJoinHandle> {
SendAttachmentJoinHandle::new(RUNTIME.spawn(async move {
let base_image_info = BaseImageInfo::try_from(&image_info)
Expand All @@ -289,11 +297,18 @@ impl Timeline {
.caption(caption)
.formatted_caption(formatted_caption.map(Into::into));

self.send_attachment(url, image_info.mimetype, attachment_config, progress_watcher)
.await
self.send_attachment(
url,
image_info.mimetype,
attachment_config,
progress_watcher,
store_in_cache,
)
.await
}))
}

#[allow(clippy::too_many_arguments)]
pub fn send_video(
self: Arc<Self>,
url: String,
Expand All @@ -302,6 +317,7 @@ impl Timeline {
caption: Option<String>,
formatted_caption: Option<FormattedBody>,
progress_watcher: Option<Box<dyn ProgressWatcher>>,
store_in_cache: bool,
) -> Arc<SendAttachmentJoinHandle> {
SendAttachmentJoinHandle::new(RUNTIME.spawn(async move {
let base_video_info: BaseVideoInfo = BaseVideoInfo::try_from(&video_info)
Expand All @@ -313,8 +329,14 @@ impl Timeline {
.caption(caption)
.formatted_caption(formatted_caption.map(Into::into));

self.send_attachment(url, video_info.mimetype, attachment_config, progress_watcher)
.await
self.send_attachment(
url,
video_info.mimetype,
attachment_config,
progress_watcher,
store_in_cache,
)
.await
}))
}

Expand All @@ -325,6 +347,7 @@ impl Timeline {
caption: Option<String>,
formatted_caption: Option<FormattedBody>,
progress_watcher: Option<Box<dyn ProgressWatcher>>,
store_in_cache: bool,
) -> Arc<SendAttachmentJoinHandle> {
SendAttachmentJoinHandle::new(RUNTIME.spawn(async move {
let base_audio_info: BaseAudioInfo = BaseAudioInfo::try_from(&audio_info)
Expand All @@ -336,11 +359,18 @@ impl Timeline {
.caption(caption)
.formatted_caption(formatted_caption.map(Into::into));

self.send_attachment(url, audio_info.mimetype, attachment_config, progress_watcher)
.await
self.send_attachment(
url,
audio_info.mimetype,
attachment_config,
progress_watcher,
store_in_cache,
)
.await
}))
}

#[allow(clippy::too_many_arguments)]
pub fn send_voice_message(
self: Arc<Self>,
url: String,
Expand All @@ -349,6 +379,7 @@ impl Timeline {
caption: Option<String>,
formatted_caption: Option<FormattedBody>,
progress_watcher: Option<Box<dyn ProgressWatcher>>,
store_in_cache: bool,
) -> Arc<SendAttachmentJoinHandle> {
SendAttachmentJoinHandle::new(RUNTIME.spawn(async move {
let base_audio_info: BaseAudioInfo = BaseAudioInfo::try_from(&audio_info)
Expand All @@ -361,8 +392,14 @@ impl Timeline {
.caption(caption)
.formatted_caption(formatted_caption.map(Into::into));

self.send_attachment(url, audio_info.mimetype, attachment_config, progress_watcher)
.await
self.send_attachment(
url,
audio_info.mimetype,
attachment_config,
progress_watcher,
store_in_cache,
)
.await
}))
}

Expand All @@ -371,6 +408,7 @@ impl Timeline {
url: String,
file_info: FileInfo,
progress_watcher: Option<Box<dyn ProgressWatcher>>,
store_in_cache: bool,
) -> Arc<SendAttachmentJoinHandle> {
SendAttachmentJoinHandle::new(RUNTIME.spawn(async move {
let base_file_info: BaseFileInfo =
Expand All @@ -379,7 +417,14 @@ impl Timeline {

let attachment_config = AttachmentConfig::new().info(attachment_info);

self.send_attachment(url, file_info.mimetype, attachment_config, progress_watcher).await
self.send_attachment(
url,
file_info.mimetype,
attachment_config,
progress_watcher,
store_in_cache,
)
.await
}))
}

Expand Down
26 changes: 21 additions & 5 deletions crates/matrix-sdk-ui/src/timeline/futures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub struct SendAttachment<'a> {
config: AttachmentConfig,
tracing_span: Span,
pub(crate) send_progress: SharedObservable<TransmissionProgress>,
store_in_cache: bool,
}

impl<'a> SendAttachment<'a> {
Expand All @@ -31,6 +32,7 @@ impl<'a> SendAttachment<'a> {
config,
tracing_span: Span::current(),
send_progress: Default::default(),
store_in_cache: false,
}
}

Expand All @@ -40,14 +42,24 @@ impl<'a> SendAttachment<'a> {
pub fn subscribe_to_send_progress(&self) -> Subscriber<TransmissionProgress> {
self.send_progress.subscribe()
}

/// Whether the sent attachment should be stored in the cache or not.
///
/// If set to true, then retrieving the data for the attachment will result
/// in a cache hit immediately after upload.
pub fn store_in_cache(&mut self) {
self.store_in_cache = true;
}
}

impl<'a> IntoFuture for SendAttachment<'a> {
type Output = Result<(), Error>;
boxed_into_future!(extra_bounds: 'a);

fn into_future(self) -> Self::IntoFuture {
let Self { timeline, path, mime_type, config, tracing_span, send_progress } = self;
let Self { timeline, path, mime_type, config, tracing_span, send_progress, store_in_cache } =
self;

let fut = async move {
let filename = path
.file_name()
Expand All @@ -56,12 +68,16 @@ impl<'a> IntoFuture for SendAttachment<'a> {
.ok_or(Error::InvalidAttachmentFileName)?;
let data = fs::read(&path).map_err(|_| Error::InvalidAttachmentData)?;

timeline
let mut fut = timeline
.room()
.send_attachment(filename, &mime_type, data, config)
.with_send_progress_observable(send_progress)
.await
.map_err(|_| Error::FailedSendingAttachment)?;
.with_send_progress_observable(send_progress);

if store_in_cache {
fut = fut.store_in_cache();
}

fut.await.map_err(|_| Error::FailedSendingAttachment)?;

Ok(())
};
Expand Down
33 changes: 30 additions & 3 deletions crates/matrix-sdk/src/room/futures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ pub struct SendAttachment<'a> {
config: AttachmentConfig,
tracing_span: Span,
send_progress: SharedObservable<TransmissionProgress>,
store_in_cache: bool,
}

impl<'a> SendAttachment<'a> {
Expand All @@ -264,6 +265,7 @@ impl<'a> SendAttachment<'a> {
config,
tracing_span: Span::current(),
send_progress: Default::default(),
store_in_cache: false,
}
}

Expand All @@ -277,17 +279,42 @@ impl<'a> SendAttachment<'a> {
self.send_progress = send_progress;
self
}

/// Whether the sent attachment should be stored in the cache or not.
///
/// If set to true, then retrieving the data for the attachment will result
/// in a cache hit immediately after upload.
pub fn store_in_cache(mut self) -> Self {
self.store_in_cache = true;
self
}
}

impl<'a> IntoFuture for SendAttachment<'a> {
type Output = Result<send_message_event::v3::Response>;
boxed_into_future!(extra_bounds: 'a);

fn into_future(self) -> Self::IntoFuture {
let Self { room, filename, content_type, data, config, tracing_span, send_progress } = self;
let Self {
room,
filename,
content_type,
data,
config,
tracing_span,
send_progress,
store_in_cache,
} = self;
let fut = async move {
room.prepare_and_send_attachment(filename, content_type, data, config, send_progress)
.await
room.prepare_and_send_attachment(
filename,
content_type,
data,
config,
send_progress,
store_in_cache,
)
.await
};

Box::pin(fut.instrument(tracing_span))
Expand Down
34 changes: 24 additions & 10 deletions crates/matrix-sdk/src/room/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1911,26 +1911,35 @@ impl Room {
/// media.
///
/// * `config` - Metadata and configuration for the attachment.
///
/// * `send_progress` - An observable to transmit forward progress about the
/// upload.
///
/// * `store_in_cache` - A boolean defining whether the uploaded media will
/// be stored in the cache immediately after a successful upload.
pub(super) async fn prepare_and_send_attachment<'a>(
&'a self,
filename: &'a str,
content_type: &'a Mime,
data: Vec<u8>,
mut config: AttachmentConfig,
send_progress: SharedObservable<TransmissionProgress>,
store_in_cache: bool,
) -> Result<send_message_event::v3::Response> {
self.ensure_room_joined()?;

let txn_id = config.txn_id.take();
let mentions = config.mentions.take();

let thumbnail = config.thumbnail.take();

#[cfg(feature = "e2e-encryption")]
let (media_source, thumbnail_source, thumbnail_info) = if self.is_encrypted().await? {
self.client
.upload_encrypted_media_and_thumbnail(
content_type,
data,
config.thumbnail.take(),
data.clone(),
thumbnail,
send_progress,
)
.await?
Expand All @@ -1939,8 +1948,8 @@ impl Room {
.media()
.upload_plain_media_and_thumbnail(
content_type,
data,
config.thumbnail.take(),
data.clone(),
thumbnail,
send_progress,
)
.await?
Expand All @@ -1950,14 +1959,19 @@ impl Room {
let (media_source, thumbnail_source, thumbnail_info) = self
.client
.media()
.upload_plain_media_and_thumbnail(
content_type,
data,
config.thumbnail.take(),
send_progress,
)
.upload_plain_media_and_thumbnail(content_type, data.clone(), thumbnail, send_progress)
.await?;

if store_in_cache {
let cache_store = self.client.event_cache_store();

let request = MediaRequest { source: media_source.clone(), format: MediaFormat::File };
// This shouldn't prevent the whole process from finishing properly.
if let Err(err) = cache_store.add_media_content(&request, data).await {
warn!("unable to cache the media after uploading it: {err}");
}
}

let msg_type = self.make_attachment_message(
content_type,
media_source,
Expand Down
Loading

0 comments on commit 92de0e2

Please sign in to comment.