forked from
rocksky.app/rocksky
A decentralized music tracking and discovery platform built on AT Protocol 馃幍
1use std::env;
2
3use actix_web::HttpResponse;
4use anyhow::Error;
5use reqwest::Client;
6use serde_json::json;
7
8use crate::types::{
9 file::{File, FileList},
10 token::AccessToken,
11};
12
13pub const BASE_URL: &str = "https://www.googleapis.com/drive/v3";
14
15pub async fn get_access_token(refresh_token: &str) -> Result<AccessToken, Error> {
16 let client = Client::new();
17
18 let params = [
19 ("grant_type", "refresh_token"),
20 ("refresh_token", refresh_token),
21 ("client_id", &env::var("GOOGLE_CLIENT_ID")?),
22 ("client_secret", &env::var("GOOGLE_CLIENT_SECRET")?),
23 ];
24
25 let body = serde_urlencoded::to_string(¶ms)?;
26 let res = client
27 .post("https://oauth2.googleapis.com/token")
28 .header("Content-Type", "application/x-www-form-urlencoded")
29 .header("Content-Length", body.len())
30 .body(body)
31 .send()
32 .await?;
33
34 Ok(res.json::<AccessToken>().await?)
35}
36
37pub struct GoogleDriveClient {
38 pub access_token: String,
39}
40
41impl GoogleDriveClient {
42 pub async fn new(refresh_token: &str) -> Result<Self, Error> {
43 let res = get_access_token(refresh_token).await?;
44 Ok(Self {
45 access_token: res.access_token,
46 })
47 }
48
49 pub async fn get_files(&self, name: &str) -> Result<FileList, Error> {
50 let client = Client::new();
51 let url = format!("{}/files", BASE_URL);
52 let res = client
53 .get(&url)
54 .bearer_auth(&self.access_token)
55 .query(&[
56 (
57 "q",
58 format!(
59 "name='{}' and mimeType='application/vnd.google-apps.folder'",
60 name
61 )
62 .as_str(),
63 ),
64 ("fields", "files(id, name, mimeType, parents)"),
65 ("orderBy", "name"),
66 ])
67 .send()
68 .await?;
69
70 Ok(res.json::<FileList>().await?)
71 }
72
73 pub async fn create_music_directory(&self) -> Result<File, Error> {
74 let client = Client::new();
75 let url = format!("{}/files", BASE_URL);
76 let res = client
77 .post(&url)
78 .bearer_auth(&self.access_token)
79 .json(&json!({
80 "name": "Music",
81 "mimeType": "application/vnd.google-apps.folder",
82 }))
83 .send()
84 .await?;
85
86 Ok(res.json::<File>().await?)
87 }
88
89 pub async fn get_music_directory(&self) -> Result<FileList, Error> {
90 let client = Client::new();
91 let url = format!("{}/files", BASE_URL);
92 let res = client.get(&url)
93 .bearer_auth(&self.access_token)
94 .query(&[
95 ("q", "name='Music' and mimeType='application/vnd.google-apps.folder' and 'root' in parents"),
96 ("fields", "files(id, name, mimeType, parents)"),
97 ("orderBy", "name"),
98 ])
99 .send()
100 .await?;
101
102 let files = res.json::<FileList>().await?;
103
104 if files.files.len() == 0 {
105 let music_dir = self.create_music_directory().await?;
106 return Ok(FileList {
107 files: vec![music_dir],
108 next_page_token: None,
109 });
110 }
111
112 Ok(files)
113 }
114
115 pub async fn get_files_in_parents(&self, parent_id: &str) -> Result<FileList, Error> {
116 let client = Client::new();
117 let url = format!("{}/files", BASE_URL);
118 let res = client
119 .get(&url)
120 .bearer_auth(&self.access_token)
121 .query(&[
122 ("q", format!("'{}' in parents", parent_id).as_str()),
123 ("fields", "files(id, name, mimeType, parents)"),
124 ("orderBy", "name"),
125 ])
126 .send()
127 .await?;
128 Ok(res.json::<FileList>().await?)
129 }
130
131 pub async fn get_file(&self, file_id: &str) -> Result<File, Error> {
132 let client = Client::new();
133 let url = format!("{}/files/{}", BASE_URL, file_id);
134 let res = client
135 .get(&url)
136 .bearer_auth(&self.access_token)
137 .query(&[("fields", "id, name, mimeType, parents")])
138 .send()
139 .await?;
140
141 Ok(res.json::<File>().await?)
142 }
143
144 pub async fn download_file(&self, file_id: &str) -> Result<HttpResponse, Error> {
145 let client = Client::new();
146 let url = format!("{}/files/{}", BASE_URL, file_id);
147 let res = client
148 .get(&url)
149 .bearer_auth(&self.access_token)
150 .query(&[("alt", "media")])
151 .send()
152 .await?;
153
154 let mut actix_response = HttpResponse::Ok();
155
156 // Forward headers
157 for (key, value) in res.headers().iter() {
158 actix_response.append_header((key.as_str(), value.to_str().unwrap_or("")));
159 }
160
161 // Forward body
162 let body = res.bytes_stream();
163
164 Ok(actix_response.streaming(body))
165 }
166}