Navid Arabi commited on
Commit
7a295c7
·
1 Parent(s): c8c252f

add audio player

Browse files
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="Annotation is Validate",
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) as self.right_col:
 
65
  self.audio = gr.Audio(label="🔊 Audio", interactive=False)
66
-
67
 
68
- # stateهای مخفی
69
- self.items_state = gr.State([]) # list[dict]
70
- self.idx_state = gr.State(0) # اندیس فعلی
71
 
72
- # ───────── wiring ───────── #
73
  def register_callbacks(
74
- self,
75
- login_page,
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
- # ---------- helpers ---------- #
84
- def _audio_path(filename: str) -> str:
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
- # 6 فیلد متنی + 3 فیلد صوت + validated
101
- return [
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
- data["id"],
119
- data["filename"],
120
- data["sentence"],
121
- data.get("annotated_sentence", ""),
122
- data.get("annotated_at", ""),
123
- bool(data.get("validated", False)),
124
- wav_path, # audio
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: list, idx: int):
130
  return min(idx + 1, max(len(items) - 1, 0))
131
 
132
- def prev_idx(items: list, idx: int):
133
  return max(idx - 1, 0)
134
 
135
- # ---------- initial load ---------- #
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
- # ---------- prev / next buttons ---------- #
163
- for btn, fn_nav in [(self.btn_prev, prev_idx), (self.btn_next, next_idx)]:
164
  (
165
  btn.click(
166
- fn=fn_nav,
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
- # ---------- Play-Selection button ---------- #
187
- def play_selection(wav_path: str, start: float, end: float):
188
- """
189
- بخش انتخاب‌شده از فایل را جدا می‌کند و به‌صورت
190
- (sr, np.array) برمی‌گرداند تا در Player پخش شود.
191
- """
192
- if not wav_path or not os.path.exists(wav_path):
 
 
 
 
 
 
 
 
 
 
193
  return None
194
  try:
195
- seg = AudioSegment.from_file(wav_path)
196
- start_ms = int(max(start, 0) * 1000)
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"Cannot slice audio '{wav_path}': {e}")
206
- return None
207
-
208
- self.play_btn.click(
209
- fn=play_selection,
210
- inputs=[self.audio, self.start_slider, self.end_slider],
211
- outputs=self.audio,
 
 
 
 
 
 
 
 
 
 
 
 
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
- GOOGLE_DRIVE_API_KEY: str = os.environ.get("GOOGLE_DRIVE_API_KEY")
 
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 = 1
11
- END_IDX = 100
12
- ANNOTATOR_NAME = "navid"
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 test.gdrive_downloader import PublicFolderAudioLoader
3
  from config import conf
4
 
5
- LOADER = PublicFolderAudioLoader(conf.GOOGLE_DRIVE_API_KEY)
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="https://drive.google.com/drive/folders/15UllyqvOB8zmhzsTL8f1wmnK4OY2nzUQ?usp=sharing"),
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