abdullah-alnahas commited on
Commit
e9f148d
·
1 Parent(s): dccede6

feat(app.py): format transcription with gemini

Browse files
Files changed (1) hide show
  1. app.py +117 -67
app.py CHANGED
@@ -1,4 +1,4 @@
1
- import random
2
  import streamlit as st
3
  import io
4
  import os
@@ -11,6 +11,9 @@ import numpy as np
11
  import pydub
12
  from litellm import completion
13
 
 
 
 
14
  # --- Model Loading and Caching ---
15
  @st.cache_resource
16
  def load_transcriber(_device):
@@ -75,8 +78,11 @@ def download_and_convert_audio(video_url, audio_format="wav"):
75
  def update_download_progress(d, status_message):
76
  """Updates the download progress in the Streamlit UI."""
77
  if d['status'] == 'downloading':
78
- p = round(d['downloaded_bytes'] / d['total_bytes'] * 100)
79
- status_message.text(f"Downloading: {p}%")
 
 
 
80
 
81
  @st.cache_data
82
  def split_audio_by_vad(audio_data: bytes, ext: str, _vad_model, sensitivity: float, max_duration: int = 30, return_seconds: bool = True):
@@ -210,41 +216,47 @@ def transcribe_batch(batch, _transcriber, language=None):
210
  def setup_ui():
211
  """Sets up the Streamlit user interface."""
212
  st.title("YouTube Video Transcriber")
213
-
214
- col1, col2, col3, col4 = st.columns(4)
215
- with col1:
216
- transcribe_option = st.checkbox("Transcribe", value=True)
217
- with col2:
218
- download_audio_option = st.checkbox("Download Audio", value=False)
219
- with col3:
220
- download_video_option = st.checkbox("Download Video", value=False)
221
- with col4:
222
- pass
223
-
224
- video_url = st.text_input("YouTube Video Link:", key="video_url")
225
- language = st.text_input("Language (two-letter code, e.g., 'en', 'es', leave empty for auto-detection):", max_chars=2, key="language")
226
- batch_size = st.number_input("Batch Size", min_value=1, value=2, key="batch_size")
227
- vad_sensitivity = st.slider("VAD Sensitivity", min_value=0.0, max_value=1.0, value=0.1, step=0.05, key="vad_sensitivity")
228
-
229
- # Use session state to manage audio format selection and reset
230
- if 'reset_audio_format' not in st.session_state:
231
- st.session_state.reset_audio_format = False
232
-
233
- if 'audio_format' not in st.session_state or st.session_state.reset_audio_format:
234
- st.session_state.audio_format = "wav" # Default value
235
- st.session_state.reset_audio_format = False
236
-
237
- audio_format = st.selectbox("Audio Format", ["wav", "mp3", "ogg", "flac"], key="audio_format_widget", index=["wav", "mp3", "ogg", "flac"].index(st.session_state.audio_format))
238
- st.session_state.audio_format = audio_format
239
-
240
- if download_video_option:
241
- video_format = st.selectbox("Video Format", ["mp4", "webm"], index=0, key="video_format")
242
- else:
243
- video_format = "mp4"
244
-
245
- process_button = st.button("Process")
246
-
247
- return video_url, language, batch_size, transcribe_option, download_audio_option, download_video_option, process_button, vad_sensitivity, audio_format, video_format
 
 
 
 
 
 
248
 
249
  @st.cache_resource
250
  def initialize_models():
@@ -278,12 +290,12 @@ def process_transcription(video_url, vad_sensitivity, batch_size, transcriber, v
278
 
279
  total_chunks = len(chunks)
280
  transcriptions = []
281
- progress_bar = st.progress(0)
282
  for i in range(0, total_chunks, batch_size):
283
  batch = chunks[i:i + batch_size]
284
  batch_transcriptions = transcribe_batch(batch, transcriber, language)
285
  transcriptions.extend(batch_transcriptions)
286
- progress_bar.progress((i + len(batch)) / total_chunks)
287
 
288
  progress_bar.empty()
289
  st.success("Transcription complete!")
@@ -293,9 +305,8 @@ def process_transcription(video_url, vad_sensitivity, batch_size, transcriber, v
293
  start_time = format_seconds(chunk['start'])
294
  end_time = format_seconds(chunk['end'])
295
  full_transcription += f"[{start_time} - {end_time}]: {chunk['text'].strip()}\n\n"
296
- formatted_transcription = format_transcript(full_transcription)
297
 
298
- return full_transcription, formatted_transcription, audio_data, audio_format, info
299
 
300
  def format_seconds(seconds):
301
  """Formats seconds into HH:MM:SS string."""
@@ -331,18 +342,21 @@ def download_video(video_url, video_format):
331
  return None, None, None
332
 
333
  def format_transcript(input_transcription):
334
-
335
 
336
  # os.environ["GEMINI_API_KEY"] = "..."
337
 
338
  sys_prompt = """
339
- Video Transcription Formatting
340
-
341
- As an LLM formatting provided video transcriptions (in any language), transform spoken language into clear, readable text. Prioritize readability, consistency, and context, adapting to the specific language conventions. **Do not hallucinate or add any information not present in the original transcript.**
342
-
343
- * **Sentences:** Restructure long, rambling sentences; correct grammatical errors *while preserving the original meaning*; use proper punctuation appropriate for the language.
344
- * **Reading:** Italicize/quote read text; clearly separate from explanations.
345
- * **Repetitions:** Remove unnecessary repetitions unless for emphasis.
 
 
 
346
  """.strip()
347
  messages = [{"content": sys_prompt, "role": "system"},
348
  {"content": f"Format the following video transcription: {input_transcription}", "role": "user"}]
@@ -353,10 +367,12 @@ def format_transcript(input_transcription):
353
 
354
  def main():
355
  """Main function to run the Streamlit application."""
356
-
357
  # Initialize session state variables
358
  if 'full_transcription' not in st.session_state:
359
  st.session_state.full_transcription = None
 
 
360
  if 'audio_data' not in st.session_state:
361
  st.session_state.audio_data = None
362
  if 'info' not in st.session_state:
@@ -369,14 +385,29 @@ def main():
369
  transcriber, vad_model = initialize_models()
370
 
371
  # Call setup_ui() to get UI element values
372
- video_url, language, batch_size, transcribe_option, download_audio_option, download_video_option, process_button, vad_sensitivity, audio_format, video_format = setup_ui()
373
-
374
- # transcription_output = st.empty()
 
 
 
 
 
 
 
 
 
375
  if st.session_state.full_transcription:
376
- st.text_area("Transcription:", value=st.session_state.full_transcription, height=300, key=random.random())
377
-
 
 
 
 
 
378
  if process_button:
379
  st.session_state.full_transcription = None
 
380
  st.session_state.audio_data = None
381
  st.session_state.info = None
382
  st.session_state.video_data = None
@@ -386,44 +417,63 @@ def main():
386
  if not video_url:
387
  st.error("Please enter a YouTube video link.")
388
  return
 
 
 
389
 
390
  if transcribe_option:
391
- st.session_state.full_transcription, st.session_state.formatted_transcription, st.session_state.audio_data, st.session_state.audio_format, st.session_state.info = process_transcription(video_url, vad_sensitivity, batch_size, transcriber, vad_model, audio_format, language)
392
  if st.session_state.full_transcription:
393
- st.text_area("Transcription:", value=st.session_state.full_transcription, height=300, key=random.random())
394
- if st.session_state.formatted_transcription:
395
- st.text_area("Formatted Transcription:", value=st.session_state.formatted_transcription, height=300, key=random.random())
396
-
 
 
397
 
398
  if download_audio_option:
399
- if st.session_state.audio_data is None or st.session_state.audio_format is None or st.session_state.info is None:
400
  st.session_state.audio_data, st.session_state.audio_format, st.session_state.info = download_and_convert_audio(video_url, audio_format)
401
 
402
  if download_video_option:
403
  st.session_state.video_data, st.session_state.video_filename, st.session_state.info = download_video(video_url, video_format)
 
 
 
 
 
 
404
 
405
  # Download button logic (moved after setup_ui() call)
406
- col1, col2, col3 = st.columns(3)
407
  with col1:
408
  if st.session_state.full_transcription and transcribe_option:
409
  st.download_button(
410
  label="Download Transcription (TXT)",
411
  data=st.session_state.full_transcription,
412
- file_name=f"{st.session_state.info['id']}_transcription.txt",
413
  mime="text/plain"
414
  )
415
-
416
  with col2:
 
 
 
 
 
 
 
 
 
417
  # Now download_audio_option is defined
418
  if st.session_state.audio_data is not None and download_audio_option:
419
  st.download_button(
420
  label=f"Download Audio ({st.session_state.audio_format})",
421
  data=st.session_state.audio_data,
422
- file_name=f"{st.session_state.info['id']}.{st.session_state.audio_format}",
423
  mime=f"audio/{st.session_state.audio_format}"
424
  )
425
 
426
- with col3:
427
  if st.session_state.video_data is not None and download_video_option:
428
  st.download_button(
429
  label="Download Video",
 
1
+ import uuid
2
  import streamlit as st
3
  import io
4
  import os
 
11
  import pydub
12
  from litellm import completion
13
 
14
+ # --- Language List ---
15
+ LANGUAGES = ['english', 'chinese', 'german', 'spanish', 'russian', 'korean', 'french', 'japanese', 'portuguese', 'turkish', 'polish', 'catalan', 'dutch', 'arabic', 'swedish', 'italian', 'indonesian', 'hindi', 'finnish', 'vietnamese', 'hebrew', 'ukrainian', 'greek', 'malay', 'czech', 'romanian', 'danish', 'hungarian', 'tamil', 'norwegian', 'thai', 'urdu', 'croatian', 'bulgarian', 'lithuanian', 'latin', 'maori', 'malayalam', 'welsh', 'slovak', 'telugu', 'persian', 'latvian', 'bengali', 'serbian', 'azerbaijani', 'slovenian', 'kannada', 'estonian', 'macedonian', 'breton', 'basque', 'icelandic', 'armenian', 'nepali', 'mongolian', 'bosnian', 'kazakh', 'albanian', 'swahili', 'galician', 'marathi', 'punjabi', 'sinhala', 'khmer', 'shona', 'yoruba', 'somali', 'afrikaans', 'occitan', 'georgian', 'belarusian', 'tajik', 'sindhi', 'gujarati', 'amharic', 'yiddish', 'lao', 'uzbek', 'faroese', 'haitian creole', 'pashto', 'turkmen', 'nynorsk', 'maltese', 'sanskrit', 'luxembourgish', 'myanmar', 'tibetan', 'tagalog', 'malagasy', 'assamese', 'tatar', 'hawaiian', 'lingala', 'hausa', 'bashkir', 'javanese', 'sundanese', 'cantonese', 'burmese', 'valencian', 'flemish', 'haitian', 'letzeburgesch', 'pushto', 'panjabi', 'moldavian', 'moldovan', 'sinhalese', 'castilian', 'mandarin']
16
+
17
  # --- Model Loading and Caching ---
18
  @st.cache_resource
19
  def load_transcriber(_device):
 
78
  def update_download_progress(d, status_message):
79
  """Updates the download progress in the Streamlit UI."""
80
  if d['status'] == 'downloading':
81
+ if 'total_bytes' in d and d['total_bytes'] is not None:
82
+ p = round(d['downloaded_bytes'] / d['total_bytes'] * 100)
83
+ status_message.text(f"Downloading: {p}%")
84
+ else:
85
+ status_message.text("Downloading...")
86
 
87
  @st.cache_data
88
  def split_audio_by_vad(audio_data: bytes, ext: str, _vad_model, sensitivity: float, max_duration: int = 30, return_seconds: bool = True):
 
216
  def setup_ui():
217
  """Sets up the Streamlit user interface."""
218
  st.title("YouTube Video Transcriber")
219
+ st.caption("This app allows you to transcribe YouTube videos and format the transcription using a large language model. You can also download the audio and video.")
220
+
221
+ with st.sidebar:
222
+ st.header("Input")
223
+ video_url = st.text_input("YouTube Video Link:", key="video_url", help="Enter the URL of the YouTube video you want to transcribe.")
224
+
225
+ st.header("Options")
226
+ col1, col2, col3, col4 = st.columns(4)
227
+ with col1:
228
+ transcribe_option = st.checkbox("Transcribe", value=True, help="Transcribe the audio of the video.")
229
+ with col2:
230
+ download_audio_option = st.checkbox("Download Audio", value=False, help="Download the audio of the video.")
231
+ with col3:
232
+ download_video_option = st.checkbox("Download Video", value=False, help="Download the video.")
233
+ with col4:
234
+ format_option = st.checkbox("Format Text", value=True, help="Format the transcription for better readability using a language model.")
235
+
236
+ with st.expander("Advanced Settings"):
237
+ language = st.selectbox("Language", options= ["Auto-Detect"] + LANGUAGES, format_func=lambda x: x.title(), help="Select the language of the audio for better transcription accuracy. Select 'Auto-Detect' to let the model determine the language.")
238
+ batch_size = st.number_input("Batch Size", min_value=1, value=2, key="batch_size", help="The number of audio chunks to process at once during transcription.")
239
+ vad_sensitivity = st.slider("VAD Sensitivity", min_value=0.0, max_value=1.0, value=0.1, step=0.05, key="vad_sensitivity", help="Adjust the sensitivity of the Voice Activity Detection (VAD) model. Higher values mean more sensitive to speech.")
240
+
241
+ # Use session state to manage audio format selection and reset
242
+ if 'reset_audio_format' not in st.session_state:
243
+ st.session_state.reset_audio_format = False
244
+
245
+ if 'audio_format' not in st.session_state or st.session_state.reset_audio_format:
246
+ st.session_state.audio_format = "wav" # Default value
247
+ st.session_state.reset_audio_format = False
248
+
249
+ audio_format = st.selectbox("Audio Format", ["wav", "mp3", "ogg", "flac"], key="audio_format_widget", index=["wav", "mp3", "ogg", "flac"].index(st.session_state.audio_format), help="Select the desired audio format for download.")
250
+ st.session_state.audio_format = audio_format
251
+
252
+ if download_video_option:
253
+ video_format = st.selectbox("Video Format", ["mp4", "webm"], index=0, key="video_format", help="Select the desired video format for download.")
254
+ else:
255
+ video_format = "mp4"
256
+
257
+ process_button = st.button("Process")
258
+
259
+ return video_url, language, batch_size, transcribe_option, download_audio_option, download_video_option, process_button, vad_sensitivity, audio_format, video_format, format_option
260
 
261
  @st.cache_resource
262
  def initialize_models():
 
290
 
291
  total_chunks = len(chunks)
292
  transcriptions = []
293
+ progress_bar = st.progress(0, "Transcribing...")
294
  for i in range(0, total_chunks, batch_size):
295
  batch = chunks[i:i + batch_size]
296
  batch_transcriptions = transcribe_batch(batch, transcriber, language)
297
  transcriptions.extend(batch_transcriptions)
298
+ progress_bar.progress((i + len(batch)) / total_chunks, f"Transcribing... {i + len(batch)}/{total_chunks} chunks done")
299
 
300
  progress_bar.empty()
301
  st.success("Transcription complete!")
 
305
  start_time = format_seconds(chunk['start'])
306
  end_time = format_seconds(chunk['end'])
307
  full_transcription += f"[{start_time} - {end_time}]: {chunk['text'].strip()}\n\n"
 
308
 
309
+ return full_transcription, audio_data, audio_format, info
310
 
311
  def format_seconds(seconds):
312
  """Formats seconds into HH:MM:SS string."""
 
342
  return None, None, None
343
 
344
  def format_transcript(input_transcription):
345
+ """Formats the transcription using the Gemini large language model."""
346
 
347
  # os.environ["GEMINI_API_KEY"] = "..."
348
 
349
  sys_prompt = """
350
+ * Format the provided video transcription as a polished piece of written text.
351
+ * **The output must be in the same language as the input; do not translate it.**
352
+ * Focus on clarity, readability, and consistency, adhering to the conventions of that specific language.
353
+ * Restructure sentences for improved flow and correct grammatical errors.
354
+ * **Edits should strictly enhance readability without altering the original meaning or nuances of the raw transcription.**
355
+ * Italicize or quote any text that is read aloud, clearly distinguishing it from the surrounding explanations.
356
+ * Eliminate unnecessary repetitions unless they are used for emphasis.
357
+ * **Do not add any information not present in the original transcript.**
358
+ * **Do not remove timestamps.**
359
+ * **Output only the formatted transcription.**
360
  """.strip()
361
  messages = [{"content": sys_prompt, "role": "system"},
362
  {"content": f"Format the following video transcription: {input_transcription}", "role": "user"}]
 
367
 
368
  def main():
369
  """Main function to run the Streamlit application."""
370
+ st.set_page_config(layout="wide")
371
  # Initialize session state variables
372
  if 'full_transcription' not in st.session_state:
373
  st.session_state.full_transcription = None
374
+ if 'formatted_transcription' not in st.session_state:
375
+ st.session_state.formatted_transcription = None
376
  if 'audio_data' not in st.session_state:
377
  st.session_state.audio_data = None
378
  if 'info' not in st.session_state:
 
385
  transcriber, vad_model = initialize_models()
386
 
387
  # Call setup_ui() to get UI element values
388
+ video_url, language, batch_size, transcribe_option, download_audio_option, download_video_option, process_button, vad_sensitivity, audio_format, video_format, format_option = setup_ui()
389
+
390
+ # Validate options
391
+ if not transcribe_option and not download_audio_option and not download_video_option and not format_option:
392
+ st.error("Please select at least one option.")
393
+ return
394
+ if format_option and not transcribe_option:
395
+ st.error("Please select the transcription option to format the transcript.")
396
+ return
397
+
398
+ transcription_output = st.empty()
399
+ formatted_transcription_output = st.empty()
400
  if st.session_state.full_transcription:
401
+ transcription_output.text_area("Transcription:", value=st.session_state.full_transcription, height=300, key=uuid.uuid4())
402
+ if format_option:
403
+ if st.session_state.formatted_transcription:
404
+ formatted_transcription_output.text_area("Formatted Transcription:", value=st.session_state.formatted_transcription, height=300, key=uuid.uuid4())
405
+ else:
406
+ formatted_transcription_output.text_area("Formatted Transcription:", value="No formatting was done on this transcription", height=300, key=uuid.uuid4())
407
+
408
  if process_button:
409
  st.session_state.full_transcription = None
410
+ st.session_state.formatted_transcription = None
411
  st.session_state.audio_data = None
412
  st.session_state.info = None
413
  st.session_state.video_data = None
 
417
  if not video_url:
418
  st.error("Please enter a YouTube video link.")
419
  return
420
+
421
+ # Handle language auto-detection
422
+ selected_language = language.lower() if language != "Auto-Detect" else None
423
 
424
  if transcribe_option:
425
+ st.session_state.full_transcription, st.session_state.audio_data, st.session_state.audio_format, st.session_state.info = process_transcription(video_url, vad_sensitivity, batch_size, transcriber, vad_model, audio_format, selected_language)
426
  if st.session_state.full_transcription:
427
+ transcription_output.text_area("Transcription:", value=st.session_state.full_transcription, height=300, key=uuid.uuid4())
428
+ if format_option:
429
+ st.session_state.formatted_transcription = format_transcript(st.session_state.full_transcription)
430
+ formatted_transcription_output.text_area("Formatted Transcription:", value=st.session_state.formatted_transcription, height=300, key=uuid.uuid4())
431
+ else:
432
+ st.session_state.formatted_transcription = None
433
 
434
  if download_audio_option:
435
+ if st.session_state.audio_data is None or st.session_state.audio_format is None:
436
  st.session_state.audio_data, st.session_state.audio_format, st.session_state.info = download_and_convert_audio(video_url, audio_format)
437
 
438
  if download_video_option:
439
  st.session_state.video_data, st.session_state.video_filename, st.session_state.info = download_video(video_url, video_format)
440
+ # Handle cases where st.session_state.info is None due to download errors
441
+ if st.session_state.info is None:
442
+ st.error("Could not retrieve video information. Please check the video URL and try again.")
443
+ # Reset video_data and video_filename to prevent further errors
444
+ st.session_state.video_data = None
445
+ st.session_state.video_filename = None
446
 
447
  # Download button logic (moved after setup_ui() call)
448
+ col1, col2, col3, col4 = st.columns(4)
449
  with col1:
450
  if st.session_state.full_transcription and transcribe_option:
451
  st.download_button(
452
  label="Download Transcription (TXT)",
453
  data=st.session_state.full_transcription,
454
+ file_name=f"{st.session_state.info['id'] if st.session_state.info else 'transcription'}.txt",
455
  mime="text/plain"
456
  )
 
457
  with col2:
458
+ if st.session_state.formatted_transcription and format_option:
459
+ st.download_button(
460
+ label="Download Formatted Transcription (TXT)",
461
+ data=st.session_state.formatted_transcription,
462
+ file_name=f"{st.session_state.info['id'] if st.session_state.info else 'formatted_transcription'}.txt",
463
+ mime="text/plain"
464
+ )
465
+
466
+ with col3:
467
  # Now download_audio_option is defined
468
  if st.session_state.audio_data is not None and download_audio_option:
469
  st.download_button(
470
  label=f"Download Audio ({st.session_state.audio_format})",
471
  data=st.session_state.audio_data,
472
+ file_name=f"{st.session_state.info['id'] if st.session_state.info else 'audio'}.{st.session_state.audio_format}",
473
  mime=f"audio/{st.session_state.audio_format}"
474
  )
475
 
476
+ with col4:
477
  if st.session_state.video_data is not None and download_video_option:
478
  st.download_button(
479
  label="Download Video",