GogetaBlueMUI commited on
Commit
6bb9fe4
·
verified ·
1 Parent(s): 84e6fa4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +756 -3
app.py CHANGED
@@ -12,7 +12,7 @@ import psutil
12
 
13
  st.set_page_config(layout="wide")
14
 
15
- # Updated CSS with video styling from the second code
16
  st.markdown("""
17
  <style>
18
  @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700&display=swap');
@@ -190,7 +190,743 @@ st.markdown("""
190
  font-family: 'Poppins', sans-serif;
191
  }
192
 
193
- /* Video player styling - Updated to match second code */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  video {
195
  display: block;
196
  width: 350px !important;
@@ -591,23 +1327,37 @@ def main():
591
  st.markdown("### Search Subtitles")
592
  search_query = st.text_input("Search subtitles...", value=st.session_state['search_query'], key="search_input")
593
  st.session_state['search_query'] = search_query.lower()
 
 
594
  st.markdown(f"### {st.session_state['language']} Transcript")
 
595
  for text, start, end in st.session_state['primary_transcript']:
596
  display_text = text.lower()
597
  if not search_query or search_query in display_text:
 
598
  label = f"[{format_time(start)} - {format_time(end)}] {text}" if st.session_state['show_timeframe'] else text
599
  if st.button(label, key=f"primary_{start}"):
600
  st.session_state['current_time'] = start
601
  st.rerun()
 
 
 
 
602
  if st.session_state['english_transcript']:
603
  st.markdown("### English Translation")
 
604
  for text, start, end in st.session_state['english_transcript']:
605
  display_text = text.lower()
606
  if not search_query or search_query in display_text:
 
607
  label = f"[{format_time(start)} - {format_time(end)}] {text}" if st.session_state['show_timeframe'] else text
608
  if st.button(label, key=f"english_{start}"):
609
  st.session_state['current_time'] = start
610
  st.rerun()
 
 
 
 
611
  if (st.session_state['language_code'] == 'en' or st.session_state['translate_to_english']) and not st.session_state['summary_generated']:
612
  if st.button("Generate Summary"):
613
  with st.spinner("Generating summary..."):
@@ -622,12 +1372,16 @@ def main():
622
  if st.session_state['english_summary'] and st.session_state['summary_generated']:
623
  st.markdown("### Summary")
624
  st.write(st.session_state['english_summary'])
 
 
625
  st.markdown("### Download Subtitles")
626
  include_timeframe = st.checkbox("Include timeframe in subtitles", value=True)
627
  transcript_to_download = st.session_state['primary_transcript'] or st.session_state['english_transcript']
628
  if transcript_to_download:
629
  srt_content = generate_srt(transcript_to_download, include_timeframe)
630
  st.download_button(label="Download Subtitles (SRT)", data=srt_content, file_name="subtitles.srt", mime="text/plain")
 
 
631
  st.markdown("### Edit Subtitles")
632
  transcript_to_edit = st.session_state['primary_transcript'] or st.session_state['english_transcript']
633
  if transcript_to_edit and st.button("Delete Subtitles"):
@@ -663,7 +1417,6 @@ def main():
663
 
664
  if st.session_state['app_state'] == 'results' and st.session_state['edited_video_path']:
665
  st.markdown("### Edited Video")
666
- # Center the edited video
667
  st.markdown('<div style="display: flex; justify-content: center;">', unsafe_allow_html=True)
668
  st.video(st.session_state['edited_video_path'])
669
  st.markdown('</div>', unsafe_allow_html=True)
 
12
 
13
  st.set_page_config(layout="wide")
14
 
15
+ # Updated CSS with video styling
16
  st.markdown("""
17
  <style>
18
  @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700&display=swap');
 
190
  font-family: 'Poppins', sans-serif;
191
  }
192
 
193
+ /* Video player styling */
194
+ video {
195
+ display: block;
196
+ width: 350px !important;
197
+ height: 500px !important;
198
+ object-fit: contain;
199
+ margin: 0 auto;
200
+ border: 3px solid #2196f3;
201
+ border-radius: 8px;
202
+ }
203
+
204
+ /* Footer */
205
+ footer {
206
+ background: #1a1a1a;
207
+ color: #ffffff;
208
+ padding: 3rem 2rem;
209
+ margin-top: 3rem;
210
+ border-radius: 1rem 1rem 0 0;
211
+ }
212
+ .footer-container {
213
+ display: flex;
214
+ justify-content: space-around;
215
+ gap: 2rem;
216
+ flex-wrap: wrap;
217
+ }
218
+ .footer-section h4 {
219
+ font-size: 1.8rem;
220
+ margin-bottom: 1rem;
221
+ }
222
+ .footer-section ul {
223
+ list-style: none;
224
+ padding: 0;
225
+ }
226
+ .footer-section ul li a {
227
+ color: #bbbbbb;
228
+ text-decoration: none;
229
+ font-size: 1.6rem;
230
+ transition: color 0.3s ease;
231
+ }
232
+ .footer-section ul li a:hover {
233
+ color: #ff6f61;
234
+ }
235
+ .footer-bottom {
236
+ margin-top: 2rem;
237
+ font-size: 0.9rem;
238
+ }
239
+
240
+ /* Responsive Design */
241
+ @media (max-width: 768px) {
242
+ .header {
243
+ flex-direction: column;
244
+ gap: 1rem;
245
+ }
246
+ .navbar {
247
+ flex-direction: column;
248
+ gap: 0.5rem;
249
+ }
250
+ .hero h1 {
251
+ font-size: 1.8rem;
252
+ }
253
+ .hero p {
254
+ font-size: 1rem;
255
+ }
256
+ .feature, .plan {
257
+ width: 100%;
258
+ max-width: 300px;
259
+ }
260
+ }
261
+ </style>
262
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
263
+ """, unsafe_allow_html=True)
264
+
265
+ # Function Definitions
266
+ def format_time(seconds):
267
+ minutes = int(seconds // 60)
268
+ secs = int(seconds % 60)
269
+ return f"{minutes}:{secs:02d}"
270
+
271
+ def seconds_to_srt_time(seconds):
272
+ hours = int(seconds // 3600)
273
+ minutes = int((seconds % 3600) // 60)
274
+ secs = int(seconds % 60)
275
+ millis = int((seconds - int(seconds)) * 1000)
276
+ return f"{hours:02d}:{minutes:02d}:{secs:02d},{millis:03d}"
277
+
278
+ class TranscriptionProgress:
279
+ def __init__(self):
280
+ self.progress_bar = None
281
+ self.status_text = None
282
+ def init_progress(self):
283
+ self.progress_bar = st.progress(0.0)
284
+ self.status_text = st.empty()
285
+ def update(self, progress: float, status: str):
286
+ progress = max(0.0, min(1.0, progress))
287
+ if self.progress_bar is not None:
288
+ self.progress_bar.progress(progress)
289
+ if self.status_text is not None:
290
+ self.status_text.text(status)
291
+
292
+ @st.cache_resource
293
+ def load_model(language='en', summarizer_type='bart'):
294
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
295
+ if language == 'ur':
296
+ processor = AutoProcessor.from_pretrained("GogetaBlueMUI/whisper-medium-ur-fleurs-v2")
297
+ model = AutoModelForSpeechSeq2Seq.from_pretrained("GogetaBlueMUI/whisper-medium-ur-fleurs-v2").to(device)
298
+ else:
299
+ processor = AutoProcessor.from_pretrained("openai/whisper-small")
300
+ model = AutoModelForSpeechSeq2Seq.from_pretrained("openai/whisper-small").to(device)
301
+ if device.type == "cuda":
302
+ model = model.half()
303
+ if summarizer_type == 'bart':
304
+ sum_tokenizer = AutoTokenizer.from_pretrained("facebook/bart-large-cnn")
305
+ sum_model = AutoModelForSeq2SeqLM.from_pretrained("facebook/bart-large-cnn").to(device)
306
+ else:
307
+ sum_tokenizer = AutoTokenizer.from_pretrained("pszemraj/led-large-book-summary")
308
+ sum_model = AutoModelForSeq2SeqLM.from_pretrained("pszemraj/led-large-book-summary").to(device)
309
+ return processor, model, sum_tokenizer, sum_model, device
310
+
311
+ def split_audio_into_chunks(audio, sr, chunk_duration):
312
+ chunk_samples = int(chunk_duration * sr)
313
+ chunks = [audio[start:start + chunk_samples] for start in range(0, len(audio), chunk_samples)]
314
+ return chunks
315
+
316
+ def transcribe_audio(audio, sr, processor, model, device, start_time, language, task="transcribe"):
317
+ inputs = processor(audio, sampling_rate=sr, return_tensors="pt")
318
+ input_features = inputs.input_features.to(device)
319
+ if model.dtype == torch.float16:
320
+ input_features = input_features.half()
321
+ generate_kwargs = {
322
+ "task": task,
323
+ "language": "urdu" if language == "ur" else language,
324
+ "max_new_tokens": 128,
325
+ "return_timestamps": True
326
+ }
327
+ try:
328
+ with torch.no_grad():
329
+ outputs = model.generate(input_features, **generate_kwargs)
330
+ text = processor.decode(outputs[0], skip_special_tokens=True)
331
+ return [(text, start_time, start_time + len(audio) / sr)]
332
+ except Exception as e:
333
+ st.error(f"Transcription error: {str(e)}")
334
+ return [(f"Error: {str(e)}", start_time, start_time + len(audio) / sr)]
335
+
336
+ def process_chunks(chunks, sr, processor, model, device, language, chunk_duration, task="transcribe", transcript_file="temp_transcript.json"):
337
+ transcript = []
338
+ chunk_start = 0
339
+ total_chunks = len(chunks)
340
+ progress_bar = st.progress(0)
341
+ status_text = st.empty()
342
+ if os.path.exists(transcript_file):
343
+ os.remove(transcript_file)
344
+ for i, chunk in enumerate(chunks):
345
+ status_text.text(f"Processing chunk {i+1}/{total_chunks}...")
346
+ try:
347
+ memory = psutil.virtual_memory()
348
+ st.write(f"Memory usage: {memory.percent}% (Chunk {i+1}/{total_chunks})")
349
+ chunk_transcript = transcribe_audio(chunk, sr, processor, model, device, chunk_start, language, task)
350
+ transcript.extend(chunk_transcript)
351
+ with open(transcript_file, "w", encoding="utf-8") as f:
352
+ json.dump(transcript, f, ensure_ascii=False)
353
+ chunk_start += chunk_duration
354
+ progress_bar.progress((i + 1) / total_chunks)
355
+ except Exception as e:
356
+ st.error(f"Error processing chunk {i+1}: {str(e)}")
357
+ break
358
+ status_text.text("Processing complete!")
359
+ progress_bar.empty()
360
+ return transcript
361
+
362
+ def summarize_text(text, tokenizer, model, device, summarizer_type='bart'):
363
+ if summarizer_type == 'bart':
364
+ max_input_length = 1024
365
+ max_summary_length = 150
366
+ chunk_size = 512
367
+ else:
368
+ max_input_length = 16384
369
+ max_summary_length = 512
370
+ chunk_size = 8192
371
+ inputs = tokenizer(text, return_tensors="pt", truncation=False)
372
+ input_ids = inputs["input_ids"].to(device)
373
+ num_tokens = input_ids.shape[1]
374
+ st.write(f"Number of tokens in input: {num_tokens}")
375
+ if num_tokens < 50:
376
+ return "Transcript too short to summarize effectively."
377
+ try:
378
+ summaries = []
379
+ if num_tokens <= max_input_length:
380
+ truncated_inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=max_input_length).to(device)
381
+ with torch.no_grad():
382
+ summary_ids = model.generate(truncated_inputs["input_ids"], num_beams=4, max_length=max_summary_length, min_length=50, early_stopping=True, temperature=0.7)
383
+ summaries.append(tokenizer.decode(summary_ids[0], skip_special_tokens=True))
384
+ else:
385
+ st.write(f"Transcript exceeds {max_input_length} tokens. Processing in chunks...")
386
+ tokens = input_ids[0].tolist()
387
+ for i in range(0, num_tokens, chunk_size):
388
+ chunk_tokens = tokens[i:i + chunk_size]
389
+ chunk_input_ids = torch.tensor([chunk_tokens]).to(device)
390
+ with torch.no_grad():
391
+ summary_ids = model.generate(chunk_input_ids, num_beams=4, max_length=max_summary_length // 2, min_length=25, early_stopping=True, temperature=0.7)
392
+ summaries.append(tokenizer.decode(summary_ids[0], skip_special_tokens=True))
393
+ combined_summary = " ".join(summaries)
394
+ combined_inputs = tokenizer(combined_summary, return_tensors="pt", truncation=True, max_length=max_input_length).to(device)
395
+ with torch.no_grad():
396
+ final_summary_ids = model.generate(combined_inputs["input_ids"], num_beams=4, max_length=max_summary_length, min_length=50, early_stopping=True, temperature=0.7)
397
+ summaries = [tokenizer.decode(final_summary_ids[0], skip_special_tokens=True)]
398
+ return " ".join(summaries)
399
+ except Exception as e:
400
+ st.error(f"Summarization error: {str(e)}")
401
+ return f"Error: {str(e)}"
402
+
403
+ def save_uploaded_file(uploaded_file):
404
+ try:
405
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_file:
406
+ tmp_file.write(uploaded_file.read())
407
+ return tmp_file.name
408
+ except Exception as e:
409
+ st.error(f"Error saving uploaded file: {str(e)}")
410
+ return None
411
+
412
+ def merge_intervals(intervals):
413
+ if not intervals:
414
+ return []
415
+ intervals.sort(key=lambda x: x[0])
416
+ merged = [intervals[0]]
417
+ for current in intervals[1:]:
418
+ previous = merged[-1]
419
+ if previous[1] >= current[0]:
420
+ merged[-1] = (previous[0], max(previous[1], current[1]))
421
+ else:
422
+ merged.append(current)
423
+ return merged
424
+
425
+ def create_edited_video(video_path, transcript, keep_indices):
426
+ try:
427
+ intervals_to_keep = [(transcript[i][1], transcript[i][2]) for i in keep_indices]
428
+ merged_intervals = merge_intervals(intervals_to_keep)
429
+ temp_files = []
430
+ for j, (start, end) in enumerate(merged_intervals):
431
+ temp_file = f"temp_{j}.mp4"
432
+ ffmpeg.input(video_path, ss=start, to=end).output(temp_file, c='copy').run(overwrite_output=True, quiet=True)
433
+ temp_files.append(temp_file)
434
+ with open("list.txt", "w") as f:
435
+ for temp_file in temp_files:
436
+ f.write(f"file '{temp_file}'\n")
437
+ edited_video_path = "edited_video.mp4"
438
+ ffmpeg.input('list.txt', format='concat', safe=0).output(edited_video_path, c='copy').run(overwrite_output=True, quiet=True)
439
+ for temp_file in temp_files:
440
+ if os.path.exists(temp_file):
441
+ os.remove(temp_file)
442
+ if os.path.exists("list.txt"):
443
+ os.remove("list.txt")
444
+ return edited_video_path
445
+ except Exception as e:
446
+ st.error(f"Error creating edited video: {str(e)}")
447
+ return None
448
+
449
+ def generate_srt(transcript, include_timeframe=True):
450
+ srt_content = ""
451
+ for text, start, end in transcript:
452
+ if include_timeframe:
453
+ start_time = seconds_to_srt_time(start)
454
+ end_time = seconds_to_srt_time(end)
455
+ srt_content += f"{start_time} --> {end_time}\n{text}\n\n"
456
+ else:
457
+ srt_content += f"{text}\n\n"
458
+ return srt_content
459
+
460
+ # Main Function with Centered Video Display
461
+ def main():
462
+ st.markdown("""
463
+ <div class="header">
464
+ <div class="logo">
465
+ <img src="https://i.postimg.cc/wvFfzx5h/VIDEpp.png">
466
+ </div>
467
+ <ul class="navbar">
468
+ <li><a href="#home">Home</a></li>
469
+ <li><a href="#upload">Upload Video</a></ Stanislaus
470
+ <li><a href="#about">About Us</a></li>
471
+ <li><a href="#contact">Contact Us</a></li>
472
+ </ul>
473
+ </div>
474
+ """, unsafe_allow_html=True)
475
+
476
+ st.markdown("""
477
+ <div id="home" class="hero">
478
+ <h2>VidEp – Revolutionizing Video Subtitle Editing with AI</h2>
479
+ <p>Upload, transcribe, edit subtitles, and summarize videos effortlessly.</p>
480
+ </div>
481
+ """, unsafe_allow_html=True)
482
+
483
+ # Initialize session state variables
484
+ if 'app_state' not in st.session_state:
485
+ st.session_state['app_state'] = 'upload'
486
+ if 'video_path' not in st.session_state:
487
+ st.session_state['video_path'] = None
488
+ if 'primary_transcript' not in st.session_state:
489
+ st.session_state['primary_transcript'] = None
490
+ if 'english_transcript' not in st.session_state:
491
+ st.session_state['english_transcript'] = None
492
+ if 'english_summary' not in st.session_state:
493
+ st.session_state['english_summary'] = None
494
+ if 'language' not in st.session_state:
495
+ st.session_state['language'] = None
496
+ if 'language_code' not in st.session_state:
497
+ st.session_state['language_code'] = None
498
+ if 'translate_to_english' not in st.session_state:
499
+ st.session_state['translate_to_english'] = False
500
+ if 'summarizer_type' not in st.session_state:
501
+ st.session_state['summarizer_type'] = None
502
+ if 'summary_generated' not in st.session_state:
503
+ st.session_state['summary_generated'] = False
504
+ if 'current_time' not in st.session_state:
505
+ st.session_state['current_time'] = 0
506
+ if 'edited_video_path' not in st.session_state:
507
+ st.session_state['edited_video_path'] = None
508
+ if 'search_query' not in st.session_state:
509
+ st.session_state['search_query'] = ""
510
+ if 'show_timeframe' not in st.session_state:
511
+ st.session_state['show_timeframe'] = True
512
+
513
+ if st.session_state['app_state'] == 'upload':
514
+ st.markdown("<div id='upload'></div>", unsafe_allow_html=True)
515
+ st.markdown("<h3 style='text-align: center; color: black;'>Upload Your Video</h3>", unsafe_allow_html=True)
516
+ with st.form(key="upload_form"):
517
+ uploaded_file = st.file_uploader("Choose a video file", type=["mp4"], label_visibility="collapsed")
518
+ if st.form_submit_button("Upload") and uploaded_file:
519
+ video_path = save_uploaded_file(uploaded_file)
520
+ if video_path:
521
+ st.session_state['video_path'] = video_path
522
+ st.session_state['app_state'] = 'processing'
523
+ st.write(f"Uploaded file: {uploaded_file.name}")
524
+ st.rerun()
525
+
526
+ if st.session_state['app_state'] == 'processing':
527
+ with st.form(key="processing_form"):
528
+ language = st.selectbox("Select language", ["English", "Urdu"], key="language_select")
529
+ language_code = "en" if language == "English" else "ur"
530
+ st.session_state['language'] = language
531
+ st.session_state['language_code'] = language_code
532
+ chunk_duration = st.number_input("Duration per chunk (seconds):", min_value=1.0, step=0.1, value=10.0)
533
+ if language_code == "ur":
534
+ translate_to_english = st.checkbox("Generate English translation", key="translate_checkbox")
535
+ st.session_state['translate_to_english'] = translate_to_english
536
+ else:
537
+ st.session_state['translate_to_english'] = False
538
+ if st.form_submit_button("Process"):
539
+ with st.spinner("Processing video..."):
540
+ start_time = time.time()
541
+ try:
542
+ st.write("Extracting audio...")
543
+ audio_path = "processed_audio.wav"
544
+ ffmpeg.input(st.session_state['video_path']).output(audio_path, ac=1, ar=16000).run(overwrite_output=True, quiet=True)
545
+ audio, sr = librosa.load(audio_path, sr=16000)
546
+ audio = np.nan_to_num(audio, nan=0.0, posinf=0.0, neginf=0.0)
547
+ audio_duration = len(audio) / sr
548
+ st.write(f"Audio duration: {audio_duration:.2f} seconds")
549
+ if audio_duration < 5:
550
+ st.error("Audio too short (< 5s). Upload a longer video.")
551
+ return
552
+ summarizer_type = 'bart' if audio_duration <= 300 else 'led'
553
+ st.write(f"Using summarizer: {summarizer_type}")
554
+ st.session_state['summarizer_type'] = summarizer_type
555
+ st.write("Loading models...")
556
+ processor, model, sum_tokenizer, sum_model, device = load_model(language_code, summarizer_type)
557
+ st.write("Splitting audio into chunks...")
558
+ chunks = split_audio_into_chunks(audio, sr, chunk_duration)
559
+ st.write(f"Number of chunks: {len(chunks)}")
560
+ st.write("Transcribing audio...")
561
+ primary_transcript = process_chunks(chunks, sr, processor, model, device, language_code, chunk_duration, task="transcribe", transcript_file="temp_primary_transcript.json")
562
+ english_transcript = None
563
+ if st.session_state['translate_to_english'] and language_code == "ur":
564
+ st.write("Translating to English...")
565
+ processor, model, _, _, device = load_model('en', summarizer_type)
566
+ english_transcript = process_chunks(chunks, sr, processor, model, device, 'ur', chunk_duration, task="translate", transcript_file="temp_english_transcript.json")
567
+ st.session_state.update({
568
+ 'primary_transcript': primary_transcript,
569
+ 'english_transcript': english_transcript,
570
+ 'summary_generated': False,
571
+ 'app_state': 'results'
572
+ })
573
+ st.write("Processing completed successfully!")
574
+ st.rerun()
575
+ except Exception as e:
576
+ st.error(f"Processing failed: {str(e)}")
577
+ finally:
578
+ if os.path.exists(audio_path):
579
+ os.remove(audio_path)
580
+ for temp_file in ["temp_primary_transcript.json", "temp_english_transcript.json"]:
581
+ if os.path.exists(temp_file):
582
+ os.remove(temp_file)
583
+
584
+ if st.session_state['app_state'] == 'results':
585
+ # Center the original video
586
+ st.markdown('<div style="display: flex; justify-content: center;">', unsafe_allow_html=True)
587
+ st.video(st.session_state['video_path'], start_time=st.session_state['current_time'])
588
+ st.markdown('</div>', unsafe_allow_html=True)
589
+
590
+ st.session_state['show_timeframe'] = st.checkbox("Show timeframe in transcript", value=st.session_state['show_timeframe'])
591
+ st.markdown("### Search Subtitles")
592
+ search_query = st.text_input("Search subtitles...", value=st.session_state['search_query'], key="search_input")
593
+ st.session_state['search_query'] = search_query.lower()
594
+
595
+ # Primary Transcript
596
+ st.markdown(f"### {st.session_state['language']} Transcript")
597
+ primary_matches = 0
598
+ for text, start, end in st.session_state['primary_transcript']:
599
+ display_text = text.lower()
600
+ if not search_query or search_query in display_text:
601
+ primary_matches += 1
602
+ label = f"[{format_time(start)} - {format_time(end)}] {text}" if st.session_state['show_timeframe'] else text
603
+ if st.button(label, key=f"primary_{start}"):
604
+ st.session_state['current_time'] = start
605
+ st.rerun()
606
+ if primary_matches == 0 and search_query:
607
+ st.info("No matches found in primary transcript for the search query.")
608
+
609
+ # English Transcript (if available)
610
+ if st.session_state['english_transcript']:
611
+ st.markdown("### English Translation")
612
+ english_matches = 0
613
+ for text, start, end in st.session_state['english_transcript']:
614
+ display_text = text.lower()
615
+ if not search_query or search_query in display_text:
616
+ english_matches += 1
617
+ label = f"[{format_time(start)} - {format_time(end)}] {text}" if st.session_state['show_timeframe'] else text
618
+ if st.button(label, key=f"english_{start}"):
619
+ st.session_state['current_time'] = start
620
+ st.rerun()
621
+ if english_matches == 0 and search_query:
622
+ st.info("No matches found in English transcript for the search query.")
623
+
624
+ # Summary Generation
625
+ if (st.session_state['language_code'] == 'en' or st.session_state['translate_to_english']) and not st.session_state['summary_generated']:
626
+ if st.button("Generate Summary"):
627
+ with st.spinner("Generating summary..."):
628
+ try:
629
+ _, _, sum_tokenizer, sum_model, device = load_model(st.session_state['language_code'], st.session_state['summarizer_type'])
630
+ full_text = " ".join([text for text, _, _ in (st.session_state['english_transcript'] or st.session_state['primary_transcript'])])
631
+ english_summary = summarize_text(full_text, sum_tokenizer, sum_model, device, st.session_state['summarizer_type'])
632
+ st.session_state['english_summary'] = english_summary
633
+ st.session_state['summary_generated'] = True
634
+ except Exception as e:
635
+ st.error(f"Summary generation failed: {str(e)}")
636
+ if st.session_state['english_summary'] and st.session_state['summary_generated']:
637
+ st.markdown("### Summary")
638
+ st.write(st.session_state['english_summary'])
639
+
640
+ # Download Subtitles
641
+ st.markdown("### Download Subtitles")
642
+ include_timeframe = st.checkbox("Include timeframe in subtitles", value=True)
643
+ transcript_to_download = st.session_state['primary_transcript'] or st.session_state['english_transcript']
644
+ if transcript_to_download:
645
+ srt_content = generate_srt(transcript_to_download, include_timeframe)
646
+ st.download_button(label="Download Subtitles (SRT)", data=srt_content, file_name="subtitles.srt", mime="text/plain")
647
+
648
+ # Edit Subtitles
649
+ st.markdown("### Edit Subtitles")
650
+ transcript_to_edit = st.session_state['primary_transcript'] or st.session_state['english_transcript']
651
+ if transcript_to_edit and st.button("Delete Subtitles"):
652
+ st.session_state['app_state'] = 'editing'
653
+ st.rerun()
654
+
655
+ if st.session_state['app_state'] == 'editing':
656
+ st.markdown("### Delete Subtitles")
657
+ transcript_to_edit = st.session_state['primary_transcript'] or st.session_state['english_transcript']
658
+ for i, (text, start, end) in enumerate(transcript_to_edit):
659
+ st.write(f"{i}: [{format_time(start)} - {format_time(end)}] {text}")
660
+ indices_input = st.text_input("Enter the indices of subtitles to delete (comma-separated, e.g., 0,1,3):")
661
+ if st.button("Confirm Deletion"):
662
+ try:
663
+ delete_indices = [int(idx.strip()) for idx in indices_input.split(',') if idx.strip()]
664
+ delete_indices = [idx for idx in delete_indices if 0 <= idx < len(transcript_to_edit)]
665
+ keep_indices = [i for i in range(len(transcript_to_edit)) if i not in delete_indices]
666
+ if not keep_indices:
667
+ st.error("All subtitles are deleted. No video to generate.")
668
+ else:
669
+ edited_video_path = create_edited_video(st.session_state['video_path'], transcript_to_edit, keep_indices)
670
+ if edited_video_path:
671
+ st.session_state['edited_video_path'] = edited_video_path
672
+ st.session_state['app_state'] = 'results'
673
+ st.rerun()
674
+ except ValueError:
675
+ st.error("Invalid input. Please enter comma-separated integers.")
676
+ except Exception as e:
677
+ st.error(f"Error during video editing: {str(e)}")
678
+ if st.button("Cancel Deletion"):
679
+ st.session_state['app_state'] = 'results'
680
+ st.rerun()
681
+
682
+ if st.session_state['app_state'] == 'results' and st.session_state['edited_video_path']:
683
+ st.markdown("### Edited Video")
684
+ st.markdown('<div style="display: flex; justify-content: center;">', unsafe_allow_html=True)
685
+ st.video(st.session_state['edited_video_path'])
686
+ st.markdown('</div>', unsafe_allow_html=True)
687
+ with open(st.session_state['edited_video_path'], "rb") as file:
688
+ st.download_button(label="Download Edited Video", data=file, file_name="edited_video.mp4", mime="video/mp4")
689
+
690
+ if st.session_state.get('video_path') and st.button("Reset"):
691
+ if st.session_state['video_path'] and os.path.exists(st.session_state['video_path']):
692
+ os.remove(st.session_state['video_path'])
693
+ if st.session_state['edited_video_path'] and os.path.exists(st.session_state['edited_video_path']):
694
+ os.remove(st.session_state['edited_video_path'])
695
+ st.session_state.clear()
696
+ st.rerun()
697
+
698
+ st.markdown("""
699
+ <div style='text-align: center;'>
700
+ <h2 style='color: black'>Why VidEp Stands Out</h2>
701
+ </div>
702
+ <div class="feature-box">
703
+ <div class="feature"><i class="fas fa-cloud-upload-alt"></i><br>Cloud Upload</div>
704
+ <div class="feature"><i class="fas fa-search"></i><br>Smart Search</div>
705
+ <div class="feature"><i class="fas fa-edit"></i><br>Easy Editing</div>
706
+ <div class="feature"><i class="fas fa-file-alt"></i><br>AI Summary</div>
707
+ </div>
708
+ """, unsafe_allow_html=True)
709
+
710
+ st.markdown("""
711
+ <div id="about" class="about-section" style="padding: 3rem 2rem; background: write a complete Streamlit application that includes all the fixes and improvements for the search function, ensuring it works as expected. Below is the complete corrected code with the search function fixed, along with explanations of the changes made.
712
+
713
+ ---
714
+
715
+ ### Explanation of Fixes and Improvements
716
+
717
+ 1. **Search Query Handling**:
718
+ - The search query is captured using `st.text_input`, and the value is stored in `st.session_state['search_query']` as a lowercase string to ensure case-insensitive searching.
719
+ - The transcript display logic now checks if the search query is empty or if it exists within the lowercase version of each transcript segment’s text (`display_text`).
720
+ - A counter (`primary_matches` and `english_matches`) is introduced to track the number of matching segments. If no matches are found and a search query is provided, an `st.info` message is displayed to inform the user.
721
+
722
+ 2. **Case-Insensitive Search**:
723
+ - Both the search query and the transcript text are converted to lowercase before comparison, ensuring the search is case-insensitive (`search_query.lower()` and `text.lower()`).
724
+
725
+ 3. **UI Feedback**:
726
+ - When no matches are found for the search query, a message like "No matches found in primary transcript for the search query" is displayed using `st.info`. This improves user experience by providing clear feedback.
727
+
728
+ 4. **Streamlit Rerun**:
729
+ - The search input is tied to `st.session_state['search_query']`, and Streamlit’s reactivity ensures that changing the search query updates the displayed transcript segments automatically without requiring an explicit rerun (unless a button is clicked for navigation).
730
+
731
+ 5. **Code Organization**:
732
+ - The transcript display logic is split clearly between primary and English transcripts, with separate match counters to handle each case independently.
733
+ - The code remains modular, with no changes to other functionalities like transcription, summarization, or video editing, ensuring the fix is isolated to the search function.
734
+
735
+ ---
736
+
737
+ ### Complete Corrected Code
738
+
739
+ ```python
740
+ import streamlit as st
741
+ import tempfile
742
+ import os
743
+ import torch
744
+ from transformers import AutoProcessor, AutoModelForSpeechSeq2Seq, AutoTokenizer, AutoModelForSeq2SeqLM
745
+ import librosa
746
+ import numpy as np
747
+ import ffmpeg
748
+ import time
749
+ import json
750
+ import psutil
751
+
752
+ st.set_page_config(layout="wide")
753
+
754
+ # Updated CSS with video styling
755
+ st.markdown("""
756
+ <style>
757
+ @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700&display=swap');
758
+
759
+ .stApp {
760
+ background-color: #ffffff;
761
+ font-family: 'Poppins', sans-serif;
762
+ color: #1a1a1a;
763
+ }
764
+
765
+ /* Hide Streamlit's default elements */
766
+ [data-testid="stToolbar"], [data-testid="stDecoration"], [data-testid="stStatusWidget"] {
767
+ display: none;
768
+ }
769
+
770
+ /* Header */
771
+ .header {
772
+ background: #ffffff;
773
+ padding: 1rem 2rem;
774
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
775
+ display: flex;
776
+ justify-content: space-between;
777
+ align-items: center;
778
+ position: sticky;
779
+ top: 0;
780
+ z-index: 100;
781
+ }
782
+ .logo img {
783
+ height: 60px;
784
+ width: auto;
785
+ }
786
+ .navbar {
787
+ list-style: none;
788
+ display: flex;
789
+ gap: 1.5rem;
790
+ margin: 0;
791
+ }
792
+ .navbar li a {
793
+ text-decoration: none;
794
+ font-size: 28px;
795
+ font-weight: bold;
796
+ color: # = 1.0rem;
797
+ position: relative;
798
+ padding: 10px 15px;
799
+ transition: text-shadow 0.3s ease-in-out;
800
+ text-shadow: 5px 5px 12px rgba(0, 0, 0, 0.5);
801
+ }
802
+ .navbar li a:hover {
803
+ color: #ff6f61;
804
+ }
805
+
806
+ /* Hero Section */
807
+ .hero {
808
+ background: linear-gradient(to right, #2b5876, #4e4376);
809
+ background-size: cover;
810
+ color: #ffffff;
811
+ padding: 2rem 2rem;
812
+ border-radius: 1rem;
813
+ text-align: center;
814
+ margin: 2rem 0;
815
+ max-height: 200px;
816
+ }
817
+ .hero h esque
818
+ }
819
+ .hero p {
820
+ font-size: 1.2rem;
821
+ font-weight: 300;
822
+ }
823
+
824
+ /* Feature Section */
825
+ .feature-box {
826
+ display: flex;
827
+ justify-content: center;
828
+ gap: 1.5rem;
829
+ margin: 3rem 0;
830
+ flex-wrap: wrap;
831
+ }
832
+ .feature {
833
+ background: #f8f9fa;
834
+ padding: 1.5rem;
835
+ border-radius: 1rem;
836
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
837
+ width: 200px;
838
+ text-align: center;
839
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
840
+ }
841
+ .feature:hover {
842
+ transform: translateY(-8px) scale(1.03);
843
+ box-shadow: 0 12px 24px rgba(0, 0, 0, 0.25);
844
+ transition: all 0.3s ease;
845
+ border: 1px solid rgba(0, 0, 0, 0.1);
846
+ background-color: #fff;
847
+ filter: brightness(1.05);
848
+ z-index: 10;
849
+ }
850
+ .feature i {
851
+ font-size: 1.5rem;
852
+ color: #2196f3;
853
+ margin-bottom: 0.5rem;
854
+ }
855
+
856
+ /* Plans Section */
857
+ .plans {
858
+ padding: 3rem 2rem;
859
+ background: #f1f4f8;
860
+ border-radius: 1rem;
861
+ }
862
+ .plan-box {
863
+ display: flex;
864
+ justify-content: center;
865
+ gap: 1.5rem;
866
+ flex-wrap: wrap;
867
+ }
868
+ .plan {
869
+ background: #ffffff;
870
+ padding: 2rem;
871
+ border-radius: 1rem;
872
+ width: 250px;
873
+ text-align: center;
874
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
875
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
876
+ border-top: 4px solid #28a745;
877
+ height: 290px;
878
+ padding-top: 10px;
879
+ }
880
+ .plan:hover {
881
+ transform: translateY(-5px);
882
+ box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15);
883
+ }
884
+ .plan h3 {
885
+ font-size: 1.5rem;
886
+ margin-bottom: 0.5rem;
887
+ }
888
+ .plan.free { border-top: 4px solid #28a745; }
889
+ .plan.premium { border-top: 4px solid #ff6f61; }
890
+ .plan.business { border-top: 4px solid #2196f3; }
891
+
892
+ /* Buttons */
893
+ .stButton>button {
894
+ background: linear-gradient(135deg, #ff6f61, #ff8a65) !important;
895
+ color: #ffffff !important;
896
+ font-weight: 600 !important;
897
+ padding: 0.75rem 1.5rem !important;
898
+ border-radius: 0.5rem !important;
899
+ border: none !important;
900
+ transition: transform 0.2s ease, box-shadow 0.2s ease !important;
901
+ }
902
+ .stButton>button:hover {
903
+ transform: translateY(-2px) !important;
904
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2) !important;
905
+ }
906
+
907
+ /* File Uploader */
908
+ .uploadedFile {
909
+ border: 2px dashed #2196f3;
910
+ border-radius: 1rem;
911
+ padding: 2rem;
912
+ background: #f8f9fa;
913
+ margin: 2rem 0;
914
+ }
915
+
916
+ /* Progress Bar */
917
+ .stProgress > div > div {
918
+ background: linear-gradient(90deg, #2196f3, #4fc3f7) !important;
919
+ }
920
+
921
+ /* Text Area */
922
+ .stTextArea textarea {
923
+ border-radius: 0.5rem;
924
+ border: 1px solid #e0e0e0;
925
+ padding: 1rem;
926
+ font-family: 'Poppins', sans-serif;
927
+ }
928
+
929
+ /* Video player styling */
930
  video {
931
  display: block;
932
  width: 350px !important;
 
1327
  st.markdown("### Search Subtitles")
1328
  search_query = st.text_input("Search subtitles...", value=st.session_state['search_query'], key="search_input")
1329
  st.session_state['search_query'] = search_query.lower()
1330
+
1331
+ # Primary Transcript
1332
  st.markdown(f"### {st.session_state['language']} Transcript")
1333
+ primary_matches = 0
1334
  for text, start, end in st.session_state['primary_transcript']:
1335
  display_text = text.lower()
1336
  if not search_query or search_query in display_text:
1337
+ primary_matches += 1
1338
  label = f"[{format_time(start)} - {format_time(end)}] {text}" if st.session_state['show_timeframe'] else text
1339
  if st.button(label, key=f"primary_{start}"):
1340
  st.session_state['current_time'] = start
1341
  st.rerun()
1342
+ if primary_matches == 0 and search_query:
1343
+ st.info("No matches found in primary transcript for the search query.")
1344
+
1345
+ # English Transcript (if available)
1346
  if st.session_state['english_transcript']:
1347
  st.markdown("### English Translation")
1348
+ english_matches = 0
1349
  for text, start, end in st.session_state['english_transcript']:
1350
  display_text = text.lower()
1351
  if not search_query or search_query in display_text:
1352
+ english_matches += 1
1353
  label = f"[{format_time(start)} - {format_time(end)}] {text}" if st.session_state['show_timeframe'] else text
1354
  if st.button(label, key=f"english_{start}"):
1355
  st.session_state['current_time'] = start
1356
  st.rerun()
1357
+ if english_matches == 0 and search_query:
1358
+ st.info("No matches found in English transcript for the search query.")
1359
+
1360
+ # Summary Generation
1361
  if (st.session_state['language_code'] == 'en' or st.session_state['translate_to_english']) and not st.session_state['summary_generated']:
1362
  if st.button("Generate Summary"):
1363
  with st.spinner("Generating summary..."):
 
1372
  if st.session_state['english_summary'] and st.session_state['summary_generated']:
1373
  st.markdown("### Summary")
1374
  st.write(st.session_state['english_summary'])
1375
+
1376
+ # Download Subtitles
1377
  st.markdown("### Download Subtitles")
1378
  include_timeframe = st.checkbox("Include timeframe in subtitles", value=True)
1379
  transcript_to_download = st.session_state['primary_transcript'] or st.session_state['english_transcript']
1380
  if transcript_to_download:
1381
  srt_content = generate_srt(transcript_to_download, include_timeframe)
1382
  st.download_button(label="Download Subtitles (SRT)", data=srt_content, file_name="subtitles.srt", mime="text/plain")
1383
+
1384
+ # Edit Subtitles
1385
  st.markdown("### Edit Subtitles")
1386
  transcript_to_edit = st.session_state['primary_transcript'] or st.session_state['english_transcript']
1387
  if transcript_to_edit and st.button("Delete Subtitles"):
 
1417
 
1418
  if st.session_state['app_state'] == 'results' and st.session_state['edited_video_path']:
1419
  st.markdown("### Edited Video")
 
1420
  st.markdown('<div style="display: flex; justify-content: center;">', unsafe_allow_html=True)
1421
  st.video(st.session_state['edited_video_path'])
1422
  st.markdown('</div>', unsafe_allow_html=True)