Spaces:
Running
Running
Navid Arabi
commited on
Commit
·
7a295c7
1
Parent(s):
c8c252f
add audio player
Browse files- components/dashboard_page.py +75 -122
- config.py +2 -1
- scripts/assign_interval_to_annotator.py +3 -3
- test/db_test.py +0 -26
- gdrive_test.py → test/gdrive_test.py +3 -3
- {test → utils}/gdrive_downloader.py +0 -0
components/dashboard_page.py
CHANGED
@@ -1,146 +1,88 @@
|
|
1 |
-
import os
|
2 |
-
from pathlib import Path
|
3 |
-
|
4 |
import gradio as gr
|
5 |
import numpy as np
|
6 |
-
from pydub import AudioSegment
|
7 |
-
|
8 |
from components.header import Header
|
9 |
from utils.logger import Logger
|
|
|
|
|
10 |
|
11 |
log = Logger()
|
12 |
-
|
13 |
-
|
14 |
-
AUDIO_DIR = Path("audio") # <project_root>/audio/<filename>.wav
|
15 |
|
16 |
|
17 |
class DashboardPage:
|
18 |
-
"""صفحهٔ داشبورد شامل اطلاعات متنی (چپ) و پخشکنندهٔ صوت (راست)."""
|
19 |
-
|
20 |
-
# ───────── ساخت UI ───────── #
|
21 |
def __init__(self) -> None:
|
22 |
with gr.Column(visible=False) as self.container:
|
23 |
-
# هدر
|
24 |
self.header = Header()
|
25 |
|
26 |
-
# بدنهٔ دو ستونه
|
27 |
with gr.Row():
|
28 |
-
|
29 |
-
|
30 |
-
with gr.Column(scale=3) as self.left_col:
|
31 |
-
|
32 |
with gr.Row():
|
33 |
self.tts_id = gr.Textbox(label="ID", interactive=False)
|
34 |
self.filename = gr.Textbox(label="Filename", interactive=False)
|
35 |
-
|
36 |
self.sentence = gr.Textbox(
|
37 |
label="Sentence", interactive=False, max_lines=5, rtl=True
|
38 |
)
|
39 |
-
|
40 |
self.ann_sentence = gr.Textbox(
|
41 |
label="Annotated Sentence",
|
42 |
interactive=True,
|
43 |
max_lines=5,
|
44 |
rtl=True,
|
45 |
)
|
46 |
-
|
47 |
with gr.Row():
|
48 |
self.ann_at = gr.Textbox(
|
49 |
-
label="Annotation Time",
|
50 |
-
interactive=False,
|
51 |
)
|
52 |
-
|
53 |
self.validated = gr.Checkbox(
|
54 |
-
label="
|
55 |
-
interactive=False,
|
56 |
)
|
57 |
-
|
58 |
-
# دکمههای پیمایش زیر اطلاعات متنی
|
59 |
with gr.Row():
|
60 |
self.btn_prev = gr.Button("⬅️ Previous")
|
61 |
self.btn_next = gr.Button("Next ➡️")
|
62 |
|
63 |
-
#
|
64 |
-
with gr.Column(scale=2)
|
|
|
65 |
self.audio = gr.Audio(label="🔊 Audio", interactive=False)
|
66 |
-
|
67 |
|
68 |
-
# state
|
69 |
-
self.items_state = gr.State([])
|
70 |
-
self.idx_state = gr.State(0)
|
71 |
|
72 |
-
#
|
73 |
def register_callbacks(
|
74 |
-
self,
|
75 |
-
|
76 |
-
session_state: gr.State, # dict درون gr.State
|
77 |
-
root_blocks: gr.Blocks,
|
78 |
-
) -> None:
|
79 |
|
80 |
-
# رویداد خروج
|
81 |
self.header.register_callbacks(login_page, self, session_state)
|
82 |
|
83 |
-
#
|
84 |
-
def
|
85 |
-
"""مسیر کامل فایل صوتی روی دیسک."""
|
86 |
-
return str(AUDIO_DIR / filename)
|
87 |
-
|
88 |
-
def _duration_seconds(wav_path: str) -> float:
|
89 |
-
"""طول فایل صوتی به ثانیه (برای اسلایدرها)."""
|
90 |
-
try:
|
91 |
-
dur = len(AudioSegment.from_file(wav_path)) / 1000.0
|
92 |
-
return round(dur, 2)
|
93 |
-
except Exception as e:
|
94 |
-
log.warning(f"Cannot read duration for '{wav_path}': {e}")
|
95 |
-
return 0.0
|
96 |
-
|
97 |
-
def show_current(items: list, idx: int):
|
98 |
-
"""دادههای رکورد idx را برای خروجیها تولید میکند."""
|
99 |
if not items:
|
100 |
-
|
101 |
-
|
102 |
-
"",
|
103 |
-
"",
|
104 |
-
"",
|
105 |
-
"",
|
106 |
-
"",
|
107 |
-
False,
|
108 |
-
None,
|
109 |
-
gr.update(minimum=0, maximum=0, value=0),
|
110 |
-
gr.update(minimum=0, maximum=0, value=0),
|
111 |
-
]
|
112 |
-
|
113 |
-
data = items[idx]
|
114 |
-
wav_path = _audio_path(data["filename"])
|
115 |
-
dur = _duration_seconds(wav_path)
|
116 |
-
|
117 |
return [
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
bool(
|
124 |
-
|
125 |
-
gr.update(minimum=0, maximum=dur, value=0), # start slider
|
126 |
-
gr.update(minimum=0, maximum=dur, value=dur), # end slider
|
127 |
]
|
128 |
|
129 |
-
def next_idx(items
|
130 |
return min(idx + 1, max(len(items) - 1, 0))
|
131 |
|
132 |
-
def prev_idx(items
|
133 |
return max(idx - 1, 0)
|
134 |
|
135 |
-
|
136 |
-
def load_items(sess: dict):
|
137 |
items = sess.get("dashboard_items", [])
|
138 |
-
return (
|
139 |
-
items,
|
140 |
-
0,
|
141 |
-
*show_current(items, 0),
|
142 |
-
)
|
143 |
|
|
|
144 |
root_blocks.load(
|
145 |
fn=load_items,
|
146 |
inputs=[session_state],
|
@@ -153,17 +95,15 @@ class DashboardPage:
|
|
153 |
self.ann_sentence,
|
154 |
self.ann_at,
|
155 |
self.validated,
|
156 |
-
self.audio,
|
157 |
-
self.start_slider,
|
158 |
-
self.end_slider,
|
159 |
],
|
160 |
)
|
161 |
|
162 |
-
# ----------
|
163 |
-
for btn,
|
164 |
(
|
165 |
btn.click(
|
166 |
-
fn=
|
167 |
inputs=[self.items_state, self.idx_state],
|
168 |
outputs=self.idx_state,
|
169 |
).then(
|
@@ -177,36 +117,49 @@ class DashboardPage:
|
|
177 |
self.ann_at,
|
178 |
self.validated,
|
179 |
self.audio,
|
180 |
-
self.start_slider,
|
181 |
-
self.end_slider,
|
182 |
],
|
183 |
)
|
184 |
)
|
185 |
|
186 |
-
# ----------
|
187 |
-
def
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
193 |
return None
|
194 |
try:
|
195 |
-
|
196 |
-
|
197 |
-
end_ms = int(min(end, len(seg) / 1000) * 1000)
|
198 |
-
if start_ms >= end_ms:
|
199 |
-
end_ms = start_ms + 1000 # حداقل ۱ ثانیه
|
200 |
-
clip = seg[start_ms:end_ms]
|
201 |
-
samples = np.array(clip.get_array_of_samples()).astype(np.float32)
|
202 |
-
samples /= np.iinfo(samples.dtype).max # نرمالسازی
|
203 |
-
return (clip.frame_rate, samples)
|
204 |
except Exception as e:
|
205 |
-
log.error(f"
|
206 |
-
return None
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
212 |
)
|
|
|
|
|
|
|
|
|
1 |
import gradio as gr
|
2 |
import numpy as np
|
|
|
|
|
3 |
from components.header import Header
|
4 |
from utils.logger import Logger
|
5 |
+
from utils.gdrive_downloader import PublicFolderAudioLoader
|
6 |
+
from config import conf
|
7 |
|
8 |
log = Logger()
|
9 |
+
LOADER = PublicFolderAudioLoader(conf.GDRIVE_API_KEY)
|
10 |
+
GDRIVE_FOLDER = conf.GDRIVE_FOLDER
|
|
|
11 |
|
12 |
|
13 |
class DashboardPage:
|
|
|
|
|
|
|
14 |
def __init__(self) -> None:
|
15 |
with gr.Column(visible=False) as self.container:
|
|
|
16 |
self.header = Header()
|
17 |
|
|
|
18 |
with gr.Row():
|
19 |
+
# ستون چپ
|
20 |
+
with gr.Column(scale=3):
|
|
|
|
|
21 |
with gr.Row():
|
22 |
self.tts_id = gr.Textbox(label="ID", interactive=False)
|
23 |
self.filename = gr.Textbox(label="Filename", interactive=False)
|
|
|
24 |
self.sentence = gr.Textbox(
|
25 |
label="Sentence", interactive=False, max_lines=5, rtl=True
|
26 |
)
|
|
|
27 |
self.ann_sentence = gr.Textbox(
|
28 |
label="Annotated Sentence",
|
29 |
interactive=True,
|
30 |
max_lines=5,
|
31 |
rtl=True,
|
32 |
)
|
|
|
33 |
with gr.Row():
|
34 |
self.ann_at = gr.Textbox(
|
35 |
+
label="Annotation Time", interactive=False
|
|
|
36 |
)
|
|
|
37 |
self.validated = gr.Checkbox(
|
38 |
+
label="Validated", interactive=False
|
|
|
39 |
)
|
|
|
|
|
40 |
with gr.Row():
|
41 |
self.btn_prev = gr.Button("⬅️ Previous")
|
42 |
self.btn_next = gr.Button("Next ➡️")
|
43 |
|
44 |
+
# ستون راست
|
45 |
+
with gr.Column(scale=2):
|
46 |
+
self.btn_load_voice = gr.Button("Load Audio")
|
47 |
self.audio = gr.Audio(label="🔊 Audio", interactive=False)
|
|
|
48 |
|
49 |
+
# stateها
|
50 |
+
self.items_state = gr.State([])
|
51 |
+
self.idx_state = gr.State(0)
|
52 |
|
53 |
+
# ---------------- wiring ---------------- #
|
54 |
def register_callbacks(
|
55 |
+
self, login_page, session_state: gr.State, root_blocks: gr.Blocks
|
56 |
+
):
|
|
|
|
|
|
|
57 |
|
|
|
58 |
self.header.register_callbacks(login_page, self, session_state)
|
59 |
|
60 |
+
# ---- helpers ----
|
61 |
+
def show_current(items, idx):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
if not items:
|
63 |
+
return ["", "", "", "", "", False, None]
|
64 |
+
d = items[idx]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
return [
|
66 |
+
d["id"],
|
67 |
+
d["filename"],
|
68 |
+
d["sentence"],
|
69 |
+
d.get("annotated_sentence", ""),
|
70 |
+
d.get("annotated_at", ""),
|
71 |
+
bool(d.get("validated", False)),
|
72 |
+
None, # Audio هنوز لود نشده
|
|
|
|
|
73 |
]
|
74 |
|
75 |
+
def next_idx(items, idx):
|
76 |
return min(idx + 1, max(len(items) - 1, 0))
|
77 |
|
78 |
+
def prev_idx(items, idx):
|
79 |
return max(idx - 1, 0)
|
80 |
|
81 |
+
def load_items(sess):
|
|
|
82 |
items = sess.get("dashboard_items", [])
|
83 |
+
return items, 0, *show_current(items, 0)
|
|
|
|
|
|
|
|
|
84 |
|
85 |
+
# ---------- initial load ---------- #
|
86 |
root_blocks.load(
|
87 |
fn=load_items,
|
88 |
inputs=[session_state],
|
|
|
95 |
self.ann_sentence,
|
96 |
self.ann_at,
|
97 |
self.validated,
|
98 |
+
self.audio, # 9 اُمین خروجى
|
|
|
|
|
99 |
],
|
100 |
)
|
101 |
|
102 |
+
# ---------- navigation ---------- #
|
103 |
+
for btn, nav_fn in [(self.btn_prev, prev_idx), (self.btn_next, next_idx)]:
|
104 |
(
|
105 |
btn.click(
|
106 |
+
fn=nav_fn,
|
107 |
inputs=[self.items_state, self.idx_state],
|
108 |
outputs=self.idx_state,
|
109 |
).then(
|
|
|
117 |
self.ann_at,
|
118 |
self.validated,
|
119 |
self.audio,
|
|
|
|
|
120 |
],
|
121 |
)
|
122 |
)
|
123 |
|
124 |
+
# ---------- load audio ---------- #
|
125 |
+
def disable_buttons():
|
126 |
+
return [
|
127 |
+
gr.update(value="⏳ Waiting...", interactive=False),
|
128 |
+
gr.update(interactive=False),
|
129 |
+
gr.update(interactive=False),
|
130 |
+
]
|
131 |
+
|
132 |
+
def enable_buttons():
|
133 |
+
return [
|
134 |
+
gr.update(value="Load Audio", interactive=True),
|
135 |
+
gr.update(interactive=True),
|
136 |
+
gr.update(interactive=True),
|
137 |
+
]
|
138 |
+
|
139 |
+
def download_voice(folder_link, filename):
|
140 |
+
if not filename:
|
141 |
return None
|
142 |
try:
|
143 |
+
sr, wav = LOADER.load_audio(folder_link, filename)
|
144 |
+
return (sr, wav)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
145 |
except Exception as e:
|
146 |
+
log.error(f"GDrive download failed: {e}")
|
147 |
+
return None # player خالي مىماند
|
148 |
+
|
149 |
+
(
|
150 |
+
self.btn_load_voice.click(
|
151 |
+
fn=disable_buttons,
|
152 |
+
inputs=None,
|
153 |
+
outputs=[self.btn_load_voice, self.btn_prev, self.btn_next],
|
154 |
+
)
|
155 |
+
.then(
|
156 |
+
fn=download_voice,
|
157 |
+
inputs=[gr.State(GDRIVE_FOLDER), self.filename],
|
158 |
+
outputs=self.audio,
|
159 |
+
)
|
160 |
+
.then(
|
161 |
+
fn=enable_buttons,
|
162 |
+
inputs=None,
|
163 |
+
outputs=[self.btn_load_voice, self.btn_prev, self.btn_next],
|
164 |
+
)
|
165 |
)
|
config.py
CHANGED
@@ -12,7 +12,8 @@ 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 |
|
17 |
APP_TITLE: str = "Gooya TTS Annotation Tools"
|
18 |
|
|
|
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 |
+
GDRIVE_API_KEY: str = os.environ.get("GDRIVE_API_KEY")
|
16 |
+
GDRIVE_FOLDER: str = os.environ.get("GDRIVE_FOLDER")
|
17 |
|
18 |
APP_TITLE: str = "Gooya TTS Annotation Tools"
|
19 |
|
scripts/assign_interval_to_annotator.py
CHANGED
@@ -7,9 +7,9 @@ from utils.logger import Logger
|
|
7 |
|
8 |
log = Logger()
|
9 |
|
10 |
-
START_IDX =
|
11 |
-
END_IDX =
|
12 |
-
ANNOTATOR_NAME = "
|
13 |
|
14 |
with get_db() as db:
|
15 |
annot_repo = AnnotatorRepo(db)
|
|
|
7 |
|
8 |
log = Logger()
|
9 |
|
10 |
+
START_IDX = 101
|
11 |
+
END_IDX = 120
|
12 |
+
ANNOTATOR_NAME = "vargha"
|
13 |
|
14 |
with get_db() as db:
|
15 |
annot_repo = AnnotatorRepo(db)
|
test/db_test.py
DELETED
@@ -1,26 +0,0 @@
|
|
1 |
-
import mysql.connector
|
2 |
-
from mysql.connector import Error
|
3 |
-
import config
|
4 |
-
|
5 |
-
try:
|
6 |
-
conn = mysql.connector.connect(**config.db_config)
|
7 |
-
if conn.is_connected():
|
8 |
-
print("✅OK!")
|
9 |
-
|
10 |
-
cursor = conn.cursor()
|
11 |
-
cursor.execute("SELECT id, filename, sentence FROM tts_data ORDER BY id DESC LIMIT 5")
|
12 |
-
records = cursor.fetchall()
|
13 |
-
|
14 |
-
print("🟢Last 5 records:")
|
15 |
-
for record in records:
|
16 |
-
print(record)
|
17 |
-
|
18 |
-
except Error as e:
|
19 |
-
print("❌ error:", e)
|
20 |
-
|
21 |
-
finally:
|
22 |
-
if 'cursor' in locals():
|
23 |
-
cursor.close()
|
24 |
-
if 'conn' in locals() and conn.is_connected():
|
25 |
-
conn.close()
|
26 |
-
print("⛔️Closed.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
gdrive_test.py → test/gdrive_test.py
RENAMED
@@ -1,8 +1,8 @@
|
|
1 |
import gradio as gr
|
2 |
-
from
|
3 |
from config import conf
|
4 |
|
5 |
-
LOADER = PublicFolderAudioLoader(conf.
|
6 |
|
7 |
def fetch_audio(folder_link, filename):
|
8 |
sr, wav = LOADER.load_audio(folder_link, filename)
|
@@ -12,7 +12,7 @@ demo = gr.Interface(
|
|
12 |
fn=fetch_audio,
|
13 |
inputs=[
|
14 |
gr.Textbox(label="Folder URL or ID",
|
15 |
-
value=
|
16 |
gr.Textbox(label="Filename (e.g. 0001.wav)")
|
17 |
],
|
18 |
outputs=gr.Audio(label="🔊 Audio"),
|
|
|
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)
|
|
|
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"),
|
{test → utils}/gdrive_downloader.py
RENAMED
File without changes
|