[fractal] API, util, dw_media: use endpoint and merge with download_file



commit 85606ffa149d513cc12cacd5cf2f569aae551ddd
Author: Alejandro Domínguez <adomu net-c com>
Date:   Tue Mar 10 12:29:14 2020 +0100

    API, util, dw_media: use endpoint and merge with download_file

 fractal-matrix-api/src/backend/directory.rs        |  8 +-
 fractal-matrix-api/src/backend/media.rs            | 23 +-----
 fractal-matrix-api/src/backend/mod.rs              |  6 +-
 fractal-matrix-api/src/backend/room.rs             | 10 +--
 fractal-matrix-api/src/backend/sync.rs             |  4 +-
 fractal-matrix-api/src/backend/types.rs            |  2 -
 fractal-matrix-api/src/backend/user.rs             | 25 ++----
 fractal-matrix-api/src/globals.rs                  |  1 -
 fractal-matrix-api/src/meson.build                 |  2 +-
 fractal-matrix-api/src/model/room.rs               |  6 +-
 fractal-matrix-api/src/r0/media.rs                 |  4 +-
 .../src/r0/media/{create.rs => create_content.rs}  |  0
 fractal-matrix-api/src/r0/media/get_content.rs     | 33 ++++++++
 .../src/r0/media/get_content_thumbnail.rs          | 37 +++++++++
 fractal-matrix-api/src/util.rs                     | 92 +++++++++-------------
 15 files changed, 140 insertions(+), 113 deletions(-)
---
diff --git a/fractal-matrix-api/src/backend/directory.rs b/fractal-matrix-api/src/backend/directory.rs
index f9796b71..0982fe7a 100644
--- a/fractal-matrix-api/src/backend/directory.rs
+++ b/fractal-matrix-api/src/backend/directory.rs
@@ -115,8 +115,12 @@ pub fn room_search(
                     .inspect(|r: &Room| {
                         if let Some(avatar) = r.avatar.clone() {
                             if let Ok(dest) = cache_dir_path(None, &r.id.to_string()) {
-                                let _ =
-                                    dw_media(&base, &avatar, ContentType::Download, Some(&dest));
+                                let _ = dw_media(
+                                    base.clone(),
+                                    &avatar,
+                                    ContentType::Download,
+                                    Some(dest),
+                                );
                             }
                         }
                     })
diff --git a/fractal-matrix-api/src/backend/media.rs b/fractal-matrix-api/src/backend/media.rs
index 6a75477c..f7996190 100644
--- a/fractal-matrix-api/src/backend/media.rs
+++ b/fractal-matrix-api/src/backend/media.rs
@@ -2,14 +2,10 @@ use crate::backend::types::Backend;
 use crate::error::Error;
 use crate::globals;
 use ruma_identifiers::RoomId;
-use std::str::Split;
 use std::sync::mpsc::Sender;
-use std::thread;
 use url::Url;
 
 use crate::r0::AccessToken;
-use crate::util::cache_dir_path;
-use crate::util::download_file;
 use crate::util::dw_media;
 use crate::util::get_prev_batch_from;
 use crate::util::semaphore;
@@ -26,14 +22,14 @@ use crate::types::Message;
 
 pub fn get_thumb_async(bk: &Backend, baseu: Url, media: String, tx: Sender<Result<String, Error>>) {
     semaphore(bk.limit_threads.clone(), move || {
-        let fname = dw_media(&baseu, &media, ContentType::default_thumbnail(), None);
+        let fname = dw_media(baseu, &media, ContentType::default_thumbnail(), None);
         tx.send(fname).expect_log("Connection closed");
     });
 }
 
 pub fn get_media_async(bk: &Backend, baseu: Url, media: String, tx: Sender<Result<String, Error>>) {
     semaphore(bk.limit_threads.clone(), move || {
-        let fname = dw_media(&baseu, &media, ContentType::Download, None);
+        let fname = dw_media(baseu, &media, ContentType::Download, None);
         tx.send(fname).expect_log("Connection closed");
     });
 }
@@ -71,21 +67,6 @@ pub fn get_media_list_async(
     });
 }
 
-pub fn get_file_async(url: Url, tx: Sender<String>) -> Result<(), Error> {
-    let name = url
-        .path_segments()
-        .and_then(Split::last)
-        .unwrap_or_default();
-    let fname = cache_dir_path(Some("files"), name)?;
-
-    thread::spawn(move || {
-        let fname = download_file(url, fname, None).unwrap_or_default();
-        tx.send(fname).expect_log("Connection closed");
-    });
-
-    Ok(())
-}
-
 fn get_room_media_list(
     baseu: Url,
     access_token: AccessToken,
diff --git a/fractal-matrix-api/src/backend/mod.rs b/fractal-matrix-api/src/backend/mod.rs
index 9d55eb69..4d905cab 100644
--- a/fractal-matrix-api/src/backend/mod.rs
+++ b/fractal-matrix-api/src/backend/mod.rs
@@ -385,15 +385,11 @@ impl Backend {
             ),
             Ok(BKCommand::GetMedia(server, media)) => {
                 thread::spawn(move || {
-                    let fname = dw_media(&server, &media, ContentType::Download, None);
+                    let fname = dw_media(server, &media, ContentType::Download, None);
                     tx.send(BKResponse::Media(fname))
                         .expect_log("Connection closed");
                 });
             }
-            Ok(BKCommand::GetFileAsync(url, ctx)) => {
-                let r = media::get_file_async(url, ctx);
-                bkerror!(r, tx, BKResponse::GetFileAsyncError);
-            }
 
             // Directory module
             Ok(BKCommand::DirectoryProtocols(server, access_token)) => {
diff --git a/fractal-matrix-api/src/backend/room.rs b/fractal-matrix-api/src/backend/room.rs
index 301cec06..39b44de4 100644
--- a/fractal-matrix-api/src/backend/room.rs
+++ b/fractal-matrix-api/src/backend/room.rs
@@ -37,9 +37,9 @@ use crate::r0::context::get_context::request as get_context;
 use crate::r0::context::get_context::Parameters as GetContextParameters;
 use crate::r0::context::get_context::Response as GetContextResponse;
 use crate::r0::filter::RoomEventFilter;
-use crate::r0::media::create::request as create_content;
-use crate::r0::media::create::Parameters as CreateContentParameters;
-use crate::r0::media::create::Response as CreateContentResponse;
+use crate::r0::media::create_content::request as create_content;
+use crate::r0::media::create_content::Parameters as CreateContentParameters;
+use crate::r0::media::create_content::Response as CreateContentResponse;
 use crate::r0::membership::invite_user::request as invite_user;
 use crate::r0::membership::invite_user::Body as InviteUserBody;
 use crate::r0::membership::invite_user::Parameters as InviteUserParameters;
@@ -154,10 +154,10 @@ pub fn get_room_avatar(
             let dest = cache_dir_path(None, &room_id.to_string()).ok();
             if let Some(ref avatar) = avatar {
                 let _ = dw_media(
-                    &base,
+                    base,
                     avatar.as_str(),
                     ContentType::default_thumbnail(),
-                    dest.as_ref().map(String::as_str),
+                    dest,
                 );
             }
 
diff --git a/fractal-matrix-api/src/backend/sync.rs b/fractal-matrix-api/src/backend/sync.rs
index d008eda7..d1bfe39a 100644
--- a/fractal-matrix-api/src/backend/sync.rs
+++ b/fractal-matrix-api/src/backend/sync.rs
@@ -113,7 +113,7 @@ pub fn sync(
                     let join = &response.rooms.join;
 
                     // New rooms
-                    let rs = Room::from_sync_response(&response, user_id.clone(), &base)
+                    let rs = Room::from_sync_response(&response, user_id.clone(), base)
                         .map_err(Into::into);
                     tx.send(BKResponse::UpdateRooms(rs))
                         .expect_log("Connection closed");
@@ -246,7 +246,7 @@ pub fn sync(
                     data.lock().unwrap().m_direct = parse_m_direct(&response.account_data.events);
 
                     let rooms_def =
-                        Room::from_sync_response(&response, user_id, &base)
+                        Room::from_sync_response(&response, user_id, base)
                             .map(|rooms| {
                                 let def =
                                     data.lock().unwrap().join_to_room.as_ref().and_then(|jtr| {
diff --git a/fractal-matrix-api/src/backend/types.rs b/fractal-matrix-api/src/backend/types.rs
index 9297b708..6f995854 100644
--- a/fractal-matrix-api/src/backend/types.rs
+++ b/fractal-matrix-api/src/backend/types.rs
@@ -53,7 +53,6 @@ pub enum BKCommand {
         Option<String>,
         Sender<(Vec<Message>, String)>,
     ),
-    GetFileAsync(Url, Sender<String>),
     GetAvatarAsync(Url, Option<Member>, Sender<String>),
     GetMedia(Url, String),
     GetUserInfoAsync(Url, UserId, Option<Sender<(String, String)>>),
@@ -137,7 +136,6 @@ pub enum BKResponse {
     GuestLoginError(Error),
     SendTypingError(Error),
     SetRoomError(Error),
-    GetFileAsyncError(Error),
     InviteError(Error),
     ChangeLanguage(Result<(), Error>),
 }
diff --git a/fractal-matrix-api/src/backend/user.rs b/fractal-matrix-api/src/backend/user.rs
index 4450169c..e01fee44 100644
--- a/fractal-matrix-api/src/backend/user.rs
+++ b/fractal-matrix-api/src/backend/user.rs
@@ -46,9 +46,9 @@ use crate::r0::contact::request_verification_token_msisdn::request as request_co
 use crate::r0::contact::request_verification_token_msisdn::Body as PhoneTokenBody;
 use crate::r0::contact::request_verification_token_msisdn::Parameters as PhoneTokenParameters;
 use crate::r0::contact::request_verification_token_msisdn::Response as PhoneTokenResponse;
-use crate::r0::media::create::request as create_content;
-use crate::r0::media::create::Parameters as CreateContentParameters;
-use crate::r0::media::create::Response as CreateContentResponse;
+use crate::r0::media::create_content::request as create_content;
+use crate::r0::media::create_content::Parameters as CreateContentParameters;
+use crate::r0::media::create_content::Response as CreateContentResponse;
 use crate::r0::profile::get_display_name::request as get_display_name;
 use crate::r0::profile::get_display_name::Response as GetDisplayNameResponse;
 use crate::r0::profile::set_avatar_url::request as set_avatar_url;
@@ -340,7 +340,7 @@ pub fn account_destruction(
 }
 
 pub fn get_avatar(base: Url, userid: UserId) -> Result<String, Error> {
-    get_user_avatar(&base, &userid).map(|(_, fname)| fname)
+    get_user_avatar(base, &userid).map(|(_, fname)| fname)
 }
 
 pub fn get_avatar_async(bk: &Backend, base: Url, member: Option<Member>, tx: Sender<String>) {
@@ -349,7 +349,7 @@ pub fn get_avatar_async(bk: &Backend, base: Url, member: Option<Member>, tx: Sen
         let avatar = member.avatar.clone().unwrap_or_default();
 
         semaphore(bk.limit_threads.clone(), move || {
-            let fname = get_user_avatar_img(&base, &uid, &avatar).unwrap_or_default();
+            let fname = get_user_avatar_img(base, &uid, &avatar).unwrap_or_default();
             tx.send(fname).expect_log("Connection closed");
         });
     } else {
@@ -418,7 +418,7 @@ pub fn get_user_info_async(
     bk.user_info_cache.insert(uid.clone(), info.clone());
 
     semaphore(bk.limit_threads.clone(), move || {
-        match (get_user_avatar(&baseu, &uid), tx) {
+        match (get_user_avatar(baseu, &uid), tx) {
             (Ok(i0), Some(tx)) => {
                 tx.send(i0.clone()).expect_log("Connection closed");
                 *info.lock().unwrap() = i0;
@@ -454,16 +454,7 @@ pub fn search(
         .map(|response| response.results.into_iter().map(Into::into).collect())
 }
 
-fn get_user_avatar_img(baseu: &Url, userid: &UserId, avatar: &str) -> Result<String, Error> {
-    if avatar.is_empty() {
-        return Ok(String::new());
-    }
-
+fn get_user_avatar_img(baseu: Url, userid: &UserId, avatar: &str) -> Result<String, Error> {
     let dest = cache_dir_path(None, &userid.to_string())?;
-    dw_media(
-        baseu,
-        &avatar,
-        ContentType::default_thumbnail(),
-        Some(&dest),
-    )
+    dw_media(baseu, avatar, ContentType::default_thumbnail(), Some(dest))
 }
diff --git a/fractal-matrix-api/src/globals.rs b/fractal-matrix-api/src/globals.rs
index e455724f..924c85df 100644
--- a/fractal-matrix-api/src/globals.rs
+++ b/fractal-matrix-api/src/globals.rs
@@ -7,7 +7,6 @@ pub static ROOM_DIRECTORY_LIMIT: i32 = 20;
 pub static DEVICE_NAME: &str = "Fractal";
 
 lazy_static! {
-    pub static ref MATRIX_RE: Regex = Regex::new(r"mxc://(?P<server>[^/]+)/(?P<media>.+)").unwrap();
     pub static ref EMAIL_RE: Regex = Regex::new(
         r"^([0-9a-zA-Z]([-\.\w]*[0-9a-zA-Z])+@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$"
     )
diff --git a/fractal-matrix-api/src/meson.build b/fractal-matrix-api/src/meson.build
index c4b1892b..99505a10 100644
--- a/fractal-matrix-api/src/meson.build
+++ b/fractal-matrix-api/src/meson.build
@@ -28,7 +28,7 @@ api_sources = files(
   'r0/contact/request_verification_token_email.rs',
   'r0/contact/request_verification_token_msisdn.rs',
   'r0/directory/post_public_rooms.rs',
-  'r0/media/create.rs',
+  'r0/media/create_content.rs',
   'r0/membership/invite_user.rs',
   'r0/membership/join_room_by_id_or_alias.rs',
   'r0/membership/leave_room.rs',
diff --git a/fractal-matrix-api/src/model/room.rs b/fractal-matrix-api/src/model/room.rs
index a0b0b12e..af42612b 100644
--- a/fractal-matrix-api/src/model/room.rs
+++ b/fractal-matrix-api/src/model/room.rs
@@ -132,7 +132,7 @@ impl Room {
     pub fn from_sync_response(
         response: &SyncResponse,
         user_id: UserId,
-        baseu: &Url,
+        baseu: Url,
     ) -> Result<Vec<Self>, IdError> {
         // getting the list of direct rooms
         let direct: HashSet<RoomId> = parse_m_direct(&response.account_data.events)
@@ -206,7 +206,7 @@ impl Room {
                 if leave_id != user_id {
                     let kick_reason = &last_event["content"]["reason"];
                     if let Some((kicker_alias, kicker_avatar)) =
-                        get_user_avatar(baseu, &leave_id).ok()
+                        get_user_avatar(baseu.clone(), &leave_id).ok()
                     {
                         let kicker = Member {
                             alias: Some(kicker_alias),
@@ -245,7 +245,7 @@ impl Room {
                     })
                     .map_or(Ok(None), |ev| {
                         Ok(get_user_avatar(
-                            baseu,
+                            baseu.clone(),
                             &UserId::try_from(ev["sender"].as_str().unwrap_or_default())?,
                         )
                         .ok())
diff --git a/fractal-matrix-api/src/r0/media.rs b/fractal-matrix-api/src/r0/media.rs
index c5fb369c..eccd2e8e 100644
--- a/fractal-matrix-api/src/r0/media.rs
+++ b/fractal-matrix-api/src/r0/media.rs
@@ -1 +1,3 @@
-pub mod create;
+pub mod create_content;
+pub mod get_content;
+pub mod get_content_thumbnail;
diff --git a/fractal-matrix-api/src/r0/media/create.rs b/fractal-matrix-api/src/r0/media/create_content.rs
similarity index 100%
rename from fractal-matrix-api/src/r0/media/create.rs
rename to fractal-matrix-api/src/r0/media/create_content.rs
diff --git a/fractal-matrix-api/src/r0/media/get_content.rs b/fractal-matrix-api/src/r0/media/get_content.rs
new file mode 100644
index 00000000..bf8cb0ea
--- /dev/null
+++ b/fractal-matrix-api/src/r0/media/get_content.rs
@@ -0,0 +1,33 @@
+use reqwest::blocking::Client;
+use reqwest::blocking::Request;
+use reqwest::Error;
+use serde::Serialize;
+use url::{Host, Url};
+
+#[derive(Clone, Debug, Serialize)]
+pub struct Parameters {
+    #[serde(skip_serializing_if = "bool::clone")]
+    pub allow_remote: bool,
+}
+
+impl Default for Parameters {
+    fn default() -> Self {
+        Self { allow_remote: true }
+    }
+}
+
+pub fn request(
+    base: Url,
+    params: &Parameters,
+    server: &Host<String>,
+    media_id: &str,
+) -> Result<Request, Error> {
+    let url = base
+        .join(&format!(
+            "/_matrix/media/r0/download/{}/{}",
+            server, media_id,
+        ))
+        .expect("Malformed URL in get_content");
+
+    Client::new().get(url).query(params).build()
+}
diff --git a/fractal-matrix-api/src/r0/media/get_content_thumbnail.rs 
b/fractal-matrix-api/src/r0/media/get_content_thumbnail.rs
new file mode 100644
index 00000000..9ec73e29
--- /dev/null
+++ b/fractal-matrix-api/src/r0/media/get_content_thumbnail.rs
@@ -0,0 +1,37 @@
+use reqwest::blocking::Client;
+use reqwest::blocking::Request;
+use reqwest::Error;
+use serde::Serialize;
+use url::{Host, Url};
+
+#[derive(Clone, Debug, Serialize)]
+pub struct Parameters {
+    pub width: u64,
+    pub height: u64,
+    pub method: Option<Method>,
+    #[serde(skip_serializing_if = "bool::clone")]
+    pub allow_remote: bool,
+}
+
+#[derive(Clone, Copy, Debug, Serialize)]
+#[serde(rename_all = "snake_case")]
+pub enum Method {
+    Crop,
+    Scale,
+}
+
+pub fn request(
+    base: Url,
+    params: &Parameters,
+    server: &Host<String>,
+    media_id: &str,
+) -> Result<Request, Error> {
+    let url = base
+        .join(&format!(
+            "/_matrix/media/r0/thumbnail/{}/{}",
+            server, media_id,
+        ))
+        .expect("Malformed URL in get_content_thumbnail");
+
+    Client::new().get(url).query(params).build()
+}
diff --git a/fractal-matrix-api/src/util.rs b/fractal-matrix-api/src/util.rs
index d90b2f06..6e5f060d 100644
--- a/fractal-matrix-api/src/util.rs
+++ b/fractal-matrix-api/src/util.rs
@@ -23,12 +23,15 @@ use crate::error::Error;
 use crate::r0::context::get_context::request as get_context;
 use crate::r0::context::get_context::Parameters as GetContextParameters;
 use crate::r0::context::get_context::Response as GetContextResponse;
+use crate::r0::media::get_content::request as get_content;
+use crate::r0::media::get_content::Parameters as GetContentParameters;
+use crate::r0::media::get_content_thumbnail::request as get_content_thumbnail;
+use crate::r0::media::get_content_thumbnail::Method;
+use crate::r0::media::get_content_thumbnail::Parameters as GetContentThumbnailParameters;
 use crate::r0::profile::get_profile::request as get_profile;
 use crate::r0::profile::get_profile::Response as GetProfileResponse;
 use crate::r0::AccessToken;
 
-use crate::globals;
-
 lazy_static! {
     pub static ref HTTP_CLIENT: Client = Client::new();
     static ref CACHE_PATH: PathBuf = ProjectDirs::from("org", "GNOME", "Fractal")
@@ -117,7 +120,7 @@ macro_rules! bkerror2 {
 
 pub enum ContentType {
     Download,
-    Thumbnail(i32, i32),
+    Thumbnail(u64, u64),
 }
 
 impl ContentType {
@@ -177,42 +180,43 @@ pub fn get_prev_batch_from(
 }
 
 pub fn dw_media(
-    base: &Url,
-    url: &str,
+    base: Url,
+    mxc: &str,
     media_type: ContentType,
-    dest: Option<&str>,
+    dest: Option<String>,
 ) -> Result<String, Error> {
-    let caps = globals::MATRIX_RE
-        .captures(url)
+    let mxc_url = Url::parse(mxc)?;
+
+    if mxc_url.scheme() != "mxc" {
+        return Err(Error::BackendError);
+    }
+
+    let server = mxc_url.host().ok_or(Error::BackendError)?.to_owned();
+    let media_id = mxc_url
+        .path_segments()
+        .and_then(|mut ps| ps.next())
+        .filter(|s| !s.is_empty())
         .ok_or(Error::BackendError)?;
-    let server = String::from(&caps["server"]);
-    let media = String::from(&caps["media"]);
-
-    let (params, path) = if let ContentType::Thumbnail(w, h) = media_type {
-        (
-            vec![
-                ("width", w.to_string()),
-                ("height", h.to_string()),
-                ("method", String::from("crop")),
-            ],
-            format!("thumbnail/{}/{}", server, media),
-        )
-    } else {
-        (vec![], format!("download/{}/{}", server, media))
-    };
 
-    let url = build_url(base, &format!("/_matrix/media/r0/{}", path), &params)?;
+    let request = if let ContentType::Thumbnail(width, height) = media_type {
+        let params = GetContentThumbnailParameters {
+            width,
+            height,
+            method: Some(Method::Crop),
+            allow_remote: true,
+        };
+        get_content_thumbnail(base, &params, &server, &media_id)
+    } else {
+        let params = GetContentParameters::default();
+        get_content(base, &params, &server, &media_id)
+    }?;
 
     let fname = match dest {
-        None if media_type.is_thumbnail() => cache_dir_path(Some("thumbs"), &media)?,
-        None => cache_dir_path(Some("medias"), &media)?,
-        Some(d) => String::from(d),
+        None if media_type.is_thumbnail() => cache_dir_path(Some("thumbs"), &media_id)?,
+        None => cache_dir_path(Some("medias"), &media_id)?,
+        Some(ref d) => d.clone(),
     };
 
-    download_file(url, fname, dest)
-}
-
-pub fn download_file(url: Url, fname: String, dest: Option<&str>) -> Result<String, Error> {
     let fpath = Path::new(&fname);
 
     // If the file is already cached and recent enough, don't download it
@@ -223,17 +227,16 @@ pub fn download_file(url: Url, fname: String, dest: Option<&str>) -> Result<Stri
     } else {
         HTTP_CLIENT
             .get_client()?
-            .get(url)
-            .send()?
+            .execute(request)?
             .bytes()
             .collect::<Result<Vec<u8>, std::io::Error>>()
             .and_then(|media| write(&fname, media))
             .and(Ok(fname))
-            .map_err(Error::from)
+            .map_err(Into::into)
     }
 }
 
-pub fn get_user_avatar(base: &Url, user_id: &UserId) -> Result<(String, String), Error> {
+pub fn get_user_avatar(base: Url, user_id: &UserId) -> Result<(String, String), Error> {
     let response = get_profile(base.clone(), user_id)
         .map_err::<Error, _>(Into::into)
         .and_then(|request| {
@@ -257,7 +260,7 @@ pub fn get_user_avatar(base: &Url, user_id: &UserId) -> Result<(String, String),
                 base,
                 url.as_str(),
                 ContentType::default_thumbnail(),
-                Some(&dest),
+                Some(dest),
             )
         })
         .unwrap_or(Ok(Default::default()))?;
@@ -265,23 +268,6 @@ pub fn get_user_avatar(base: &Url, user_id: &UserId) -> Result<(String, String),
     Ok((name, img))
 }
 
-pub fn build_url(base: &Url, path: &str, params: &[(&str, String)]) -> Result<Url, Error> {
-    let mut url = base.join(path)?;
-
-    {
-        // If len was 0 `?` would be appended without being needed.
-        if !params.is_empty() {
-            let mut query = url.query_pairs_mut();
-            query.clear();
-            for (k, v) in params {
-                query.append_pair(k, &v);
-            }
-        }
-    }
-
-    Ok(url)
-}
-
 pub fn cache_dir_path(dir: Option<&str>, name: &str) -> Result<String, Error> {
     let path = CACHE_PATH.join(dir.unwrap_or_default());
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]