Sulitha commited on
Commit
d8d08b1
·
1 Parent(s): 5ab105e

feat: add optional Google Drive upload for recordings

Browse files
Files changed (3) hide show
  1. README.md +11 -0
  2. app.py +74 -1
  3. requirements.txt +5 -1
README.md CHANGED
@@ -37,3 +37,14 @@ Uploads may take a few seconds. Large batches could hit rate limits; keep per-su
37
 
38
  The repository view shows only Git-tracked content. Runtime-generated files live only in the ephemeral container filesystem until the Space restarts. Upload or commit them if you need persistence.
39
 
 
 
 
 
 
 
 
 
 
 
 
 
37
 
38
  The repository view shows only Git-tracked content. Runtime-generated files live only in the ephemeral container filesystem until the Space restarts. Upload or commit them if you need persistence.
39
 
40
+ ## Google Drive Upload (Alternative)
41
+
42
+ If you prefer uploading to Google Drive:
43
+
44
+ 1. Create a Google Cloud service account with Drive API enabled and grant it access to a Drive folder.
45
+ 2. Put the service account JSON in a Space secret named `GDRIVE_SERVICE_ACCOUNT_JSON`. You can paste the JSON string or mount a path and store the path in the secret.
46
+ 3. Add another secret `GDRIVE_FOLDER_ID` with the target folder ID.
47
+ 4. In the app UI, tick "Upload to Google Drive" before Submit.
48
+
49
+ The app uses `google-api-python-client` to upload each WAV file into that folder. Errors will be shown in the results area if credentials or permissions are incorrect.
50
+
app.py CHANGED
@@ -1,4 +1,5 @@
1
  import os
 
2
  import re
3
  import time
4
  import math
@@ -14,6 +15,16 @@ except Exception: # package might be missing in some local runs
14
  HfApi = None
15
  HfFolder = None
16
 
 
 
 
 
 
 
 
 
 
 
17
  # Output directory for saved recordings
18
  OUT_DIR = "recordings"
19
  os.makedirs(OUT_DIR, exist_ok=True)
@@ -146,6 +157,57 @@ def upload_recordings(paths: Sequence[str]) -> Tuple[int, Optional[str]]:
146
  return uploaded, None
147
 
148
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  def submit_recordings(
150
  username: str,
151
  lumos_path: Optional[str],
@@ -155,6 +217,7 @@ def submit_recordings(
155
  accio_path: Optional[str],
156
  reparo_path: Optional[str],
157
  upload_flag: bool,
 
158
  ) -> str:
159
  user = sanitize_username(username)
160
 
@@ -199,6 +262,14 @@ def submit_recordings(
199
  lines.append(f"Hub upload: {uploaded} file(s) committed to repo.")
200
  lines.append("(It may take a few seconds to appear in the file browser.)")
201
 
 
 
 
 
 
 
 
 
202
  return "\n".join(lines)
203
 
204
 
@@ -226,12 +297,13 @@ def build_ui() -> gr.Blocks:
226
 
227
  with gr.Row():
228
  upload_checkbox = gr.Checkbox(label="Upload to Hub (requires HF_TOKEN)", value=False)
 
229
  submit = gr.Button("Submit")
230
  result = gr.Markdown()
231
 
232
  submit.click(
233
  fn=submit_recordings,
234
- inputs=[username, lumos, nox, alohomora, wingardium, accio, reparo, upload_checkbox],
235
  outputs=[result],
236
  )
237
 
@@ -239,6 +311,7 @@ def build_ui() -> gr.Blocks:
239
  Notes:
240
  - Files are saved locally in `recordings/` with `<spell>_<username>_<timestamp>.wav`.
241
  - Check "Upload to Hub" to commit them to the repo (needs HF_TOKEN secret).
 
242
  - 16 kHz mono WAV ensures consistent model training.
243
  - You can submit partial sets; only provided spells are saved.
244
  """)
 
1
  import os
2
+ import json
3
  import re
4
  import time
5
  import math
 
15
  HfApi = None
16
  HfFolder = None
17
 
18
+ # Google Drive API (service account) optional imports
19
+ try:
20
+ from google.oauth2 import service_account
21
+ from googleapiclient.discovery import build
22
+ from googleapiclient.http import MediaFileUpload
23
+ except Exception:
24
+ service_account = None
25
+ build = None
26
+ MediaFileUpload = None
27
+
28
  # Output directory for saved recordings
29
  OUT_DIR = "recordings"
30
  os.makedirs(OUT_DIR, exist_ok=True)
 
157
  return uploaded, None
158
 
159
 
160
+ def upload_recordings_to_gdrive(paths: Sequence[str]) -> Tuple[int, Optional[str]]:
161
+ """Upload files to Google Drive into a folder using a service account.
162
+
163
+ Requires secrets:
164
+ - GDRIVE_SERVICE_ACCOUNT_JSON: full JSON credentials for a service account
165
+ - GDRIVE_FOLDER_ID: target Drive folder ID
166
+ Returns (uploaded_count, error_message).
167
+ """
168
+ if not paths:
169
+ return 0, None
170
+ if not (service_account and build and MediaFileUpload):
171
+ return 0, "Google API client not installed."
172
+ svc_json = os.getenv("GDRIVE_SERVICE_ACCOUNT_JSON")
173
+ folder_id = os.getenv("GDRIVE_FOLDER_ID")
174
+ if not svc_json:
175
+ return 0, "Missing GDRIVE_SERVICE_ACCOUNT_JSON secret."
176
+ if not folder_id:
177
+ return 0, "Missing GDRIVE_FOLDER_ID secret."
178
+
179
+ try:
180
+ creds = None
181
+ if svc_json.strip().startswith("{"):
182
+ data = json.loads(svc_json)
183
+ creds = service_account.Credentials.from_service_account_info(
184
+ data,
185
+ scopes=["https://www.googleapis.com/auth/drive.file"],
186
+ )
187
+ else:
188
+ # if not JSON string, maybe it's a file path provided via secret
189
+ creds = service_account.Credentials.from_service_account_file(
190
+ svc_json,
191
+ scopes=["https://www.googleapis.com/auth/drive.file"],
192
+ )
193
+ drive = build("drive", "v3", credentials=creds)
194
+ except Exception as e:
195
+ return 0, f"Auth error: {e}"
196
+
197
+ uploaded = 0
198
+ try:
199
+ for p in paths:
200
+ if not os.path.isfile(p):
201
+ continue
202
+ media = MediaFileUpload(p, mimetype="audio/wav", resumable=False)
203
+ body = {"name": os.path.basename(p), "parents": [folder_id]}
204
+ drive.files().create(body=body, media_body=media, fields="id").execute()
205
+ uploaded += 1
206
+ except Exception as e:
207
+ return uploaded, f"Drive upload error: {e}"
208
+ return uploaded, None
209
+
210
+
211
  def submit_recordings(
212
  username: str,
213
  lumos_path: Optional[str],
 
217
  accio_path: Optional[str],
218
  reparo_path: Optional[str],
219
  upload_flag: bool,
220
+ gdrive_flag: bool,
221
  ) -> str:
222
  user = sanitize_username(username)
223
 
 
262
  lines.append(f"Hub upload: {uploaded} file(s) committed to repo.")
263
  lines.append("(It may take a few seconds to appear in the file browser.)")
264
 
265
+ if gdrive_flag:
266
+ gup, gerr = upload_recordings_to_gdrive(saved_paths)
267
+ lines.append("")
268
+ if gerr:
269
+ lines.append(f"Drive upload attempted: {gup} succeeded, error: {gerr}")
270
+ else:
271
+ lines.append(f"Drive upload: {gup} file(s) uploaded to folder.")
272
+
273
  return "\n".join(lines)
274
 
275
 
 
297
 
298
  with gr.Row():
299
  upload_checkbox = gr.Checkbox(label="Upload to Hub (requires HF_TOKEN)", value=False)
300
+ gdrive_checkbox = gr.Checkbox(label="Upload to Google Drive (service account)", value=False)
301
  submit = gr.Button("Submit")
302
  result = gr.Markdown()
303
 
304
  submit.click(
305
  fn=submit_recordings,
306
+ inputs=[username, lumos, nox, alohomora, wingardium, accio, reparo, upload_checkbox, gdrive_checkbox],
307
  outputs=[result],
308
  )
309
 
 
311
  Notes:
312
  - Files are saved locally in `recordings/` with `<spell>_<username>_<timestamp>.wav`.
313
  - Check "Upload to Hub" to commit them to the repo (needs HF_TOKEN secret).
314
+ - Or check "Upload to Google Drive" to upload via a service account.
315
  - 16 kHz mono WAV ensures consistent model training.
316
  - You can submit partial sets; only provided spells are saved.
317
  """)
requirements.txt CHANGED
@@ -2,4 +2,8 @@ gradio
2
  numpy
3
  soundfile
4
  scipy
5
- huggingface_hub
 
 
 
 
 
2
  numpy
3
  soundfile
4
  scipy
5
+ huggingface_hub
6
+ google-api-python-client
7
+ google-auth
8
+ google-auth-httplib2
9
+ google-auth-oauthlib