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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +7 -744
app.py CHANGED
@@ -12,7 +12,7 @@ import psutil
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');
@@ -457,743 +457,7 @@ def generate_srt(transcript, include_timeframe=True):
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;
933
- height: 500px !important;
934
- object-fit: contain;
935
- margin: 0 auto;
936
- border: 3px solid #2196f3;
937
- border-radius: 8px;
938
- }
939
-
940
- /* Footer */
941
- footer {
942
- background: #1a1a1a;
943
- color: #ffffff;
944
- padding: 3rem 2rem;
945
- margin-top: 3rem;
946
- border-radius: 1rem 1rem 0 0;
947
- }
948
- .footer-container {
949
- display: flex;
950
- justify-content: space-around;
951
- gap: 2rem;
952
- flex-wrap: wrap;
953
- }
954
- .footer-section h4 {
955
- font-size: 1.8rem;
956
- margin-bottom: 1rem;
957
- }
958
- .footer-section ul {
959
- list-style: none;
960
- padding: 0;
961
- }
962
- .footer-section ul li a {
963
- color: #bbbbbb;
964
- text-decoration: none;
965
- font-size: 1.6rem;
966
- transition: color 0.3s ease;
967
- }
968
- .footer-section ul li a:hover {
969
- color: #ff6f61;
970
- }
971
- .footer-bottom {
972
- margin-top: 2rem;
973
- font-size: 0.9rem;
974
- }
975
-
976
- /* Responsive Design */
977
- @media (max-width: 768px) {
978
- .header {
979
- flex-direction: column;
980
- gap: 1rem;
981
- }
982
- .navbar {
983
- flex-direction: column;
984
- gap: 0.5rem;
985
- }
986
- .hero h1 {
987
- font-size: 1.8rem;
988
- }
989
- .hero p {
990
- font-size: 1rem;
991
- }
992
- .feature, .plan {
993
- width: 100%;
994
- max-width: 300px;
995
- }
996
- }
997
- </style>
998
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
999
- """, unsafe_allow_html=True)
1000
-
1001
- # Function Definitions
1002
- def format_time(seconds):
1003
- minutes = int(seconds // 60)
1004
- secs = int(seconds % 60)
1005
- return f"{minutes}:{secs:02d}"
1006
-
1007
- def seconds_to_srt_time(seconds):
1008
- hours = int(seconds // 3600)
1009
- minutes = int((seconds % 3600) // 60)
1010
- secs = int(seconds % 60)
1011
- millis = int((seconds - int(seconds)) * 1000)
1012
- return f"{hours:02d}:{minutes:02d}:{secs:02d},{millis:03d}"
1013
-
1014
- class TranscriptionProgress:
1015
- def __init__(self):
1016
- self.progress_bar = None
1017
- self.status_text = None
1018
- def init_progress(self):
1019
- self.progress_bar = st.progress(0.0)
1020
- self.status_text = st.empty()
1021
- def update(self, progress: float, status: str):
1022
- progress = max(0.0, min(1.0, progress))
1023
- if self.progress_bar is not None:
1024
- self.progress_bar.progress(progress)
1025
- if self.status_text is not None:
1026
- self.status_text.text(status)
1027
-
1028
- @st.cache_resource
1029
- def load_model(language='en', summarizer_type='bart'):
1030
- device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
1031
- if language == 'ur':
1032
- processor = AutoProcessor.from_pretrained("GogetaBlueMUI/whisper-medium-ur-fleurs-v2")
1033
- model = AutoModelForSpeechSeq2Seq.from_pretrained("GogetaBlueMUI/whisper-medium-ur-fleurs-v2").to(device)
1034
- else:
1035
- processor = AutoProcessor.from_pretrained("openai/whisper-small")
1036
- model = AutoModelForSpeechSeq2Seq.from_pretrained("openai/whisper-small").to(device)
1037
- if device.type == "cuda":
1038
- model = model.half()
1039
- if summarizer_type == 'bart':
1040
- sum_tokenizer = AutoTokenizer.from_pretrained("facebook/bart-large-cnn")
1041
- sum_model = AutoModelForSeq2SeqLM.from_pretrained("facebook/bart-large-cnn").to(device)
1042
- else:
1043
- sum_tokenizer = AutoTokenizer.from_pretrained("pszemraj/led-large-book-summary")
1044
- sum_model = AutoModelForSeq2SeqLM.from_pretrained("pszemraj/led-large-book-summary").to(device)
1045
- return processor, model, sum_tokenizer, sum_model, device
1046
-
1047
- def split_audio_into_chunks(audio, sr, chunk_duration):
1048
- chunk_samples = int(chunk_duration * sr)
1049
- chunks = [audio[start:start + chunk_samples] for start in range(0, len(audio), chunk_samples)]
1050
- return chunks
1051
-
1052
- def transcribe_audio(audio, sr, processor, model, device, start_time, language, task="transcribe"):
1053
- inputs = processor(audio, sampling_rate=sr, return_tensors="pt")
1054
- input_features = inputs.input_features.to(device)
1055
- if model.dtype == torch.float16:
1056
- input_features = input_features.half()
1057
- generate_kwargs = {
1058
- "task": task,
1059
- "language": "urdu" if language == "ur" else language,
1060
- "max_new_tokens": 128,
1061
- "return_timestamps": True
1062
- }
1063
- try:
1064
- with torch.no_grad():
1065
- outputs = model.generate(input_features, **generate_kwargs)
1066
- text = processor.decode(outputs[0], skip_special_tokens=True)
1067
- return [(text, start_time, start_time + len(audio) / sr)]
1068
- except Exception as e:
1069
- st.error(f"Transcription error: {str(e)}")
1070
- return [(f"Error: {str(e)}", start_time, start_time + len(audio) / sr)]
1071
-
1072
- def process_chunks(chunks, sr, processor, model, device, language, chunk_duration, task="transcribe", transcript_file="temp_transcript.json"):
1073
- transcript = []
1074
- chunk_start = 0
1075
- total_chunks = len(chunks)
1076
- progress_bar = st.progress(0)
1077
- status_text = st.empty()
1078
- if os.path.exists(transcript_file):
1079
- os.remove(transcript_file)
1080
- for i, chunk in enumerate(chunks):
1081
- status_text.text(f"Processing chunk {i+1}/{total_chunks}...")
1082
- try:
1083
- memory = psutil.virtual_memory()
1084
- st.write(f"Memory usage: {memory.percent}% (Chunk {i+1}/{total_chunks})")
1085
- chunk_transcript = transcribe_audio(chunk, sr, processor, model, device, chunk_start, language, task)
1086
- transcript.extend(chunk_transcript)
1087
- with open(transcript_file, "w", encoding="utf-8") as f:
1088
- json.dump(transcript, f, ensure_ascii=False)
1089
- chunk_start += chunk_duration
1090
- progress_bar.progress((i + 1) / total_chunks)
1091
- except Exception as e:
1092
- st.error(f"Error processing chunk {i+1}: {str(e)}")
1093
- break
1094
- status_text.text("Processing complete!")
1095
- progress_bar.empty()
1096
- return transcript
1097
-
1098
- def summarize_text(text, tokenizer, model, device, summarizer_type='bart'):
1099
- if summarizer_type == 'bart':
1100
- max_input_length = 1024
1101
- max_summary_length = 150
1102
- chunk_size = 512
1103
- else:
1104
- max_input_length = 16384
1105
- max_summary_length = 512
1106
- chunk_size = 8192
1107
- inputs = tokenizer(text, return_tensors="pt", truncation=False)
1108
- input_ids = inputs["input_ids"].to(device)
1109
- num_tokens = input_ids.shape[1]
1110
- st.write(f"Number of tokens in input: {num_tokens}")
1111
- if num_tokens < 50:
1112
- return "Transcript too short to summarize effectively."
1113
- try:
1114
- summaries = []
1115
- if num_tokens <= max_input_length:
1116
- truncated_inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=max_input_length).to(device)
1117
- with torch.no_grad():
1118
- 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)
1119
- summaries.append(tokenizer.decode(summary_ids[0], skip_special_tokens=True))
1120
- else:
1121
- st.write(f"Transcript exceeds {max_input_length} tokens. Processing in chunks...")
1122
- tokens = input_ids[0].tolist()
1123
- for i in range(0, num_tokens, chunk_size):
1124
- chunk_tokens = tokens[i:i + chunk_size]
1125
- chunk_input_ids = torch.tensor([chunk_tokens]).to(device)
1126
- with torch.no_grad():
1127
- 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)
1128
- summaries.append(tokenizer.decode(summary_ids[0], skip_special_tokens=True))
1129
- combined_summary = " ".join(summaries)
1130
- combined_inputs = tokenizer(combined_summary, return_tensors="pt", truncation=True, max_length=max_input_length).to(device)
1131
- with torch.no_grad():
1132
- 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)
1133
- summaries = [tokenizer.decode(final_summary_ids[0], skip_special_tokens=True)]
1134
- return " ".join(summaries)
1135
- except Exception as e:
1136
- st.error(f"Summarization error: {str(e)}")
1137
- return f"Error: {str(e)}"
1138
-
1139
- def save_uploaded_file(uploaded_file):
1140
- try:
1141
- with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_file:
1142
- tmp_file.write(uploaded_file.read())
1143
- return tmp_file.name
1144
- except Exception as e:
1145
- st.error(f"Error saving uploaded file: {str(e)}")
1146
- return None
1147
-
1148
- def merge_intervals(intervals):
1149
- if not intervals:
1150
- return []
1151
- intervals.sort(key=lambda x: x[0])
1152
- merged = [intervals[0]]
1153
- for current in intervals[1:]:
1154
- previous = merged[-1]
1155
- if previous[1] >= current[0]:
1156
- merged[-1] = (previous[0], max(previous[1], current[1]))
1157
- else:
1158
- merged.append(current)
1159
- return merged
1160
-
1161
- def create_edited_video(video_path, transcript, keep_indices):
1162
- try:
1163
- intervals_to_keep = [(transcript[i][1], transcript[i][2]) for i in keep_indices]
1164
- merged_intervals = merge_intervals(intervals_to_keep)
1165
- temp_files = []
1166
- for j, (start, end) in enumerate(merged_intervals):
1167
- temp_file = f"temp_{j}.mp4"
1168
- ffmpeg.input(video_path, ss=start, to=end).output(temp_file, c='copy').run(overwrite_output=True, quiet=True)
1169
- temp_files.append(temp_file)
1170
- with open("list.txt", "w") as f:
1171
- for temp_file in temp_files:
1172
- f.write(f"file '{temp_file}'\n")
1173
- edited_video_path = "edited_video.mp4"
1174
- ffmpeg.input('list.txt', format='concat', safe=0).output(edited_video_path, c='copy').run(overwrite_output=True, quiet=True)
1175
- for temp_file in temp_files:
1176
- if os.path.exists(temp_file):
1177
- os.remove(temp_file)
1178
- if os.path.exists("list.txt"):
1179
- os.remove("list.txt")
1180
- return edited_video_path
1181
- except Exception as e:
1182
- st.error(f"Error creating edited video: {str(e)}")
1183
- return None
1184
-
1185
- def generate_srt(transcript, include_timeframe=True):
1186
- srt_content = ""
1187
- for text, start, end in transcript:
1188
- if include_timeframe:
1189
- start_time = seconds_to_srt_time(start)
1190
- end_time = seconds_to_srt_time(end)
1191
- srt_content += f"{start_time} --> {end_time}\n{text}\n\n"
1192
- else:
1193
- srt_content += f"{text}\n\n"
1194
- return srt_content
1195
-
1196
- # Main Function with Centered Video Display
1197
  def main():
1198
  st.markdown("""
1199
  <div class="header">
@@ -1216,7 +480,7 @@ def main():
1216
  </div>
1217
  """, unsafe_allow_html=True)
1218
 
1219
- # Initialize session state variables
1220
  if 'app_state' not in st.session_state:
1221
  st.session_state['app_state'] = 'upload'
1222
  if 'video_path' not in st.session_state:
@@ -1318,7 +582,6 @@ def main():
1318
  os.remove(temp_file)
1319
 
1320
  if st.session_state['app_state'] == 'results':
1321
- # Center the original video
1322
  st.markdown('<div style="display: flex; justify-content: center;">', unsafe_allow_html=True)
1323
  st.video(st.session_state['video_path'], start_time=st.session_state['current_time'])
1324
  st.markdown('</div>', unsafe_allow_html=True)
@@ -1342,7 +605,7 @@ def main():
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
@@ -1474,11 +737,11 @@ def main():
1474
  <div style="max-width: 600px; margin: 0 auto;">
1475
  <div style="margin-bottom: 1rem;">
1476
  <label for="email" style="display: block; margin-bottom: 0.5rem; font-weight: 500;">Email</label>
1477
- <input type="email" id="email" placeholder="Your email address" style="width: 100%; padding: 0.75rem; background-color:white; color:white; border-radius: 0.5rem; border: 1px solid #e0e0e0;">
1478
  </div>
1479
  <div style="margin-bottom: 1rem;">
1480
  <label for="message" style="display: block; margin-bottom: 0.5rem; font-weight: 500;">Message</label>
1481
- <textarea id="message" rows="5" placeholder="Your message" style="width: 100%; padding: 0.75rem; border-radius: 0.5rem; background-color:white; color:white; border: 1px solid #e0e0e0;"></textarea>
1482
  </div>
1483
  <button onclick="alert('Message sent successfully!')" style="background: linear-gradient(135deg, #ff6f61, #ff8a65); color: white; font-weight: 600; padding: 0.75rem 1.5rem; border-radius: 0.5rem; border: none; cursor: pointer; width: 100%;">Send Message</button>
1484
  </div>
@@ -1487,7 +750,7 @@ def main():
1487
 
1488
  st.markdown("""
1489
  <div class="plans">
1490
- <h2 style="text-align: center; margin-bottom: 2rem;color: black;">Choose Your Plan</h2>
1491
  <div class="plan-box">
1492
  <div class="plan free" style="background: linear-gradient(135deg, #299f45, #185726); padding-bottom: 0px">
1493
  <h3 style="color: white;">Free</h3>
 
12
 
13
  st.set_page_config(layout="wide")
14
 
15
+ # CSS Styling
16
  st.markdown("""
17
  <style>
18
  @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700&display=swap');
 
457
  srt_content += f"{text}\n\n"
458
  return srt_content
459
 
460
+ # Main Function
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
461
  def main():
462
  st.markdown("""
463
  <div class="header">
 
480
  </div>
481
  """, unsafe_allow_html=True)
482
 
483
+ # Initialize session state
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:
 
582
  os.remove(temp_file)
583
 
584
  if st.session_state['app_state'] == 'results':
 
585
  st.markdown('<div style="display: flex; justify-content: center;">', unsafe_allow_html=True)
586
  st.video(st.session_state['video_path'], start_time=st.session_state['current_time'])
587
  st.markdown('</div>', unsafe_allow_html=True)
 
605
  if primary_matches == 0 and search_query:
606
  st.info("No matches found in primary transcript for the search query.")
607
 
608
+ # English Transcript
609
  if st.session_state['english_transcript']:
610
  st.markdown("### English Translation")
611
  english_matches = 0
 
737
  <div style="max-width: 600px; margin: 0 auto;">
738
  <div style="margin-bottom: 1rem;">
739
  <label for="email" style="display: block; margin-bottom: 0.5rem; font-weight: 500;">Email</label>
740
+ <input type="email" id="email" placeholder="Your email address" style="width: 100%; padding: 0.75rem; border-radius: 0.5rem; border: 1px solid #e0e0e0;">
741
  </div>
742
  <div style="margin-bottom: 1rem;">
743
  <label for="message" style="display: block; margin-bottom: 0.5rem; font-weight: 500;">Message</label>
744
+ <textarea id="message" rows="5" placeholder="Your message" style="width: 100%; padding: 0.75rem; border-radius: 0.5rem; border: 1px solid #e0e0e0;"></textarea>
745
  </div>
746
  <button onclick="alert('Message sent successfully!')" style="background: linear-gradient(135deg, #ff6f61, #ff8a65); color: white; font-weight: 600; padding: 0.75rem 1.5rem; border-radius: 0.5rem; border: none; cursor: pointer; width: 100%;">Send Message</button>
747
  </div>
 
750
 
751
  st.markdown("""
752
  <div class="plans">
753
+ <h2 style="text-align: center; margin-bottom: 2rem; color: black;">Choose Your Plan</h2>
754
  <div class="plan-box">
755
  <div class="plan free" style="background: linear-gradient(135deg, #299f45, #185726); padding-bottom: 0px">
756
  <h3 style="color: white;">Free</h3>