-
Notifications
You must be signed in to change notification settings - Fork 323
refactor: Include file name when uploading unencrypted media #5430
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c2338b7
f072fa7
cecf017
b868702
2e32c25
d665ab2
713ae36
58d5f6e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -219,7 +219,11 @@ pub enum UploadSource { | |
/// Upload source is a file on disk | ||
File { | ||
/// Path to file | ||
filename: String, | ||
path: String, | ||
|
||
/// An optional filename, if the one in the path is not the one we want | ||
/// to use for the file. | ||
filename: Option<String>, | ||
Comment on lines
+224
to
+226
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, not sure why the path's filename isn't sufficient here… |
||
}, | ||
/// Upload source is data in memory | ||
Data { | ||
|
@@ -233,7 +237,7 @@ pub enum UploadSource { | |
impl From<UploadSource> for AttachmentSource { | ||
fn from(value: UploadSource) -> Self { | ||
match value { | ||
UploadSource::File { filename } => Self::File(filename.into()), | ||
UploadSource::File { path, filename } => Self::File { path: path.into(), filename }, | ||
UploadSource::Data { bytes, filename } => Self::Data { bytes, filename }, | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -142,6 +142,9 @@ pub trait MediaEventContent { | |
/// Returns `None` if `Self` has no file. | ||
fn source(&self) -> Option<MediaSource>; | ||
|
||
/// Get the name of the uploaded file for `Self`. | ||
fn filename_or_body(&self) -> Option<String>; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this is a good API; a final user wants the filename, or the caption, but they shouldn't have to know about the Matrix Client-Server API to know how to compute one or the other. Instead, I'd recommend having both |
||
|
||
/// Get the source of the thumbnail for `Self`. | ||
/// | ||
/// Returns `None` if `Self` has no thumbnail. | ||
|
@@ -153,6 +156,10 @@ impl MediaEventContent for StickerEventContent { | |
Some(MediaSource::from(self.source.clone())) | ||
} | ||
|
||
fn filename_or_body(&self) -> Option<String> { | ||
None | ||
} | ||
|
||
fn thumbnail_source(&self) -> Option<MediaSource> { | ||
None | ||
} | ||
|
@@ -163,6 +170,14 @@ impl MediaEventContent for AudioMessageEventContent { | |
Some(self.source.clone()) | ||
} | ||
|
||
fn filename_or_body(&self) -> Option<String> { | ||
if let Some(filename) = &self.filename { | ||
Some(filename.clone()) | ||
} else { | ||
Some(self.body.clone()) | ||
} | ||
} | ||
|
||
fn thumbnail_source(&self) -> Option<MediaSource> { | ||
None | ||
} | ||
|
@@ -173,6 +188,14 @@ impl MediaEventContent for FileMessageEventContent { | |
Some(self.source.clone()) | ||
} | ||
|
||
fn filename_or_body(&self) -> Option<String> { | ||
if let Some(filename) = &self.filename { | ||
Some(filename.clone()) | ||
} else { | ||
Some(self.body.clone()) | ||
} | ||
} | ||
|
||
fn thumbnail_source(&self) -> Option<MediaSource> { | ||
self.info.as_ref()?.thumbnail_source.clone() | ||
} | ||
|
@@ -183,6 +206,14 @@ impl MediaEventContent for ImageMessageEventContent { | |
Some(self.source.clone()) | ||
} | ||
|
||
fn filename_or_body(&self) -> Option<String> { | ||
if let Some(filename) = &self.filename { | ||
Some(filename.clone()) | ||
} else { | ||
Some(self.body.clone()) | ||
} | ||
} | ||
|
||
fn thumbnail_source(&self) -> Option<MediaSource> { | ||
self.info | ||
.as_ref() | ||
|
@@ -196,6 +227,14 @@ impl MediaEventContent for VideoMessageEventContent { | |
Some(self.source.clone()) | ||
} | ||
|
||
fn filename_or_body(&self) -> Option<String> { | ||
if let Some(filename) = &self.filename { | ||
Some(filename.clone()) | ||
} else { | ||
Some(self.body.clone()) | ||
} | ||
} | ||
|
||
fn thumbnail_source(&self) -> Option<MediaSource> { | ||
self.info | ||
.as_ref() | ||
|
@@ -209,6 +248,10 @@ impl MediaEventContent for LocationMessageEventContent { | |
None | ||
} | ||
|
||
fn filename_or_body(&self) -> Option<String> { | ||
None | ||
} | ||
|
||
fn thumbnail_source(&self) -> Option<MediaSource> { | ||
self.info.as_ref()?.thumbnail_source.clone() | ||
} | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -109,6 +109,9 @@ pub enum QueuedRequestKind { | |||||
#[cfg(feature = "unstable-msc4274")] | ||||||
#[serde(default)] | ||||||
accumulated: Vec<AccumulatedSentMediaInfo>, | ||||||
|
||||||
/// The name of the file to upload, if known or public. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
filename: Option<String>, | ||||||
}, | ||||||
} | ||||||
|
||||||
|
@@ -241,6 +244,9 @@ pub enum DependentQueuedRequestKind { | |||||
#[cfg(feature = "unstable-msc4274")] | ||||||
#[serde(default = "default_parent_is_thumbnail_upload")] | ||||||
parent_is_thumbnail_upload: bool, | ||||||
|
||||||
/// The name of the file to upload, if known. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
filename: Option<String>, | ||||||
}, | ||||||
|
||||||
/// Finish an upload by updating references to the media cache and sending | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -821,21 +821,24 @@ pub enum AttachmentSource { | |
/// An attachment loaded from a file. | ||
/// | ||
/// The bytes and the filename will be read from the file at the given path. | ||
File(PathBuf), | ||
File { filename: Option<String>, path: PathBuf }, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why to require that the filename be provided in addition to the |
||
} | ||
|
||
impl AttachmentSource { | ||
/// Try to convert this attachment source into a `(bytes, filename)` tuple. | ||
pub(crate) fn try_into_bytes_and_filename(self) -> Result<(Vec<u8>, String), Error> { | ||
match self { | ||
Self::Data { bytes, filename } => Ok((bytes, filename)), | ||
Self::File(path) => { | ||
let filename = path | ||
.file_name() | ||
.ok_or(Error::InvalidAttachmentFileName)? | ||
.to_str() | ||
.ok_or(Error::InvalidAttachmentFileName)? | ||
.to_owned(); | ||
Self::File { path, filename } => { | ||
let filename = if let Some(filename) = filename { | ||
filename | ||
} else { | ||
path.file_name() | ||
.ok_or(Error::InvalidAttachmentFileName)? | ||
.to_str() | ||
.ok_or(Error::InvalidAttachmentFileName)? | ||
.to_owned() | ||
}; | ||
let bytes = fs::read(&path).map_err(|_| Error::InvalidAttachmentData)?; | ||
Ok((bytes, filename)) | ||
} | ||
|
@@ -848,7 +851,7 @@ where | |
P: Into<PathBuf>, | ||
{ | ||
fn from(value: P) -> Self { | ||
Self::File(value.into()) | ||
Self::File { path: value.into(), filename: None } | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -156,17 +156,50 @@ where | |
} | ||
} | ||
|
||
/// `IntoFuture` used to send media upload requests. It wraps another | ||
/// [`SendRequest`], checking its size will be accepted by the homeserver before | ||
/// uploading. | ||
/// `IntoFuture` used to send media upload requests. It works as a builder which | ||
/// can create a [`SendRequest`] given several configuration options, checking | ||
/// its size will be accepted by the homeserver before uploading. | ||
#[allow(missing_debug_implementations)] | ||
pub struct SendMediaUploadRequest { | ||
send_request: SendRequest<media::create_content::v3::Request>, | ||
client: Client, | ||
request: media::create_content::v3::Request, | ||
/// The [`RequestConfig`] to use for uploading the media file. | ||
pub request_config: Option<RequestConfig>, | ||
progress_observable: SharedObservable<TransmissionProgress>, | ||
} | ||
|
||
impl SendMediaUploadRequest { | ||
pub fn new(request: SendRequest<media::create_content::v3::Request>) -> Self { | ||
Self { send_request: request } | ||
/// Creates a new instance. | ||
pub fn new(client: Client, file: Vec<u8>) -> Self { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (This ctor shouldn't be public, if taking into account the change I suggested with respect to |
||
Self { | ||
client, | ||
request: media::create_content::v3::Request::new(file), | ||
request_config: Default::default(), | ||
progress_observable: SharedObservable::new(TransmissionProgress::default()), | ||
} | ||
} | ||
|
||
/// Returns the data to upload. | ||
pub fn data(&self) -> &Vec<u8> { | ||
&self.request.file | ||
} | ||
Comment on lines
+182
to
+185
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't think it should be needed? (If it is, then it should return |
||
|
||
/// Sets the content type of the media for the media upload request. | ||
pub fn with_content_type(mut self, content_type: impl Into<String>) -> Self { | ||
self.request.content_type = Some(content_type.into()); | ||
self | ||
} | ||
|
||
/// Sets the filename for the media upload request. | ||
pub fn with_filename(mut self, filename: Option<impl Into<String>>) -> Self { | ||
self.request.filename = filename.map(Into::into); | ||
self | ||
} | ||
|
||
/// Applies the provided [`RequestConfig`] to the future [`SendRequest`]. | ||
pub fn with_request_config(mut self, request_config: Option<RequestConfig>) -> Self { | ||
self.request_config = request_config; | ||
self | ||
} | ||
|
||
/// Replace the default `SharedObservable` used for tracking upload | ||
|
@@ -179,14 +212,24 @@ impl SendMediaUploadRequest { | |
mut self, | ||
send_progress: SharedObservable<TransmissionProgress>, | ||
) -> Self { | ||
self.send_request = self.send_request.with_send_progress_observable(send_progress); | ||
self.progress_observable = send_progress; | ||
self | ||
} | ||
|
||
/// Get a subscriber to observe the progress of sending the request | ||
/// body. | ||
pub fn subscribe_to_send_progress(&self) -> Subscriber<TransmissionProgress> { | ||
self.send_request.send_progress.subscribe() | ||
self.progress_observable.subscribe() | ||
} | ||
|
||
/// Creates the [`SendRequest`] using the provided parameters. | ||
pub fn build_send_request(self) -> SendRequest<media::create_content::v3::Request> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this should be |
||
SendRequest { | ||
client: self.client, | ||
request: self.request, | ||
config: self.request_config, | ||
send_progress: self.progress_observable, | ||
} | ||
} | ||
} | ||
|
||
|
@@ -195,9 +238,9 @@ impl IntoFuture for SendMediaUploadRequest { | |
boxed_into_future!(); | ||
|
||
fn into_future(self) -> Self::IntoFuture { | ||
let request_length = self.send_request.request.file.len(); | ||
let client = self.send_request.client.clone(); | ||
let send_request = self.send_request; | ||
let send_request = self.build_send_request(); | ||
let request_length = send_request.request.file.len(); | ||
let client = send_request.client.clone(); | ||
|
||
Box::pin(async move { | ||
let max_upload_size = client.load_or_fetch_max_upload_size().await?; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's surprising that
with_content_type()
doesn't take the same parameter type as the previous&mime_type
in the previous call site. Can we make it so that it does, please?