A decentralized music tracking and discovery platform built on AT Protocol 馃幍
at main 122 lines 3.7 kB view raw
1use std::env; 2 3use actix_web::HttpResponse; 4use anyhow::Error; 5use reqwest::Client; 6use serde_json::json; 7 8use crate::types::{ 9 file::{Entry, EntryList, TemporaryLink}, 10 token::AccessToken, 11}; 12 13pub const BASE_URL: &str = "https://api.dropboxapi.com/2"; 14pub const CONTENT_URL: &str = "https://content.dropboxapi.com/2"; 15 16pub async fn get_access_token(refresh_token: &str) -> Result<AccessToken, Error> { 17 let client = Client::new(); 18 let res = client 19 .post("https://api.dropboxapi.com/oauth2/token") 20 .header("Content-Type", "application/x-www-form-urlencoded") 21 .query(&[ 22 ("grant_type", "refresh_token"), 23 ("refresh_token", refresh_token), 24 ("client_id", &env::var("DROPBOX_CLIENT_ID")?), 25 ("client_secret", &env::var("DROPBOX_CLIENT_SECRET")?), 26 ]) 27 .send() 28 .await?; 29 30 Ok(res.json::<AccessToken>().await?) 31} 32 33pub struct DropboxClient { 34 pub access_token: String, 35} 36 37impl DropboxClient { 38 pub async fn new(refresh_token: &str) -> Result<Self, Error> { 39 let res = get_access_token(refresh_token).await?; 40 Ok(DropboxClient { 41 access_token: res.access_token, 42 }) 43 } 44 45 pub async fn create_music_folder(&self) -> Result<(), Error> { 46 let client = Client::new(); 47 client 48 .post(&format!("{}/files/create_folder_v2", BASE_URL)) 49 .bearer_auth(&self.access_token) 50 .json(&json!({ "path": "/Music" })) 51 .send() 52 .await?; 53 54 Ok(()) 55 } 56 57 pub async fn get_metadata(&self, path: &str) -> Result<Entry, Error> { 58 let client = Client::new(); 59 let res = client 60 .post(&format!("{}/files/get_metadata", BASE_URL)) 61 .bearer_auth(&self.access_token) 62 .json(&json!({ "path": path })) 63 .send() 64 .await?; 65 66 Ok(res.json::<Entry>().await?) 67 } 68 69 pub async fn get_files(&self, path: &str) -> Result<EntryList, Error> { 70 let client = Client::new(); 71 let res = client 72 .post(&format!("{}/files/list_folder", BASE_URL)) 73 .bearer_auth(&self.access_token) 74 .json(&json!({ 75 "path": path, 76 "recursive": false, 77 "include_media_info": true, 78 "include_deleted": false, 79 "include_has_explicit_shared_members": false, 80 "include_mounted_folders": true, 81 "include_non_downloadable_files": true, 82 })) 83 .send() 84 .await?; 85 86 Ok(res.json::<EntryList>().await?) 87 } 88 89 pub async fn download_file(&self, path: &str) -> Result<HttpResponse, Error> { 90 let client = Client::new(); 91 let res = client 92 .post(&format!("{}/files/download", CONTENT_URL)) 93 .bearer_auth(&self.access_token) 94 .header("Dropbox-API-Arg", &json!({ "path": path }).to_string()) 95 .send() 96 .await?; 97 98 let mut actix_response = HttpResponse::Ok(); 99 100 // Forward headers 101 for (key, value) in res.headers().iter() { 102 actix_response.append_header((key.as_str(), value.to_str().unwrap_or(""))); 103 } 104 105 // Forward body 106 let body = res.bytes_stream(); 107 108 Ok(actix_response.streaming(body)) 109 } 110 111 pub async fn get_temporary_link(&self, path: &str) -> Result<TemporaryLink, Error> { 112 let client = Client::new(); 113 let res = client 114 .post(&format!("{}/files/get_temporary_link", BASE_URL)) 115 .bearer_auth(&self.access_token) 116 .json(&json!({ "path": path })) 117 .send() 118 .await?; 119 120 Ok(res.json::<TemporaryLink>().await?) 121 } 122}