Turbiling commited on
Commit
19b8b76
·
verified ·
1 Parent(s): 5e0c096

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +116 -255
app.py CHANGED
@@ -1,30 +1,19 @@
1
- # app.py
2
- # SmartTranscribe - Single-file Hugging Face Space (Gradio)
3
- # Requirements (put in requirements.txt for the Space):
4
- # gradio>=3.0
5
- # requests
6
- # yt-dlp
7
- # python-dotenv
8
- # ffmpeg (system package, usually present on Spaces)
9
- #
10
- # Environment variables required:
11
- # GROQ_API_KEY
12
- # HUGGINGFACE_API_TOKEN
13
 
14
  import os
15
- import tempfile
16
- import subprocess
17
- import json
18
- from pathlib import Path
19
-
20
- import requests
21
  import gradio as gr
22
- import yt_dlp
23
-
24
- # --------- Configuration / Endpoints ----------
25
- GROQ_TRANSCRIPTION_URL = "https://api.groq.com/openai/v1/audio/transcriptions"
26
- HUGGINGFACE_INFERENCE_URL = "https://api-inference.huggingface.co/models/openai/gpt-oss-120b"
 
27
 
 
 
 
28
  GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
29
  HUGGINGFACE_API_TOKEN = os.environ.get("HUGGINGFACE_API_TOKEN")
30
 
@@ -33,268 +22,140 @@ if not GROQ_API_KEY or not HUGGINGFACE_API_TOKEN:
33
  "Environment variables GROQ_API_KEY and HUGGINGFACE_API_TOKEN must be set."
34
  )
35
 
36
- # --------- Helper utilities ---------
 
37
 
38
- def download_youtube_audio(youtube_url: str, out_path: str) -> str:
39
- """
40
- Download best audio from YouTube and convert to WAV using yt-dlp + ffmpeg.
41
- Returns path to the WAV file.
42
- """
43
- out_base = out_path
44
- ydl_opts = {
45
- "format": "bestaudio/best",
46
- "outtmpl": out_base + ".%(ext)s",
47
- "quiet": True,
48
- "no_warnings": True,
49
- "postprocessors": [
50
- {
51
- "key": "FFmpegExtractAudio",
52
- "preferredcodec": "wav",
53
- "preferredquality": "192",
54
- }
55
- ],
56
- }
57
- with yt_dlp.YoutubeDL(ydl_opts) as ydl:
58
- ydl.extract_info(youtube_url, download=True)
59
- wav_path = out_base + ".wav"
60
- if not Path(wav_path).exists():
61
- raise FileNotFoundError("YouTube audio download failed or ffmpeg postprocessing missing.")
62
- return wav_path
63
 
64
- def convert_to_wav(input_path: str) -> str:
65
- """
66
- Convert input file to 16kHz mono WAV (overwrites if same name exists).
67
- If input already .wav, returns it.
68
- """
69
- p = Path(input_path)
70
- if p.suffix.lower() == ".wav":
71
- return input_path
72
- out = str(p.with_suffix(".wav"))
73
- cmd = [
74
- "ffmpeg",
75
- "-y",
76
- "-i",
77
- str(input_path),
78
- "-ar",
79
- "16000",
80
- "-ac",
81
- "1",
82
- out,
83
- ]
84
- subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
85
- return out
86
 
87
- def transcribe_with_groq(audio_wav_path: str, api_key: str, language: str = None) -> dict:
88
  """
89
- Send audio file to Groq transcription endpoint.
90
- language: 'ur' or 'en' or None/'auto'
91
- Returns JSON response (expects at least 'text' or similar).
92
  """
93
- headers = {"Authorization": f"Bearer {api_key}"}
94
- data = {}
95
- if language and language != "auto":
96
- # pass human-friendly label; endpoint handlers may vary
97
- data["language"] = "urdu" if language == "ur" else "english"
98
- # If we want special behavior for Urdu, add prompt
99
- if data.get("language") == "urdu":
100
- data["prompt"] = (
101
- "Transcribe speech in Urdu. If English words are present in the Urdu audio, "
102
- "write them using Urdu script (مثلاً 'school' -> 'اسکول'). "
103
- "Preserve correct Urdu punctuation and grammar."
104
- )
105
- files = {"file": open(audio_wav_path, "rb")}
106
  try:
107
- resp = requests.post(GROQ_TRANSCRIPTION_URL, headers=headers, data=data, files=files, timeout=120)
108
- resp.raise_for_status()
109
- return resp.json()
110
- finally:
111
- files["file"].close()
 
112
 
113
- def hf_chat_completion(prompt: str, hf_token: str, max_tokens: int = 512) -> str:
114
- """
115
- Simple wrapper to call Hugging Face Inference API for openai/gpt-oss-120b
116
- """
117
- headers = {"Authorization": f"Bearer {hf_token}", "Content-Type": "application/json"}
118
- payload = {"inputs": prompt, "parameters": {"max_new_tokens": max_tokens}}
119
- r = requests.post(HUGGINGFACE_INFERENCE_URL, headers=headers, json=payload, timeout=180)
120
- r.raise_for_status()
121
- out = r.json()
122
- # Standard HF inference output handlers
123
- if isinstance(out, dict) and "generated_text" in out:
124
- return out["generated_text"]
125
- if isinstance(out, list) and len(out) > 0 and isinstance(out[0], dict) and "generated_text" in out[0]:
126
- return out[0]["generated_text"]
127
- if isinstance(out, str):
128
- return out
129
- return json.dumps(out)
130
-
131
- def normalize_urdu_text_with_gpt(transcript: str, hf_token: str) -> str:
132
- """
133
- Use GPT-OSS to convert embedded English words to Urdu script and fix punctuation/grammar.
134
- Returns normalized Urdu text only.
135
- """
136
- prompt = (
137
- "You are an expert in Urdu orthography and transliteration.\n"
138
- "Task: Convert the following Urdu transcription into correct, well-punctuated Urdu script.\n"
139
- "Whenever English words appear inside the Urdu text, transliterate them into Urdu alphabets "
140
- "(for example: 'school' -> 'اسکول') while preserving meaning and grammar.\n"
141
- "Return only the corrected Urdu transcription — do not include any explanations.\n\n"
142
- "Transcription:\n" + transcript + "\n\nCorrected transcription:"
143
- )
144
- return hf_chat_completion(prompt, hf_token, max_tokens=1024)
145
 
146
- def summarize_with_gpt(text: str, hf_token: str) -> str:
147
- """
148
- Summarize input text using GPT-OSS-120B. Keeps the language the same as input.
149
- Returns short summary and bullet key-takeaways.
150
- """
151
- prompt = (
152
- "Summarize the following text. Output a short summary (3-6 sentences) "
153
- "followed by bullet-point key takeaways. Keep the language the same as the input.\n\n"
154
- "Text:\n" + text
155
- )
156
- return hf_chat_completion(prompt, hf_token, max_tokens=256)
157
 
158
- # --------- Core processing pipeline ---------
159
 
160
- def process_audio_file(audio_path: str, force_language: str = "auto"):
161
- """
162
- Full pipeline:
163
- - ensure WAV
164
- - transcribe via Groq
165
- - if Urdu (detected or forced), normalize English words to Urdu script using GPT
166
- - summarize using GPT-OSS
167
- Returns dict: transcript (raw), normalized_transcript, summary, detected_language
168
- """
169
- wav_path = convert_to_wav(audio_path)
170
- try:
171
- groq_resp = transcribe_with_groq(wav_path, api_key=GROQ_API_KEY, language=force_language)
172
  except Exception as e:
173
- return {"error": f"Transcription error: {e}"}
174
-
175
- # Groq response shape may vary; attempt to extract text and language
176
- transcript = ""
177
- detected_language = None
178
- if isinstance(groq_resp, dict):
179
- # common keys: text, transcription
180
- transcript = groq_resp.get("text") or groq_resp.get("transcription") or groq_resp.get("result") or ""
181
- # sometimes 'language' may be provided
182
- detected_language = groq_resp.get("language") or groq_resp.get("detected_language") or None
183
- # fallback: if transcript is nested
184
- if not transcript and "segments" in groq_resp and isinstance(groq_resp["segments"], list):
185
- transcript = " ".join([seg.get("text", "") for seg in groq_resp["segments"]])
186
- elif isinstance(groq_resp, str):
187
- transcript = groq_resp
188
 
189
- normalized = transcript
190
- # Decide whether to normalize to Urdu script:
191
- do_urdu_normalize = False
192
- if force_language == "ur":
193
- do_urdu_normalize = True
194
- elif detected_language and isinstance(detected_language, str) and detected_language.lower().startswith("ur"):
195
- do_urdu_normalize = True
196
- # If transcript contains significant Urdu characters, we may still want normalization,
197
- # but we rely on explicit detection/force for reliability.
198
- if do_urdu_normalize and transcript.strip():
199
- try:
200
- normalized = normalize_urdu_text_with_gpt(transcript, HUGGINGFACE_API_TOKEN)
201
- except Exception as e:
202
- normalized = transcript + f"\n\n[Normalization failed: {e}]"
203
 
204
- # Summarize (use normalized text if available)
205
- to_summarize = normalized if normalized else transcript
206
- summary = ""
207
- if to_summarize.strip():
208
- try:
209
- summary = summarize_with_gpt(to_summarize, HUGGINGFACE_API_TOKEN)
210
- except Exception as e:
211
- summary = f"Summary failed: {e}"
212
-
213
- return {
214
- "transcript": transcript,
215
- "normalized_transcript": normalized,
216
- "summary": summary,
217
- "detected_language": detected_language,
218
  }
 
 
 
219
 
220
- # --------- Gradio UI callbacks ---------
221
 
222
- def transcribe_upload(file, language_choice):
223
  """
224
- file: path to uploaded/recorded file (gr.Audio returns a file path)
225
- language_choice: 'auto', 'ur', 'en'
226
  """
227
- if not file:
228
- return "", "", "براہِ مہربانی آڈیو/ویڈیو فائل اپلوڈ کریں۔"
229
  try:
230
- result = process_audio_file(file, force_language=language_choice)
 
 
 
 
 
 
 
231
  except Exception as e:
232
- return "", "", f"پروسیسنگ میں خرابی: {e}"
233
- if "error" in result:
234
- return "", "", result["error"]
235
- return result.get("transcript", ""), result.get("normalized_transcript", ""), result.get("summary", "")
236
 
237
- def transcribe_youtube(youtube_url, language_choice):
238
- if not youtube_url:
239
- return "", "", "براہِ مہربانی YouTube کا URL فراہم کریں۔"
240
- tmp = tempfile.NamedTemporaryFile(suffix=".wav", delete=False)
241
- out_base = tmp.name[:-4] # remove .wav
242
- tmp.close()
243
- try:
244
- wav_path = download_youtube_audio(youtube_url, out_base)
245
- result = process_audio_file(wav_path, force_language=language_choice)
246
- except Exception as e:
247
- return "", "", f"YouTube یا کنورژن میں خرابی: {e}"
248
- finally:
249
- # cleanup downloaded file(s)
250
- try:
251
- if os.path.exists(out_base + ".wav"):
252
- os.remove(out_base + ".wav")
253
- except:
254
- pass
255
- if "error" in result:
256
- return "", "", result["error"]
257
- return result.get("transcript", ""), result.get("normalized_transcript", ""), result.get("summary", "")
258
 
259
- # --------- Gradio App Layout ---------
 
 
 
260
 
261
- title_md = """
262
- # SmartTranscribe — اردو / English ٹرانسکرِپشن + خلاصہ
263
- یہ ایپ آڈیو یا ویڈیو کو اسی زبان میں متن میں بدلتی ہے۔ اگر اردو ریکارڈنگ میں انگریزی الفاظ ہوں تو وہ اردو رسمِ حروف میں لکھے جائیں گے، پھر خلاصہ `openai/gpt-oss-120b` سے بنایا جائے گا۔
264
- """
265
 
266
- with gr.Blocks(title="SmartTranscribe") as demo:
267
- gr.Markdown(title_md)
268
 
269
- with gr.Tab("Upload / Record"):
270
- gr.Markdown("اپنی فائل اپلوڈ کریں یا براہِ راست ریکارڈ کریں:")
271
- audio_input = gr.Audio(source="upload", type="filepath", label="Upload / Record audio or video")
272
- language = gr.Radio(["auto", "ur", "en"], value="auto", label="زبان منتخب کریں (عام حالت: auto)")
273
- transcribe_btn = gr.Button("Transcribe & Summarize")
274
 
275
- out_transcript = gr.Textbox(label="Transcription (raw)", lines=8)
276
- out_normalized = gr.Textbox(label="Transcription (normalized — Urdu script when applicable)", lines=8)
277
- out_summary = gr.Textbox(label="Summary (GPT-OSS)", lines=6)
278
 
279
- transcribe_btn.click(fn=transcribe_upload, inputs=[audio_input, language], outputs=[out_transcript, out_normalized, out_summary])
 
 
 
 
280
 
281
- with gr.Tab("YouTube Link"):
282
- gr.Markdown("YouTube کا URL پیسٹ کریں:")
283
- yt_url = gr.Textbox(label="YouTube URL")
284
- yt_lang = gr.Radio(["auto", "ur", "en"], value="auto", label="زبان منتخب کریں")
285
- yt_btn = gr.Button("Fetch, Transcribe & Summarize")
 
286
 
287
- yt_out_transcript = gr.Textbox(label="Transcription (raw)", lines=8)
288
- yt_out_normalized = gr.Textbox(label="Transcription (normalized)", lines=8)
289
- yt_out_summary = gr.Textbox(label="Summary (GPT-OSS)", lines=6)
 
 
 
 
 
 
 
290
 
291
- yt_btn.click(fn=transcribe_youtube, inputs=[yt_url, yt_lang], outputs=[yt_out_transcript, yt_out_normalized, yt_out_summary])
 
 
 
 
292
 
293
- gr.Markdown("""
294
- ---
295
- **ہدایات:** Space سیٹنگز میں `GROQ_API_KEY` اور `HUGGINGFACE_API_TOKEN` بطور Secrets شامل کریں۔
296
- لمبی فائلز کے لیے ریئل-ٹائم کی جگہ چنکس بنائے جائیں گے — پروسیسنگ کا وقت ماڈیولز اور لمبائی پر منحصر ہوگا۔
297
- """)
 
 
 
 
 
 
 
 
298
 
 
 
 
299
  if __name__ == "__main__":
300
- demo.launch()
 
1
+ # =========================
2
+ # SmartTranscribe - Updated Version (For Hugging Face Spaces)
3
+ # =========================
 
 
 
 
 
 
 
 
 
4
 
5
  import os
 
 
 
 
 
 
6
  import gradio as gr
7
+ import requests
8
+ from groq import Groq
9
+ from datetime import datetime
10
+ from pathlib import Path
11
+ import tempfile
12
+ from huggingface_hub import InferenceClient
13
 
14
+ # -------------------------
15
+ # Environment Variables
16
+ # -------------------------
17
  GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
18
  HUGGINGFACE_API_TOKEN = os.environ.get("HUGGINGFACE_API_TOKEN")
19
 
 
22
  "Environment variables GROQ_API_KEY and HUGGINGFACE_API_TOKEN must be set."
23
  )
24
 
25
+ # Initialize Groq Client
26
+ groq_client = Groq(api_key=GROQ_API_KEY)
27
 
28
+ # Initialize Hugging Face Inference Client
29
+ hf_client = InferenceClient(token=HUGGINGFACE_API_TOKEN)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
+ # -------------------------
32
+ # Utility Functions
33
+ # -------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
+ def transcribe_audio(audio_path, language=None):
36
  """
37
+ Transcribe Urdu or English audio using Whisper Large-v3 Turbo on Groq.
38
+ Auto-detects language and returns cleaned text.
 
39
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  try:
41
+ with open(audio_path, "rb") as audio_file:
42
+ response = groq_client.audio.transcriptions.create(
43
+ model="whisper-large-v3-turbo",
44
+ file=audio_file,
45
+ response_format="text"
46
+ )
47
 
48
+ transcript = response.strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
+ # Urdu-English post-processing (convert English words in Urdu audio into Urdu script)
51
+ transcript = normalize_transcription(transcript)
 
 
 
 
 
 
 
 
 
52
 
53
+ return transcript
54
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  except Exception as e:
56
+ return f" Error during transcription: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
+ def normalize_transcription(text):
60
+ """
61
+ Basic normalization for Urdu-English blend.
62
+ You can enhance this later with a proper transliteration module.
63
+ """
64
+ replacements = {
65
+ "school": "اسکول",
66
+ "teacher": "ٹیچر",
67
+ "student": "سٹوڈنٹ",
68
+ "education": "ایجوکیشن",
69
+ "university": "یونیورسٹی",
70
+ "computer": "کمپیوٹر",
71
+ "mobile": "موبائل",
72
+ "class": "کلاس",
73
  }
74
+ for eng, urdu in replacements.items():
75
+ text = text.replace(eng, urdu)
76
+ return text
77
 
 
78
 
79
+ def summarize_text(text):
80
  """
81
+ Summarize text using openai/gpt-oss-120b model from Hugging Face.
 
82
  """
 
 
83
  try:
84
+ summary_prompt = f"Summarize the following text in the same language (Urdu or English):\n\n{text}\n\nSummary:"
85
+ response = hf_client.text_generation(
86
+ model="openai/gpt-oss-120b",
87
+ inputs=summary_prompt,
88
+ max_new_tokens=250,
89
+ temperature=0.5,
90
+ )
91
+ return response.generated_text.strip()
92
  except Exception as e:
93
+ return f" Error during summarization: {str(e)}"
 
 
 
94
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
 
96
+ def process_audio(audio_path):
97
+ """Main pipeline for transcription + summarization"""
98
+ if not audio_path:
99
+ return "⚠️ Please upload or record an audio/video file.", ""
100
 
101
+ transcript = transcribe_audio(audio_path)
102
+ if transcript.startswith("❌"):
103
+ return transcript, ""
 
104
 
105
+ summary = summarize_text(transcript)
106
+ return transcript, summary
107
 
 
 
 
 
 
108
 
109
+ # -------------------------
110
+ # Gradio Interface
111
+ # -------------------------
112
 
113
+ with gr.Blocks(theme=gr.themes.Soft(), title="SmartTranscribe Urdu & English AI Transcription") as app:
114
+ gr.Markdown(
115
+ """
116
+ # 🎙️ **SmartTranscribe**
117
+ **AI-Powered Urdu & English Transcription & Summarization App**
118
 
119
+ Upload, record, or link your audio/video — the app will:
120
+ 1. 🎧 Transcribe in the same language (Urdu/English)
121
+ 2. 📝 Convert English words in Urdu speech into Urdu script
122
+ 3. Generate a concise summary using `openai/gpt-oss-120b`
123
+ """
124
+ )
125
 
126
+ with gr.Tab("🎤 Upload or Record"):
127
+ audio_input = gr.Audio(
128
+ sources=["microphone", "upload"], # ✅ Updated syntax
129
+ type="filepath",
130
+ label="Upload or Record audio/video"
131
+ )
132
+
133
+ transcribe_btn = gr.Button("🚀 Start Transcription")
134
+ transcript_output = gr.Textbox(label="📝 Transcribed Text", lines=10)
135
+ summary_output = gr.Textbox(label="📄 Summary", lines=8)
136
 
137
+ transcribe_btn.click(
138
+ fn=process_audio,
139
+ inputs=audio_input,
140
+ outputs=[transcript_output, summary_output]
141
+ )
142
 
143
+ with gr.Tab("ℹ️ About"):
144
+ gr.Markdown(
145
+ """
146
+ ### 💡 How it Works
147
+ - Uses **Groq + Whisper Large-v3 Turbo** for lightning-fast transcription
148
+ - Post-processes mixed Urdu-English text into clean, grammatically correct Urdu
149
+ - Generates summaries with **openai/gpt-oss-120b**
150
+
151
+ ### 🔒 Privacy
152
+ Your files and text are **not stored** after processing.
153
+ All processing happens temporarily in memory.
154
+ """
155
+ )
156
 
157
+ # -------------------------
158
+ # Launch App
159
+ # -------------------------
160
  if __name__ == "__main__":
161
+ app.launch(server_name="0.0.0.0", server_port=7860)