Spaces:
Running
Running
Navid Arabi
commited on
Commit
·
edb5886
1
Parent(s):
faa5405
add new ftp host
Browse files- components/dashboard_page.py +5 -6
- config.py +1 -2
- test/gdrive_test.py +0 -22
- utils/cloud_server_audio_loader.py +42 -0
- utils/gdrive_downloader.py +0 -98
components/dashboard_page.py
CHANGED
@@ -5,15 +5,14 @@ from sqlalchemy import orm, func # Added func for count
|
|
5 |
|
6 |
from components.header import Header
|
7 |
from utils.logger import Logger # Changed from get_logger to Logger
|
8 |
-
from utils.
|
9 |
from config import conf
|
10 |
from utils.database import get_db
|
11 |
from data.models import Annotation, AudioTrim, TTSData, AnnotationInterval # Added AnnotationInterval
|
12 |
from data.repository.annotator_workload_repo import AnnotatorWorkloadRepo # For progress
|
13 |
|
14 |
log = Logger() # Changed from get_logger() to Logger()
|
15 |
-
LOADER =
|
16 |
-
GDRIVE_FOLDER = conf.GDRIVE_FOLDER
|
17 |
|
18 |
|
19 |
class DashboardPage:
|
@@ -188,12 +187,12 @@ class DashboardPage:
|
|
188 |
log.error(f"Error fetching progress for user {user_id}: {e}")
|
189 |
return "Annotation Progress: Error" # Added label
|
190 |
|
191 |
-
def download_voice_fn(
|
192 |
if not filename_to_load:
|
193 |
return None, None, gr.update(value=None, autoplay=False)
|
194 |
try:
|
195 |
log.info(f"Downloading voice: {filename_to_load}, Autoplay: {autoplay_on_load}")
|
196 |
-
sr, wav = LOADER.load_audio(
|
197 |
return (sr, wav), (sr, wav.copy()), gr.update(value=(sr, wav), autoplay=autoplay_on_load)
|
198 |
except Exception as e:
|
199 |
log.error(f"GDrive download failed for {filename_to_load}: {e}")
|
@@ -602,7 +601,7 @@ class DashboardPage:
|
|
602 |
outputs=self.interactive_ui_elements
|
603 |
).then(
|
604 |
fn=download_voice_fn,
|
605 |
-
inputs=[
|
606 |
outputs=[self.audio, self.original_audio_state, self.audio],
|
607 |
).then(
|
608 |
fn=self._apply_multiple_trims_fn,
|
|
|
5 |
|
6 |
from components.header import Header
|
7 |
from utils.logger import Logger # Changed from get_logger to Logger
|
8 |
+
from utils.cloud_server_audio_loader import CloudServerAudioLoader
|
9 |
from config import conf
|
10 |
from utils.database import get_db
|
11 |
from data.models import Annotation, AudioTrim, TTSData, AnnotationInterval # Added AnnotationInterval
|
12 |
from data.repository.annotator_workload_repo import AnnotatorWorkloadRepo # For progress
|
13 |
|
14 |
log = Logger() # Changed from get_logger() to Logger()
|
15 |
+
LOADER = CloudServerAudioLoader(conf.FTP_URL)
|
|
|
16 |
|
17 |
|
18 |
class DashboardPage:
|
|
|
187 |
log.error(f"Error fetching progress for user {user_id}: {e}")
|
188 |
return "Annotation Progress: Error" # Added label
|
189 |
|
190 |
+
def download_voice_fn(filename_to_load, autoplay_on_load=True): # Autoplay here is for the btn_load_voice click
|
191 |
if not filename_to_load:
|
192 |
return None, None, gr.update(value=None, autoplay=False)
|
193 |
try:
|
194 |
log.info(f"Downloading voice: {filename_to_load}, Autoplay: {autoplay_on_load}")
|
195 |
+
sr, wav = LOADER.load_audio(filename_to_load)
|
196 |
return (sr, wav), (sr, wav.copy()), gr.update(value=(sr, wav), autoplay=autoplay_on_load)
|
197 |
except Exception as e:
|
198 |
log.error(f"GDrive download failed for {filename_to_load}: {e}")
|
|
|
601 |
outputs=self.interactive_ui_elements
|
602 |
).then(
|
603 |
fn=download_voice_fn,
|
604 |
+
inputs=[self.filename, gr.State(True)], # Autoplay TRUE
|
605 |
outputs=[self.audio, self.original_audio_state, self.audio],
|
606 |
).then(
|
607 |
fn=self._apply_multiple_trims_fn,
|
config.py
CHANGED
@@ -12,8 +12,7 @@ class Config(BaseSettings):
|
|
12 |
DB_NAME: str = os.getenv("DB_NAME", "defaultdb")
|
13 |
HF_TOKEN: str = os.environ.get("HF_TOKEN")
|
14 |
HF_TTS_DS_REPO: str = os.environ.get("HF_TTS_DS_REPO")
|
15 |
-
|
16 |
-
GDRIVE_FOLDER: str = os.environ.get("GDRIVE_FOLDER")
|
17 |
|
18 |
APP_TITLE: str = "Gooya TTS Annotation Tools"
|
19 |
|
|
|
12 |
DB_NAME: str = os.getenv("DB_NAME", "defaultdb")
|
13 |
HF_TOKEN: str = os.environ.get("HF_TOKEN")
|
14 |
HF_TTS_DS_REPO: str = os.environ.get("HF_TTS_DS_REPO")
|
15 |
+
FTP_URL: str = os.environ.get("FTP_URL")
|
|
|
16 |
|
17 |
APP_TITLE: str = "Gooya TTS Annotation Tools"
|
18 |
|
test/gdrive_test.py
DELETED
@@ -1,22 +0,0 @@
|
|
1 |
-
import gradio as gr
|
2 |
-
from utils.gdrive_downloader import PublicFolderAudioLoader
|
3 |
-
from config import conf
|
4 |
-
|
5 |
-
LOADER = PublicFolderAudioLoader(conf.GDRIVE_API_KEY)
|
6 |
-
|
7 |
-
def fetch_audio(folder_link, filename):
|
8 |
-
sr, wav = LOADER.load_audio(folder_link, filename)
|
9 |
-
return (sr, wav)
|
10 |
-
|
11 |
-
demo = gr.Interface(
|
12 |
-
fn=fetch_audio,
|
13 |
-
inputs=[
|
14 |
-
gr.Textbox(label="Folder URL or ID",
|
15 |
-
value=conf.GDRIVE_FOLDER),
|
16 |
-
gr.Textbox(label="Filename (e.g. 0001.wav)")
|
17 |
-
],
|
18 |
-
outputs=gr.Audio(label="🔊 Audio"),
|
19 |
-
)
|
20 |
-
|
21 |
-
if __name__ == "__main__":
|
22 |
-
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/cloud_server_audio_loader.py
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# cloud_server_audio_loader.py
|
2 |
+
|
3 |
+
import io
|
4 |
+
import requests
|
5 |
+
import numpy as np
|
6 |
+
from pydub import AudioSegment
|
7 |
+
|
8 |
+
class CloudServerAudioLoader:
|
9 |
+
def __init__(self, base_url: str) -> None:
|
10 |
+
if not base_url.endswith("/"):
|
11 |
+
base_url += "/"
|
12 |
+
self.base_url = base_url
|
13 |
+
|
14 |
+
def _download_to_buf(self, filename: str) -> io.BytesIO:
|
15 |
+
url = self.base_url + filename
|
16 |
+
resp = requests.get(url, stream=True)
|
17 |
+
if resp.status_code != 200:
|
18 |
+
raise FileNotFoundError(f"'{filename}' not found. HTTP {resp.status_code}")
|
19 |
+
buf = io.BytesIO(resp.content)
|
20 |
+
buf.seek(0)
|
21 |
+
return buf
|
22 |
+
|
23 |
+
def load_audio(self, filename: str) -> tuple[int, np.ndarray]:
|
24 |
+
buf = self._download_to_buf(filename)
|
25 |
+
seg = AudioSegment.from_file(buf)
|
26 |
+
samples = np.array(seg.get_array_of_samples())
|
27 |
+
|
28 |
+
if seg.channels > 1:
|
29 |
+
samples = samples.reshape(-1, seg.channels)
|
30 |
+
|
31 |
+
if np.issubdtype(samples.dtype, np.integer):
|
32 |
+
max_int = np.iinfo(samples.dtype).max
|
33 |
+
samples = samples.astype(np.float32)
|
34 |
+
samples /= max_int
|
35 |
+
else:
|
36 |
+
max_val = np.abs(samples).max()
|
37 |
+
if max_val > 1:
|
38 |
+
samples = samples / max_val
|
39 |
+
samples = samples.astype(np.float32)
|
40 |
+
# --------------------------------------------------------
|
41 |
+
|
42 |
+
return seg.frame_rate, samples
|
utils/gdrive_downloader.py
DELETED
@@ -1,98 +0,0 @@
|
|
1 |
-
# gdrive_downloader.py
|
2 |
-
|
3 |
-
from __future__ import annotations
|
4 |
-
import io
|
5 |
-
import re
|
6 |
-
import numpy as np
|
7 |
-
from pydub import AudioSegment
|
8 |
-
from googleapiclient.discovery import build
|
9 |
-
from googleapiclient.http import MediaIoBaseDownload
|
10 |
-
|
11 |
-
|
12 |
-
def extract_folder_id(url_or_id: str) -> str:
|
13 |
-
"""
|
14 |
-
اگر کاربر لینک فولدر بدهد ← ID را برمیگرداند.
|
15 |
-
اگر خودش ID باشد همان را برمیگرداند.
|
16 |
-
"""
|
17 |
-
s = url_or_id.strip()
|
18 |
-
if "/" not in s and "?" not in s:
|
19 |
-
return s # احتمالاً خودش ID است
|
20 |
-
m = re.search(r"/folders/([a-zA-Z0-9_-]{10,})", s)
|
21 |
-
if not m:
|
22 |
-
raise ValueError("Cannot extract folder id from url")
|
23 |
-
return m.group(1)
|
24 |
-
|
25 |
-
|
26 |
-
class PublicFolderAudioLoader:
|
27 |
-
"""
|
28 |
-
دانلودر فایل صوتی از فولدر عمومی گوگلدرایو بدون ذخیره روی دیسک.
|
29 |
-
|
30 |
-
Parameters
|
31 |
-
----------
|
32 |
-
api_key : str
|
33 |
-
Google API Key (کیِ عمومی؛ نه OAuth, نه سرویساکانت).
|
34 |
-
"""
|
35 |
-
|
36 |
-
def __init__(self, api_key: str) -> None:
|
37 |
-
self.svc = build("drive", "v3", developerKey=api_key, cache_discovery=False)
|
38 |
-
|
39 |
-
# ---------- helpers ---------- #
|
40 |
-
def _file_id_by_name(self, folder_id: str, filename: str) -> str:
|
41 |
-
q = (
|
42 |
-
f"'{folder_id}' in parents "
|
43 |
-
f"and name = '{filename}' "
|
44 |
-
f"and trashed = false"
|
45 |
-
)
|
46 |
-
rsp = (
|
47 |
-
self.svc.files()
|
48 |
-
.list(q=q, fields="files(id,name)", pageSize=5, supportsAllDrives=True)
|
49 |
-
.execute()
|
50 |
-
)
|
51 |
-
files = rsp.get("files", [])
|
52 |
-
if not files:
|
53 |
-
raise FileNotFoundError(f"'{filename}' not found in folder {folder_id}")
|
54 |
-
return files[0]["id"]
|
55 |
-
|
56 |
-
def _download_to_buf(self, file_id: str) -> io.BytesIO:
|
57 |
-
request = self.svc.files().get_media(fileId=file_id, supportsAllDrives=True)
|
58 |
-
buf = io.BytesIO()
|
59 |
-
downloader = MediaIoBaseDownload(buf, request)
|
60 |
-
done = False
|
61 |
-
while not done:
|
62 |
-
_, done = downloader.next_chunk()
|
63 |
-
buf.seek(0)
|
64 |
-
return buf
|
65 |
-
|
66 |
-
# ---------- public ---------- #
|
67 |
-
def load_audio(
|
68 |
-
self,
|
69 |
-
folder_url_or_id: str,
|
70 |
-
filename: str,
|
71 |
-
) -> tuple[int, np.ndarray]:
|
72 |
-
# """
|
73 |
-
# فایل را به `(sample_rate, np.ndarray)` نرمالشده در بازهی [-1,1] تبدیل میکند.
|
74 |
-
# """
|
75 |
-
folder_id = extract_folder_id(folder_url_or_id)
|
76 |
-
file_id = self._file_id_by_name(folder_id, filename)
|
77 |
-
buf = self._download_to_buf(file_id)
|
78 |
-
seg = AudioSegment.from_file(buf)
|
79 |
-
samples = np.array(seg.get_array_of_samples())
|
80 |
-
|
81 |
-
# اگر چندکاناله بود، شکل دهیم
|
82 |
-
if seg.channels > 1:
|
83 |
-
samples = samples.reshape(-1, seg.channels)
|
84 |
-
|
85 |
-
# ---------------------- نرمالسازی ----------------------
|
86 |
-
if np.issubdtype(samples.dtype, np.integer):
|
87 |
-
max_int = np.iinfo(samples.dtype).max # ← قبل از cast
|
88 |
-
samples = samples.astype(np.float32)
|
89 |
-
samples /= max_int # ← از max_int استفاده میکنیم
|
90 |
-
else:
|
91 |
-
# در حالت float
|
92 |
-
max_val = np.abs(samples).max()
|
93 |
-
if max_val > 1:
|
94 |
-
samples = samples / max_val
|
95 |
-
samples = samples.astype(np.float32)
|
96 |
-
# --------------------------------------------------------
|
97 |
-
|
98 |
-
return seg.frame_rate, samples
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|