File size: 9,795 Bytes
cec74c1
7af809b
 
cec74c1
113eac1
cec74c1
 
 
 
 
113eac1
cec74c1
55943c4
113eac1
cec74c1
3a785ad
cec74c1
3a785ad
cec74c1
 
55943c4
593b83c
7af809b
55943c4
 
cec74c1
 
113eac1
cec74c1
113eac1
7af809b
113eac1
 
 
 
 
 
 
 
 
7af809b
 
113eac1
 
 
cec74c1
7af809b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cec74c1
 
7af809b
cec74c1
7af809b
cec74c1
7af809b
55943c4
 
 
 
 
 
 
 
 
 
 
7af809b
55943c4
 
 
 
 
 
 
 
 
 
 
cec74c1
3a785ad
3f39059
7af809b
3f39059
7af809b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55943c4
7af809b
 
 
 
 
 
 
 
 
 
 
 
55943c4
7af809b
d180905
 
 
7af809b
d180905
 
3a785ad
cec74c1
3a785ad
7af809b
dc7f33e
cec74c1
dc7f33e
cec74c1
113eac1
7af809b
593b83c
cec74c1
 
 
 
9765bc3
 
 
 
 
 
 
 
 
 
cec74c1
 
 
 
 
 
593b83c
cec74c1
 
593b83c
cec74c1
 
 
593b83c
 
 
9765bc3
593b83c
 
 
 
cec74c1
 
 
593b83c
 
 
 
 
 
 
9765bc3
 
 
 
 
 
 
dc7f33e
cec74c1
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
"""
AI Graduate Job-Matching Platform
Production-Safe Version with Retry & Chunk Handling
Developer: Najaf Ali Sharqi
MoviePy-free (uses ffmpeg + tempfile)
"""

import os
import gradio as gr
import tempfile
import subprocess
import math
import time
from groq import Groq

# ============================================================================
# CONFIGURATION
# ============================================================================
client = Groq(api_key=os.getenv("GROQ_API_KEY"))

TRANSCRIPTION_MODEL = "whisper-large-v3"
REWRITE_MODEL = "openai/gpt-oss-120b"
CHUNK_SIZE = 300  # 5 min chunks for transcription to avoid rate limit
REWRITE_CHUNK_SIZE = 4000  # words per rewrite chunk
MAX_RETRIES = 5  # retries for 429 errors

# ============================================================================
# UTILITY FUNCTIONS
# ============================================================================
def get_video_duration(video_path):
    """Get video duration using ffprobe"""
    try:
        result = subprocess.run(
            ["ffprobe", "-v", "error", "-show_entries",
             "format=duration", "-of",
             "default=noprint_wrappers=1:nokey=1", video_path],
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
            text=True
        )
        duration = float(result.stdout.strip())
        return duration
    except Exception as e:
        raise RuntimeError(f"Failed to get video duration: {str(e)}")

def extract_audio_chunks(video_path, chunk_size=CHUNK_SIZE):
    """Extract audio from video and split into chunks"""
    try:
        duration = get_video_duration(video_path)
        num_chunks = math.ceil(duration / chunk_size)
        audio_chunks = []

        # extract full audio first
        temp_audio_full = tempfile.NamedTemporaryFile(suffix=".mp3", delete=False)
        audio_full_path = temp_audio_full.name
        temp_audio_full.close()

        os.system(f"ffmpeg -y -i \"{video_path}\" -q:a 0 -map a \"{audio_full_path}\"")

        # split audio into chunks
        for i in range(num_chunks):
            start = i * chunk_size
            end = min((i + 1) * chunk_size, duration)
            temp_chunk = tempfile.NamedTemporaryFile(suffix=".mp3", delete=False)
            chunk_path = temp_chunk.name
            temp_chunk.close()
            os.system(f"ffmpeg -y -i \"{audio_full_path}\" -ss {start} -to {end} -c copy \"{chunk_path}\"")
            audio_chunks.append(chunk_path)

        # remove full audio file
        if os.path.exists(audio_full_path):
            os.unlink(audio_full_path)

        return audio_chunks
    except Exception as e:
        raise RuntimeError(f"Audio extraction failed: {str(e)}")

# ============================================================================
# TRANSCRIPTION WITH RETRY
# ============================================================================
def transcribe_audio_chunks(audio_chunks, language="English"):
    transcript_text = ""
    for chunk in audio_chunks:
        for attempt in range(MAX_RETRIES):
            try:
                with open(chunk, "rb") as audio_file:
                    transcription = client.audio.transcriptions.create(
                        file=(os.path.basename(chunk), audio_file.read()),
                        model=TRANSCRIPTION_MODEL,
                        temperature=0,
                        response_format="verbose_json",
                        language="en" if language=="English" else "ur"
                    )
                transcript_text += transcription.text + "\n\n"
                break  # success
            except Exception as e:
                err_str = str(e)
                if "Rate limit reached" in err_str or "429" in err_str:
                    wait_time = 5 + attempt * 5
                    print(f"Rate limit hit. Waiting {wait_time}s before retrying...")
                    time.sleep(wait_time)
                else:
                    transcript_text += f"[Error in chunk transcription: {err_str}]\n\n"
                    break
        if os.path.exists(chunk):
            os.unlink(chunk)
    return transcript_text.strip()

# ============================================================================
# GRAMMAR & PUNCTUATION CORRECTION (Chunked)
# ============================================================================
def rewrite_transcript_chunked(text, language="English", chunk_size=REWRITE_CHUNK_SIZE):
    """Rewrite transcript in smaller chunks to avoid token limits"""
    try:
        words = text.split()
        rewritten_chunks = []
        for i in range(0, len(words), chunk_size):
            chunk_text = " ".join(words[i:i+chunk_size])
            prompt = f"Correct grammar, punctuation, and make it readable. Language: {language}\n\n{chunk_text}"
            for attempt in range(MAX_RETRIES):
                try:
                    response = client.chat.completions.create(
                        model=REWRITE_MODEL,
                        messages=[{"role": "user", "content": prompt}],
                        temperature=0
                    )
                    rewritten_chunks.append(response.choices[0].message.content)
                    break
                except Exception as e:
                    err_str = str(e)
                    if "Rate limit reached" in err_str or "429" in err_str:
                        wait_time = 5 + attempt*5
                        print(f"Rewrite rate limit hit. Waiting {wait_time}s before retrying...")
                        time.sleep(wait_time)
                    else:
                        rewritten_chunks.append(f"[Error rewriting chunk: {err_str}]")
                        break
        return "\n\n".join(rewritten_chunks)
    except Exception as e:
        return f"[Error rewriting transcript: {str(e)}]"

def correct_transcript(transcript_text, language):
    lang = "English" if "English" in language else "Urdu"
    if not transcript_text:
        return "", "Please generate transcript first." if lang=="English" else "پہلے ٹرانسکرپٹ بنائیں۔"
    rewritten_text = rewrite_transcript_chunked(transcript_text, language=lang)
    return rewritten_text, "✅ Grammar & punctuation corrected / گرامر اور پنکچویشن درست کی گئی"

# ============================================================================
# PROCESS VIDEO
# ============================================================================
def generate_transcript(video_file, language):
    lang = "English" if "English" in language else "Urdu"
    if not video_file:
        return "", "Please upload a video file." if lang=="English" else "براہ کرم ایک ویڈیو فائل اپ لوڈ کریں۔"
    try:
        audio_chunks = extract_audio_chunks(video_file.name)
        transcript = transcribe_audio_chunks(audio_chunks, language=lang)
        return transcript, "✅ Transcription completed / ٹرانسکرپشن مکمل"
    except Exception as e:
        error_msg = str(e)
        return "", error_msg if lang=="English" else "خرابی: " + error_msg

# ============================================================================
# DOWNLOAD FUNCTION
# ============================================================================
def download_transcript(text, language):
    lang_suffix = "en" if "English" in language else "ur"
    temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=f"_{lang_suffix}.txt")
    temp_file.write(text.encode("utf-8"))
    temp_file.close()
    return temp_file.name

# ============================================================================
# GRADIO UI
# ============================================================================
def create_ui():
    with gr.Blocks(title="AI Graduate Job Matcher") as demo:
        gr.HTML("<h1>🎓 AI Graduate Job-Matching Platform</h1><p>Developer: Najaf Ali Sharqi</p>")
        
        video_input = gr.File(label="Upload Video / ویڈیو اپ لوڈ کریں", file_types=[".mp4", ".mov", ".avi"])
        language_dropdown = gr.Dropdown(choices=["English", "Urdu / اردو"], value="English", label="Output Language / آؤٹ پٹ زبان")
        
        transcript_output = gr.Textbox(label="Transcript / ٹرانسکرپٹ", lines=20)
        status_output = gr.Textbox(label="Status / حیثیت", lines=1)
        
        # Buttons
        transcribe_btn = gr.Button("🚀 Generate Transcript / ٹرانسکرپٹ بنائیں")
        rewrite_btn = gr.Button("✏️ Correct Grammar & Punctuation / گرامر درست کریں")
        download_btn = gr.Button("💾 Download Transcript / ٹرانسکرپٹ ڈاؤن لوڈ کریں")
        
        # Transcription click
        transcribe_btn.click(
            fn=generate_transcript,
            inputs=[video_input, language_dropdown],
            outputs=[transcript_output, status_output]
        )
        
        # Grammar correction click
        rewrite_btn.click(
            fn=correct_transcript,
            inputs=[transcript_output, language_dropdown],
            outputs=[transcript_output, status_output]
        )
        
        # Download click
        download_btn.click(
            fn=download_transcript,
            inputs=[transcript_output, language_dropdown],
            outputs=gr.File(label="Download your transcript")
        )
    
    return demo

# ============================================================================
# MAIN
# ============================================================================
if __name__ == "__main__":
    if not os.getenv("GROQ_API_KEY"):
        print("⚠️ WARNING: GROQ_API_KEY not found!")
    demo = create_ui()
    demo.launch(server_name="0.0.0.0", server_port=7860)