Spaces:
Sleeping
Sleeping
Update app.py
Browse files
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 |
-
|
| 131 |
-
|
| 132 |
-
|
| 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 |
-
|
| 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 |
-
|
| 164 |
first_item = dataset[0]
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
|
|
|
| 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("🎉 आपने सभी फाइलें पूरी कर ली हैं!
|
| 180 |
save_state_to_disk(dataset, current_idx)
|
| 181 |
item = dataset[current_idx]
|
| 182 |
-
|
| 183 |
-
|
| 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 |
-
|
| 196 |
-
|
| 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="🗑️ सारा डेटा डिलीट कर दिया गया है।
|
| 205 |
|
| 206 |
def generate_final_csv():
|
| 207 |
if not os.path.exists(CSV_FILE):
|
| 208 |
return None
|
| 209 |
return CSV_FILE
|
| 210 |
|
| 211 |
-
# ----------------
|
| 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 |
-
# ----------------
|
| 230 |
custom_css = """
|
| 231 |
-
|
| 232 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 243 |
-
delete_btn = gr.Button("🗑️
|
| 244 |
|
| 245 |
-
with gr.Accordion("🔑 Gemini API Key सेटअप
|
| 246 |
with gr.Row():
|
| 247 |
-
api_input = gr.Textbox(label="API Key",
|
| 248 |
-
save_key_btn = gr.Button("सेव
|
| 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.
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 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 |
-
|
| 269 |
-
|
|
|
|
|
|
|
|
|
|
| 270 |
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
|
|
|
|
|
|
| 274 |
|
| 275 |
-
|
|
|
|
|
|
|
| 276 |
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 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()
|