feat: download media and thumbnails over federation
This commit is contained in:
		
							parent
							
								
									4e44fedbcd
								
							
						
					
					
						commit
						aa5e9e607e
					
				
					 2 changed files with 90 additions and 7 deletions
				
			
		|  | @ -1,5 +1,7 @@ | ||||||
| use super::State; | use super::State; | ||||||
| use crate::{database::media::FileMeta, utils, ConduitResult, Database, Error, Ruma}; | use crate::{ | ||||||
|  |     database::media::FileMeta, server_server, utils, ConduitResult, Database, Error, Ruma, | ||||||
|  | }; | ||||||
| use ruma::api::client::{ | use ruma::api::client::{ | ||||||
|     error::ErrorKind, |     error::ErrorKind, | ||||||
|     r0::media::{create_content, get_content, get_content_thumbnail, get_media_config}, |     r0::media::{create_content, get_content, get_content_thumbnail, get_media_config}, | ||||||
|  | @ -35,7 +37,7 @@ pub fn create_content_route( | ||||||
|         utils::random_string(MXC_LENGTH) |         utils::random_string(MXC_LENGTH) | ||||||
|     ); |     ); | ||||||
|     db.media |     db.media | ||||||
|         .create(mxc.clone(), &body.filename, &body.content_type, &body.file)?; |         .create(mxc.clone(), &body.filename.as_deref(), &body.content_type, &body.file)?; | ||||||
| 
 | 
 | ||||||
|     Ok(create_content::Response { content_uri: mxc }.into()) |     Ok(create_content::Response { content_uri: mxc }.into()) | ||||||
| } | } | ||||||
|  | @ -47,19 +49,25 @@ pub fn create_content_route( | ||||||
|         data = "<body>" |         data = "<body>" | ||||||
|     ) |     ) | ||||||
| )] | )] | ||||||
| pub fn get_content_route( | pub async fn get_content_route( | ||||||
|     db: State<'_, Database>, |     db: State<'_, Database>, | ||||||
|     body: Ruma<get_content::Request<'_>>, |     body: Ruma<get_content::Request<'_>>, | ||||||
|     _server_name: String, |     _server_name: String, | ||||||
|     _media_id: String, |     _media_id: String, | ||||||
| ) -> ConduitResult<get_content::Response> { | ) -> ConduitResult<get_content::Response> { | ||||||
|  |         let mxc = format!( | ||||||
|  |             "mxc://{}/{}", | ||||||
|  |             db.globals.server_name(), | ||||||
|  |             utils::random_string(MXC_LENGTH) | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|     if let Some(FileMeta { |     if let Some(FileMeta { | ||||||
|         filename, |         filename, | ||||||
|         content_type, |         content_type, | ||||||
|         file, |         file, | ||||||
|     }) = db |     }) = db | ||||||
|         .media |         .media | ||||||
|         .get(format!("mxc://{}/{}", body.server_name, body.media_id))? |         .get(&mxc)? | ||||||
|     { |     { | ||||||
|         Ok(get_content::Response { |         Ok(get_content::Response { | ||||||
|             file, |             file, | ||||||
|  | @ -67,6 +75,26 @@ pub fn get_content_route( | ||||||
|             content_disposition: filename.unwrap_or_default(), // TODO: Spec says this should be optional
 |             content_disposition: filename.unwrap_or_default(), // TODO: Spec says this should be optional
 | ||||||
|         } |         } | ||||||
|         .into()) |         .into()) | ||||||
|  |     } else if body.allow_remote { | ||||||
|  |         let get_content_response = server_server::send_request( | ||||||
|  |             &db, | ||||||
|  |             body.server_name.as_ref(), | ||||||
|  |             get_content::Request { | ||||||
|  |                 allow_remote: false, | ||||||
|  |                 server_name: &body.server_name, | ||||||
|  |                 media_id: &body.media_id, | ||||||
|  |             }, | ||||||
|  |         ) | ||||||
|  |         .await?; | ||||||
|  | 
 | ||||||
|  |         db.media.create( | ||||||
|  |             mxc, | ||||||
|  |             &Some(&get_content_response.content_disposition), | ||||||
|  |             &get_content_response.content_type, | ||||||
|  |             &get_content_response.file, | ||||||
|  |         )?; | ||||||
|  | 
 | ||||||
|  |         Ok(get_content_response.into()) | ||||||
|     } else { |     } else { | ||||||
|         Err(Error::BadRequest(ErrorKind::NotFound, "Media not found.")) |         Err(Error::BadRequest(ErrorKind::NotFound, "Media not found.")) | ||||||
|     } |     } | ||||||
|  | @ -79,7 +107,7 @@ pub fn get_content_route( | ||||||
|         data = "<body>" |         data = "<body>" | ||||||
|     ) |     ) | ||||||
| )] | )] | ||||||
| pub fn get_content_thumbnail_route( | pub async fn get_content_thumbnail_route( | ||||||
|     db: State<'_, Database>, |     db: State<'_, Database>, | ||||||
|     body: Ruma<get_content_thumbnail::Request<'_>>, |     body: Ruma<get_content_thumbnail::Request<'_>>, | ||||||
|     _server_name: String, |     _server_name: String, | ||||||
|  | @ -97,6 +125,37 @@ pub fn get_content_thumbnail_route( | ||||||
|             .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid."))?, |             .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid."))?, | ||||||
|     )? { |     )? { | ||||||
|         Ok(get_content_thumbnail::Response { file, content_type }.into()) |         Ok(get_content_thumbnail::Response { file, content_type }.into()) | ||||||
|  |     } else if body.allow_remote { | ||||||
|  |         let get_thumbnail_response = server_server::send_request( | ||||||
|  |             &db, | ||||||
|  |             body.server_name.as_ref(), | ||||||
|  |             get_content_thumbnail::Request { | ||||||
|  |                 allow_remote: false, | ||||||
|  |                 height: body.height, | ||||||
|  |                 width: body.width, | ||||||
|  |                 method: body.method, | ||||||
|  |                 server_name: &body.server_name, | ||||||
|  |                 media_id: &body.media_id, | ||||||
|  |             }, | ||||||
|  |         ) | ||||||
|  |         .await?; | ||||||
|  | 
 | ||||||
|  |         let mxc = format!( | ||||||
|  |             "mxc://{}/{}", | ||||||
|  |             db.globals.server_name(), | ||||||
|  |             utils::random_string(MXC_LENGTH) | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         db.media.upload_thumbnail( | ||||||
|  |             mxc, | ||||||
|  |             &None, | ||||||
|  |             &get_thumbnail_response.content_type, | ||||||
|  |             body.width.try_into().expect("all UInts are valid u32s"), | ||||||
|  |             body.height.try_into().expect("all UInts are valid u32s"), | ||||||
|  |             &get_thumbnail_response.file, | ||||||
|  |         )?; | ||||||
|  | 
 | ||||||
|  |         Ok(get_thumbnail_response.into()) | ||||||
|     } else { |     } else { | ||||||
|         Err(Error::BadRequest(ErrorKind::NotFound, "Media not found.")) |         Err(Error::BadRequest(ErrorKind::NotFound, "Media not found.")) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -16,7 +16,7 @@ impl Media { | ||||||
|     pub fn create( |     pub fn create( | ||||||
|         &self, |         &self, | ||||||
|         mxc: String, |         mxc: String, | ||||||
|         filename: &Option<String>, |         filename: &Option<&str>, | ||||||
|         content_type: &str, |         content_type: &str, | ||||||
|         file: &[u8], |         file: &[u8], | ||||||
|     ) -> Result<()> { |     ) -> Result<()> { | ||||||
|  | @ -34,8 +34,32 @@ impl Media { | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /// Uploads or replaces a file thumbnail.
 | ||||||
|  |     pub fn upload_thumbnail( | ||||||
|  |         &self, | ||||||
|  |         mxc: String, | ||||||
|  |         filename: &Option<String>, | ||||||
|  |         content_type: &str, | ||||||
|  |         width: u32, | ||||||
|  |         height: u32, | ||||||
|  |         file: &[u8], | ||||||
|  |     ) -> Result<()> { | ||||||
|  |         let mut key = mxc.as_bytes().to_vec(); | ||||||
|  |         key.push(0xff); | ||||||
|  |         key.extend_from_slice(&width.to_be_bytes()); | ||||||
|  |         key.extend_from_slice(&height.to_be_bytes()); | ||||||
|  |         key.push(0xff); | ||||||
|  |         key.extend_from_slice(filename.as_ref().map(|f| f.as_bytes()).unwrap_or_default()); | ||||||
|  |         key.push(0xff); | ||||||
|  |         key.extend_from_slice(content_type.as_bytes()); | ||||||
|  | 
 | ||||||
|  |         self.mediaid_file.insert(key, file)?; | ||||||
|  | 
 | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /// Downloads a file.
 |     /// Downloads a file.
 | ||||||
|     pub fn get(&self, mxc: String) -> Result<Option<FileMeta>> { |     pub fn get(&self, mxc: &str) -> Result<Option<FileMeta>> { | ||||||
|         let mut prefix = mxc.as_bytes().to_vec(); |         let mut prefix = mxc.as_bytes().to_vec(); | ||||||
|         prefix.push(0xff); |         prefix.push(0xff); | ||||||
|         prefix.extend_from_slice(&0_u32.to_be_bytes()); // Width = 0 if it's not a thumbnail
 |         prefix.extend_from_slice(&0_u32.to_be_bytes()); // Width = 0 if it's not a thumbnail
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue