Hasanvoice commited on
Commit
2f116e7
·
verified ·
1 Parent(s): 32f0015

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +186 -123
app.py CHANGED
@@ -23,23 +23,20 @@ def init_workspace():
23
  init_workspace()
24
 
25
  # --- Load Fast Hindi Whisper Model on CPU ---
26
- # यह मॉडल हिंदी के लिए फाइन-ट्यून है और CPU पर तेज़ चलता है
27
  whisper_pipe = pipeline(
28
  "automatic-speech-recognition",
29
- model="adalat-ai/whisper-small-hi-high-lr", # छोटा, तेज़, हिंदी-स्पेशल
30
  device=-1, # CPU
31
  torch_dtype=torch.float32
32
  )
33
 
34
  # ---------------- Gemini API Rate Limit Setup ----------------
35
- # Free Tier: 5 requests per minute (सबसे कॉमन लिमिट)
36
  ONE_MINUTE = 60
37
  MAX_CALLS_PER_MINUTE = 5
38
 
39
  @sleep_and_retry
40
  @limits(calls=MAX_CALLS_PER_MINUTE, period=ONE_MINUTE)
41
  def call_gemini_api(client, prompt, uploaded_audio):
42
- """Gemini API को रेट लिमिट के साथ कॉल करें"""
43
  return client.models.generate_content(
44
  model='gemini-2.5-flash',
45
  contents=[prompt, uploaded_audio]
@@ -48,7 +45,6 @@ def call_gemini_api(client, prompt, uploaded_audio):
48
  # ---------------- Transcription Functions ----------------
49
 
50
  def transcribe_audio_cpu(audio_path):
51
- """CPU पर Whisper (Hindi Small) से ट्रांसक्रिप्शन"""
52
  if not audio_path:
53
  return "⚠️ कृपया ऑडियो फाइल अपलोड करें।"
54
  try:
@@ -61,7 +57,6 @@ def transcribe_audio_cpu(audio_path):
61
  return f"❌ ट्रांसक्रिप्शन एरर: {str(e)}"
62
 
63
  def transcribe_gemini_api(api_key, audio_path):
64
- """Gemini API से ट्रांसक्रिप्शन (रेट लिमिट के साथ)"""
65
  if not api_key:
66
  return "⚠️ कृपया ऊपर अपनी Gemini API Key डालें।"
67
  if not audio_path:
@@ -77,18 +72,81 @@ def transcribe_gemini_api(api_key, audio_path):
77
  Do not use any English alphabets. Provide ONLY the exact word-for-word final transcription.
78
  """
79
 
80
- # रेट-लिमिटेड कॉल
81
  response = call_gemini_api(client, prompt, uploaded_audio)
82
-
83
  client.files.delete(name=uploaded_audio.name)
84
  return response.text.strip()
85
 
86
  except Exception as e:
87
- # रेट लिमिट का एरर पहचानें
88
  if "429" in str(e) or "Too Many Requests" in str(e):
89
  return "⚠️ Gemini API लिमिट (5 requests/minute) क्रॉस हो गई। कृपया 1 मिनट बाद पुनः प्रयास करें।"
90
  return f"❌ Gemini एरर: {str(e)}"
91
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  # ---------------- Logic Functions for Dataset Studio ----------------
93
 
94
  def save_state_to_disk(dataset, current_idx):
@@ -100,6 +158,16 @@ def save_state_to_disk(dataset, current_idx):
100
  with open(PROGRESS_FILE, "w", encoding="utf-8") as f:
101
  f.write(str(current_idx))
102
 
 
 
 
 
 
 
 
 
 
 
103
  def load_workspace_on_start():
104
  if not os.path.exists(CSV_FILE) or not os.path.exists(AUDIO_DIR):
105
  return gr.update(visible=False), [], {}, 0, None, "", "फ़ाइल: 0 / 0"
@@ -116,6 +184,7 @@ def load_workspace_on_start():
116
  for file in files:
117
  if file.endswith('.wav'):
118
  audio_map[file] = os.path.join(root, file)
 
119
  current_idx = 0
120
  if os.path.exists(PROGRESS_FILE):
121
  with open(PROGRESS_FILE, 'r', encoding='utf-8') as f:
@@ -126,11 +195,11 @@ def load_workspace_on_start():
126
  current_idx = len(dataset) - 1
127
  if len(dataset) == 0:
128
  return gr.update(visible=False), [], {}, 0, None, "", "फ़ाइल: 0 / 0"
 
129
  item = dataset[current_idx]
130
- expected_audio_name = f"{item['id']}.wav"
131
- audio_path = audio_map.get(expected_audio_name, None)
132
- file_counter = f"फ़ाइल: {current_idx + 1} / {len(dataset)} | {expected_audio_name}"
133
- msg = f"✅ पुराना सेशन वापस लोड हो गया! (फ़ाइल {current_idx + 1} से शुरू)"
134
  return gr.update(value=msg, visible=True), dataset, audio_map, current_idx, audio_path, item['text'], file_counter
135
  except Exception as e:
136
  return gr.update(visible=False), [], {}, 0, None, "", "फ़ाइल: 0 / 0"
@@ -142,6 +211,7 @@ def process_uploads(csv_file, audio_zip):
142
  if os.path.exists(WORKSPACE_DIR):
143
  shutil.rmtree(WORKSPACE_DIR)
144
  init_workspace()
 
145
  with open(csv_file.name, 'r', encoding='utf-8') as f:
146
  lines = f.readlines()
147
  dataset = []
@@ -149,22 +219,23 @@ def process_uploads(csv_file, audio_zip):
149
  if '|' in line:
150
  parts = line.split('|', 1)
151
  dataset.append({"id": parts[0].strip(), "text": parts[1].strip()})
152
- if len(dataset) == 0:
153
- return gr.update(value="⚠️ CSV का फॉर्मेट गलत है! id|text फॉर्मेट होना चाहिए।"), [], {}, 0, None, "", "फ़ाइल: 0 / 0"
154
- dataset.sort(key=lambda x: int(''.join(filter(str.isdigit, x['id'])) or 0))
155
  with zipfile.ZipFile(audio_zip.name, 'r') as zip_ref:
156
  zip_ref.extractall(AUDIO_DIR)
 
157
  audio_map = {}
158
  for root, dirs, files in os.walk(AUDIO_DIR):
159
  for file in files:
160
  if file.endswith('.wav'):
161
  audio_map[file] = os.path.join(root, file)
 
162
  save_state_to_disk(dataset, 0)
163
- msg = f"✅ सफलतापूर्वक {len(dataset)} पंक्तियाँ और {len(audio_map)} ऑडियो लोड हो गए!"
164
  first_item = dataset[0]
165
- expected_audio_name = f"{first_item['id']}.wav"
166
- first_audio_path = audio_map.get(expected_audio_name, None)
167
- file_counter = f"फ़ाइल: 1 / {len(dataset)} | {expected_audio_name}"
 
168
  return gr.update(value=msg, visible=True), dataset, audio_map, 0, first_audio_path, first_item['text'], file_counter
169
  except Exception as e:
170
  return gr.update(value=f"❌ एरर: {str(e)}", visible=True), [], {}, 0, None, "", "फ़ाइल: 0 / 0"
@@ -176,12 +247,11 @@ def save_and_next(current_text, dataset, audio_map, current_idx):
176
  if current_idx < len(dataset) - 1:
177
  current_idx += 1
178
  else:
179
- gr.Info("🎉 आपने सभी फाइलें पूरी कर ली हैं! अब फाइनल CSV डाउनलोड करें।")
180
  save_state_to_disk(dataset, current_idx)
181
  item = dataset[current_idx]
182
- expected_audio_name = f"{item['id']}.wav"
183
- audio_path = audio_map.get(expected_audio_name, None)
184
- file_counter = f"फ़ाइल: {current_idx + 1} / {len(dataset)} | {expected_audio_name}"
185
  return dataset, current_idx, audio_path, item['text'], file_counter
186
 
187
  def go_previous(current_text, dataset, audio_map, current_idx):
@@ -192,133 +262,126 @@ def go_previous(current_text, dataset, audio_map, current_idx):
192
  current_idx -= 1
193
  save_state_to_disk(dataset, current_idx)
194
  item = dataset[current_idx]
195
- expected_audio_name = f"{item['id']}.wav"
196
- audio_path = audio_map.get(expected_audio_name, None)
197
- file_counter = f"फ़ाइल: {current_idx + 1} / {len(dataset)} | {expected_audio_name}"
198
  return dataset, current_idx, audio_path, item['text'], file_counter
199
 
200
  def delete_all_data():
201
  if os.path.exists(WORKSPACE_DIR):
202
  shutil.rmtree(WORKSPACE_DIR)
203
  init_workspace()
204
- return gr.update(value="🗑️ सारा डेटा डिलीट कर दिया गया है। नया डेटा अपलोड करें।", visible=True), [], {}, 0, None, "", "फ़ाइल: 0 / 0"
205
 
206
  def generate_final_csv():
207
  if not os.path.exists(CSV_FILE):
208
  return None
209
  return CSV_FILE
210
 
211
- # ---------------- JavaScript for Persistent API Key ----------------
212
- load_js = """
213
- function() {
214
- let key = localStorage.getItem('aatika_gemini_key');
215
- if (key) {
216
- return key;
217
- }
218
- return "";
219
- }
220
- """
221
- save_js = """
222
- function(key) {
223
- localStorage.setItem('aatika_gemini_key', key);
224
- alert("✅ API Key सुरक्षित रूप से आपके डिवाइस में सेव हो गई है!");
225
- return key;
226
- }
227
- """
228
 
229
- # ---------------- Gradio User Interface ----------------
230
  custom_css = """
231
- .studio-box { border: 1px solid #e5e7eb; border-radius: 12px; padding: 20px; background: white; box-shadow: 0 4px 6px rgba(0,0,0,0.05); }
232
- .del-btn { background-color: #ef4444 !important; color: white !important; }
 
 
 
 
 
 
233
  """
234
 
235
  with gr.Blocks(title="Aatika AI Studio", theme=gr.themes.Soft(primary_hue="indigo"), css=custom_css) as demo:
236
 
237
- dataset_state = gr.State([])
238
- audio_map_state = gr.State({})
239
- current_index_state = gr.State(0)
240
-
241
  with gr.Row():
242
- gr.Markdown("# 🎙️ Aatika AI Studio (Auto-Save + CPU Mode)")
243
- delete_btn = gr.Button("🗑️ सारा ेटा डिलीट कर", elem_classes="del-btn", size="sm")
244
 
245
- with gr.Accordion("🔑 Gemini API Key सेटअप (सिर्फ एक बार करें)", open=False):
246
  with gr.Row():
247
- api_input = gr.Textbox(label="API Key", placeholder="यहाँ पेस्ट करें...", type="password", scale=3)
248
- save_key_btn = gr.Button("सेव करें", variant="primary", scale=1)
249
 
250
  demo.load(None, None, api_input, js=load_js)
251
  save_key_btn.click(None, api_input, api_input, js=save_js)
252
 
253
- with gr.Row():
254
- with gr.Column(scale=1):
255
- gr.Markdown("### 1. डेटा अपलो करें")
256
- csv_upload = gr.File(label="📄 Choose CSV (.csv)", file_types=[".csv"])
257
- gr.Markdown("💡 **टिप:** सारे ऑडियो को एक `.zip` फाइल में पैक करके यहाँ अपलोड करें।")
258
- audio_upload = gr.File(label="🗜️ Choose Audio ZIP (.zip)", file_types=[".zip"])
259
- load_btn = gr.Button("डेटा अपलोड करें", variant="primary")
260
- status_text = gr.Markdown(visible=False)
261
-
262
- with gr.Column(scale=2, elem_classes="studio-box"):
263
- file_counter_display = gr.Markdown("### फ़ाइल: 0 / 0 | sample_0.wav")
264
- audio_player = gr.Audio(label="ऑडियो सुनें", type="filepath", interactive=False)
265
- text_editor = gr.Textbox(label="📝 स्क्रिप्ट (हिंदी देवनागरी)", lines=4)
266
 
267
  with gr.Row():
268
- gemini_ai_btn = gr.Button("✨ Gemini AI से टाइप करें (बेस्ट)", variant="primary")
269
- free_ai_btn = gr.Button("🤖 Free AI से टाइप करें (CPU)", variant="secondary")
 
 
 
270
 
271
- with gr.Row():
272
- prev_btn = gr.Button("⬅️ पिछला")
273
- save_next_btn = gr.Button("सेव अग ➡️", variant="primary")
 
 
274
 
275
- download_btn = gr.DownloadButton("⬇️ फाइनल CSV डाउनलोड करें", visible=True)
 
 
276
 
277
- # --- Events ---
278
- demo.load(
279
- fn=load_workspace_on_start,
280
- inputs=None,
281
- outputs=[status_text, dataset_state, audio_map_state, current_index_state, audio_player, text_editor, file_counter_display]
282
- )
283
- load_btn.click(
284
- fn=process_uploads,
285
- inputs=[csv_upload, audio_upload],
286
- outputs=[status_text, dataset_state, audio_map_state, current_index_state, audio_player, text_editor, file_counter_display]
287
- )
288
- save_next_btn.click(
289
- fn=save_and_next,
290
- inputs=[text_editor, dataset_state, audio_map_state, current_index_state],
291
- outputs=[dataset_state, current_index_state, audio_player, text_editor, file_counter_display]
292
- )
293
- prev_btn.click(
294
- fn=go_previous,
295
- inputs=[text_editor, dataset_state, audio_map_state, current_index_state],
296
- outputs=[dataset_state, current_index_state, audio_player, text_editor, file_counter_display]
297
- )
298
- delete_btn.click(
299
- fn=delete_all_data,
300
- inputs=None,
301
- outputs=[status_text, dataset_state, audio_map_state, current_index_state, audio_player, text_editor, file_counter_display]
302
- )
303
-
304
- # Free AI (CPU) – नया तेज़ मॉडल
305
- free_ai_btn.click(
306
- fn=transcribe_audio_cpu,
307
- inputs=[audio_player],
308
- outputs=[text_editor]
309
- )
310
-
311
- # Gemini AI रेट लिमिट के साथ
312
- gemini_ai_btn.click(
313
- fn=transcribe_gemini_api,
314
- inputs=[api_input, audio_player],
315
- outputs=[text_editor]
316
- )
317
-
318
- download_btn.click(
319
- fn=generate_final_csv,
320
- inputs=None,
321
- outputs=[download_btn]
322
- )
 
 
 
 
 
 
323
 
324
- demo.launch()
 
23
  init_workspace()
24
 
25
  # --- Load Fast Hindi Whisper Model on CPU ---
 
26
  whisper_pipe = pipeline(
27
  "automatic-speech-recognition",
28
+ model="adalat-ai/whisper-small-hi-high-lr",
29
  device=-1, # CPU
30
  torch_dtype=torch.float32
31
  )
32
 
33
  # ---------------- Gemini API Rate Limit Setup ----------------
 
34
  ONE_MINUTE = 60
35
  MAX_CALLS_PER_MINUTE = 5
36
 
37
  @sleep_and_retry
38
  @limits(calls=MAX_CALLS_PER_MINUTE, period=ONE_MINUTE)
39
  def call_gemini_api(client, prompt, uploaded_audio):
 
40
  return client.models.generate_content(
41
  model='gemini-2.5-flash',
42
  contents=[prompt, uploaded_audio]
 
45
  # ---------------- Transcription Functions ----------------
46
 
47
  def transcribe_audio_cpu(audio_path):
 
48
  if not audio_path:
49
  return "⚠️ कृपया ऑडियो फाइल अपलोड करें।"
50
  try:
 
57
  return f"❌ ट्रांसक्रिप्शन एरर: {str(e)}"
58
 
59
  def transcribe_gemini_api(api_key, audio_path):
 
60
  if not api_key:
61
  return "⚠️ कृपया ऊपर अपनी Gemini API Key डालें।"
62
  if not audio_path:
 
72
  Do not use any English alphabets. Provide ONLY the exact word-for-word final transcription.
73
  """
74
 
 
75
  response = call_gemini_api(client, prompt, uploaded_audio)
 
76
  client.files.delete(name=uploaded_audio.name)
77
  return response.text.strip()
78
 
79
  except Exception as e:
 
80
  if "429" in str(e) or "Too Many Requests" in str(e):
81
  return "⚠️ Gemini API लिमिट (5 requests/minute) क्रॉस हो गई। कृपया 1 मिनट बाद पुनः प्रयास करें।"
82
  return f"❌ Gemini एरर: {str(e)}"
83
 
84
+ # ---------------- Auto Dataset Creator (New Feature) ----------------
85
+
86
+ def auto_create_dataset(audio_file, progress=gr.Progress()):
87
+ """बड़े ऑडियो को काटकर डेटासेट बनाता है"""
88
+ if not audio_file:
89
+ return None, None, "⚠️ कृपया ऑडियो फाइल अपलोड करें।"
90
+
91
+ try:
92
+ from pydub import AudioSegment
93
+ from pydub.silence import split_on_silence
94
+ except ImportError:
95
+ return None, None, "❌ pydub लाइब्रेरी इन्स्टॉल नहीं है। कृपया इसे इन्स्टॉल करें।"
96
+
97
+ progress(0.1, desc="ऑडियो लोड हो रहा है...")
98
+ try:
99
+ audio = AudioSegment.from_file(audio_file.name)
100
+ except Exception as e:
101
+ return None, None, f"❌ ऑडियो लोड करने में एरर: {str(e)}"
102
+
103
+ progress(0.3, desc="आवाज़ के आधार पर ऑडियो काटा जा रहा है...")
104
+ chunks = split_on_silence(
105
+ audio,
106
+ min_silence_len=500, # 500ms की शांति पर काटेगा
107
+ silence_thresh=audio.dBFS-14,
108
+ keep_silence=250
109
+ )
110
+
111
+ if not chunks:
112
+ chunks = [audio]
113
+
114
+ output_dir = os.path.join(WORKSPACE_DIR, "auto_generated")
115
+ if os.path.exists(output_dir):
116
+ shutil.rmtree(output_dir)
117
+ os.makedirs(output_dir)
118
+
119
+ csv_data = ""
120
+
121
+ for i, chunk in enumerate(chunks):
122
+ chunk_name = f"sample_{i}.wav"
123
+ chunk_path = os.path.join(output_dir, chunk_name)
124
+ chunk.export(chunk_path, format="wav")
125
+
126
+ progress((0.4 + (0.5 * (i/len(chunks)))), desc=f"टुकड़ा {i+1}/{len(chunks)} का टेक्स्ट बन रहा है...")
127
+
128
+ try:
129
+ res = whisper_pipe(chunk_path, generate_kwargs={"language": "hi", "task": "transcribe"})
130
+ text = res["text"].strip()
131
+ except:
132
+ text = "Error"
133
+
134
+ csv_data += f"sample_{i}|{text}\n"
135
+
136
+ csv_path = os.path.join(output_dir, "metadata.csv")
137
+ with open(csv_path, "w", encoding="utf-8") as f:
138
+ f.write(csv_data)
139
+
140
+ zip_path = os.path.join(WORKSPACE_DIR, "auto_dataset.zip")
141
+ with zipfile.ZipFile(zip_path, 'w') as zipf:
142
+ # ZIP के अंदर wavs फोल्डर बनाएं (जैसा आपके 141204.jpg में है)
143
+ zipf.write(csv_path, arcname="metadata.csv")
144
+ for i in range(len(chunks)):
145
+ zipf.write(os.path.join(output_dir, f"sample_{i}.wav"), arcname=f"wavs/sample_{i}.wav")
146
+
147
+ progress(1.0, desc="डेटासेट तैयार!")
148
+ return csv_path, zip_path, f"✅ {len(chunks)} टुकड़ों का डेटासेट तैयार है! इन्हें डाउनलोड करें और 'स्टूडियो' में अपलोड करें।"
149
+
150
  # ---------------- Logic Functions for Dataset Studio ----------------
151
 
152
  def save_state_to_disk(dataset, current_idx):
 
158
  with open(PROGRESS_FILE, "w", encoding="utf-8") as f:
159
  f.write(str(current_idx))
160
 
161
+ def get_audio_path_safely(audio_map, item_id):
162
+ # यह फंक्शन wavs फोल्डर के अंदर या बाहर दोनों जगह ऑडियो ढूंढता है
163
+ names_to_try = [f"{item_id}.wav", f"wavs/{item_id}.wav"]
164
+ for name in names_to_try:
165
+ # audio_map में key सिर्फ फाइल का नाम हो सकती है, इसलिए डायरेक्ट चेक करें
166
+ for key, full_path in audio_map.items():
167
+ if full_path.endswith(name) or key == f"{item_id}.wav":
168
+ return full_path
169
+ return None
170
+
171
  def load_workspace_on_start():
172
  if not os.path.exists(CSV_FILE) or not os.path.exists(AUDIO_DIR):
173
  return gr.update(visible=False), [], {}, 0, None, "", "फ़ाइल: 0 / 0"
 
184
  for file in files:
185
  if file.endswith('.wav'):
186
  audio_map[file] = os.path.join(root, file)
187
+
188
  current_idx = 0
189
  if os.path.exists(PROGRESS_FILE):
190
  with open(PROGRESS_FILE, 'r', encoding='utf-8') as f:
 
195
  current_idx = len(dataset) - 1
196
  if len(dataset) == 0:
197
  return gr.update(visible=False), [], {}, 0, None, "", "फ़ाइल: 0 / 0"
198
+
199
  item = dataset[current_idx]
200
+ audio_path = get_audio_path_safely(audio_map, item['id'])
201
+ file_counter = f"फ़ाइल: {current_idx + 1} / {len(dataset)} | {item['id']}.wav"
202
+ msg = f"✅ पुराना सेशन लोड हो गया! (फ़ाइल {current_idx + 1} से शुरू)"
 
203
  return gr.update(value=msg, visible=True), dataset, audio_map, current_idx, audio_path, item['text'], file_counter
204
  except Exception as e:
205
  return gr.update(visible=False), [], {}, 0, None, "", "फ़ाइल: 0 / 0"
 
211
  if os.path.exists(WORKSPACE_DIR):
212
  shutil.rmtree(WORKSPACE_DIR)
213
  init_workspace()
214
+
215
  with open(csv_file.name, 'r', encoding='utf-8') as f:
216
  lines = f.readlines()
217
  dataset = []
 
219
  if '|' in line:
220
  parts = line.split('|', 1)
221
  dataset.append({"id": parts[0].strip(), "text": parts[1].strip()})
222
+
 
 
223
  with zipfile.ZipFile(audio_zip.name, 'r') as zip_ref:
224
  zip_ref.extractall(AUDIO_DIR)
225
+
226
  audio_map = {}
227
  for root, dirs, files in os.walk(AUDIO_DIR):
228
  for file in files:
229
  if file.endswith('.wav'):
230
  audio_map[file] = os.path.join(root, file)
231
+
232
  save_state_to_disk(dataset, 0)
233
+
234
  first_item = dataset[0]
235
+ first_audio_path = get_audio_path_safely(audio_map, first_item['id'])
236
+ file_counter = f"फ़ाइल: 1 / {len(dataset)} | {first_item['id']}.wav"
237
+
238
+ msg = f"✅ {len(dataset)} पंक्तियाँ और {len(audio_map)} ऑडियो लोड हो गए!"
239
  return gr.update(value=msg, visible=True), dataset, audio_map, 0, first_audio_path, first_item['text'], file_counter
240
  except Exception as e:
241
  return gr.update(value=f"❌ एरर: {str(e)}", visible=True), [], {}, 0, None, "", "फ़ाइल: 0 / 0"
 
247
  if current_idx < len(dataset) - 1:
248
  current_idx += 1
249
  else:
250
+ gr.Info("🎉 आपने सभी फाइलें पूरी कर ली हैं!")
251
  save_state_to_disk(dataset, current_idx)
252
  item = dataset[current_idx]
253
+ audio_path = get_audio_path_safely(audio_map, item['id'])
254
+ file_counter = f"फ़ाइल: {current_idx + 1} / {len(dataset)} | {item['id']}.wav"
 
255
  return dataset, current_idx, audio_path, item['text'], file_counter
256
 
257
  def go_previous(current_text, dataset, audio_map, current_idx):
 
262
  current_idx -= 1
263
  save_state_to_disk(dataset, current_idx)
264
  item = dataset[current_idx]
265
+ audio_path = get_audio_path_safely(audio_map, item['id'])
266
+ file_counter = f"फ़ाइल: {current_idx + 1} / {len(dataset)} | {item['id']}.wav"
 
267
  return dataset, current_idx, audio_path, item['text'], file_counter
268
 
269
  def delete_all_data():
270
  if os.path.exists(WORKSPACE_DIR):
271
  shutil.rmtree(WORKSPACE_DIR)
272
  init_workspace()
273
+ return gr.update(value="🗑️ सारा डेटा डिलीट कर दिया गया है।", visible=True), [], {}, 0, None, "", "फ़ाइल: 0 / 0"
274
 
275
  def generate_final_csv():
276
  if not os.path.exists(CSV_FILE):
277
  return None
278
  return CSV_FILE
279
 
280
+ # ---------------- JS for API Key ----------------
281
+ load_js = """function() { return localStorage.getItem('aatika_gemini_key') || ""; }"""
282
+ save_js = """function(key) { localStorage.setItem('aatika_gemini_key', key); alert("✅ सेव हो गई!"); return key; }"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
 
284
+ # ---------------- UI Setup ----------------
285
  custom_css = """
286
+ /* मोबाइल के लिए फॉन्ट और पैडिंग एडजस्टमेंट */
287
+ h2, h3 { font-size: 1.2rem !important; margin-bottom: 5px !important; }
288
+ .studio-box { border: 1px solid #e5e7eb; border-radius: 8px; padding: 10px; background: white; }
289
+ .del-btn { background-color: #ef4444 !important; color: white !important; font-size: 0.9rem !important; }
290
+ @media (max-width: 600px) {
291
+ .gradio-container { padding: 5px !important; }
292
+ button { padding: 8px !important; font-size: 0.9rem !important; }
293
+ }
294
  """
295
 
296
  with gr.Blocks(title="Aatika AI Studio", theme=gr.themes.Soft(primary_hue="indigo"), css=custom_css) as demo:
297
 
 
 
 
 
298
  with gr.Row():
299
+ gr.Markdown("## 🎙️ Aatika AI Studio")
300
+ delete_btn = gr.Button("🗑️ डिलीट टा", elem_classes="del-btn", size="sm")
301
 
302
+ with gr.Accordion("🔑 Gemini API Key सेटअप", open=False):
303
  with gr.Row():
304
+ api_input = gr.Textbox(label="API Key", type="password", scale=3)
305
+ save_key_btn = gr.Button("सेव", variant="primary", scale=1)
306
 
307
  demo.load(None, None, api_input, js=load_js)
308
  save_key_btn.click(None, api_input, api_input, js=save_js)
309
 
310
+ with gr.Tabs():
311
+ # ------------- TAB 1: Main Studio -------------
312
+ with gr.Tab("📝 डेटासेट स्टूियो"):
313
+ dataset_state = gr.State([])
314
+ audio_map_state = gr.State({})
315
+ current_index_state = gr.State(0)
 
 
 
 
 
 
 
316
 
317
  with gr.Row():
318
+ with gr.Column(scale=1):
319
+ csv_upload = gr.File(label="📄 CSV (.csv)", file_types=[".csv"])
320
+ audio_upload = gr.File(label="🗜️ Audio ZIP (.zip)", file_types=[".zip"])
321
+ load_btn = gr.Button("डेटा अपलोड करें", variant="primary")
322
+ status_text = gr.Markdown(visible=False)
323
 
324
+ with gr.Column(scale=2, elem_classes="studio-box"):
325
+ file_counter_display = gr.Markdown("### फ़इल: 0 / 0 | ...")
326
+ # autoplay=True जोड़ा गया है ताकि तुंत प् हो सके
327
+ audio_player = gr.Audio(label="ऑडियो सुनें", type="filepath", interactive=False, autoplay=True)
328
+ text_editor = gr.Textbox(label="📝 स्क्रिप्ट (हिंदी)", lines=3)
329
 
330
+ with gr.Row():
331
+ gemini_ai_btn = gr.Button("✨ Gemini AI", variant="primary")
332
+ free_ai_btn = gr.Button("🤖 CPU AI", variant="secondary")
333
 
334
+ with gr.Row():
335
+ prev_btn = gr.Button("⬅️ पिछला")
336
+ save_next_btn = gr.Button("सेव और अगला ➡️", variant="primary")
337
+
338
+ download_btn = gr.DownloadButton("⬇️ फाइनल CSV डाउनलोड", visible=True)
339
+
340
+ # Events for Tab 1
341
+ demo.load(
342
+ fn=load_workspace_on_start,
343
+ outputs=[status_text, dataset_state, audio_map_state, current_index_state, audio_player, text_editor, file_counter_display]
344
+ )
345
+ load_btn.click(
346
+ fn=process_uploads,
347
+ inputs=[csv_upload, audio_upload],
348
+ outputs=[status_text, dataset_state, audio_map_state, current_index_state, audio_player, text_editor, file_counter_display]
349
+ )
350
+ save_next_btn.click(
351
+ fn=save_and_next,
352
+ inputs=[text_editor, dataset_state, audio_map_state, current_index_state],
353
+ outputs=[dataset_state, current_index_state, audio_player, text_editor, file_counter_display]
354
+ )
355
+ prev_btn.click(
356
+ fn=go_previous,
357
+ inputs=[text_editor, dataset_state, audio_map_state, current_index_state],
358
+ outputs=[dataset_state, current_index_state, audio_player, text_editor, file_counter_display]
359
+ )
360
+ delete_btn.click(
361
+ fn=delete_all_data,
362
+ outputs=[status_text, dataset_state, audio_map_state, current_index_state, audio_player, text_editor, file_counter_display]
363
+ )
364
+ free_ai_btn.click(fn=transcribe_audio_cpu, inputs=[audio_player], outputs=[text_editor])
365
+ gemini_ai_btn.click(fn=transcribe_gemini_api, inputs=[api_input, audio_player], outputs=[text_editor])
366
+ download_btn.click(fn=generate_final_csv, outputs=[download_btn])
367
+
368
+ # ------------- TAB 2: Auto Dataset Creator -------------
369
+ with gr.Tab("✂️ ऑटो-डेटासेट क्रिएटर (MP3)"):
370
+ gr.Markdown("यहाँ एक बड़ा MP3 या WAV ऑडियो डालें। AI (CPU) खुद-ब-खुद इसे छोटे टुकड़ों में काटेगा, टेक्स्ट लिखेगा, और आपको सीधे **CSV** और **ZIP** फाइल दे देगा जिसे आप पहले टैब में अपलोड कर सकते हैं।")
371
+
372
+ with gr.Row():
373
+ raw_audio_input = gr.File(label="बड़ा ऑडियो फाइल चुनें (MP3/WAV)", file_types=["audio"])
374
+ auto_create_btn = gr.Button("ऑटोमेटिक डेटासेट बनाएँ 🚀", variant="primary")
375
+
376
+ auto_status = gr.Markdown()
377
+ with gr.Row():
378
+ out_csv = gr.File(label="तैयार CSV डाउनलोड करें")
379
+ out_zip = gr.File(label="तैयार Audio ZIP डाउनलोड करें")
380
+
381
+ auto_create_btn.click(
382
+ fn=auto_create_dataset,
383
+ inputs=[raw_audio_input],
384
+ outputs=[out_csv, out_zip, auto_status]
385
+ )
386
 
387
+ demo.launch()