mp3-splitter / app.py
avadhuta's picture
Update app.py
097fdcb verified
Raw
History Blame Contribute Delete
5.56 kB
import gradio as gr
from pydub import AudioSegment
from google import genai
import tempfile
import os
import time
import re
import concurrent.futures
API_TIMEOUT_SEC = 300
PROMPT = """
Transcribe the following audio exactly as spoken.
Format the transcript with clear paragraphs.
When the speaker moves to a new topic or subject, insert a subheading in markdown (### Topic) that summarizes the upcoming content.
For verses, stanzas, or ślokas, break them into separate lines as they would appear in a written poem (one line per quarter or line as appropriate).
Maintain the original language rules:
- Sanskrit proper names: IAST without italics.
- Other Sanskrit words and phrases: IAST with *italics* and English translation in parentheses, e.g., *śāntiḥ* (*peace*).
- English: exactly as spoken.
- Serbian/Croatian: word order as given (e.g., 'Svāmī je klimnuo glavom' not 'klimnuo je').
Do not add timestamps. Output only the formatted transcript.
"""
def call_gemini(seg_path, api_key):
"""Poziva Gemini API (izvršava se u pozadinskoj niti)."""
client = genai.Client(api_key=api_key)
myfile = client.files.upload(file=seg_path)
response = client.models.generate_content(
model="gemini-2.5-flash",
contents=[myfile, PROMPT]
)
text = response.text
client.files.delete(name=myfile.name)
return text
def transcribe_segment(audio_path, api_key, start_min, end_min, progress=gr.Progress()):
# Provera ulaza
if audio_path is None:
yield "❌ Niste otpremili MP3 fajl!"
return
if not api_key.strip():
yield "❌ Unesite Gemini API ključ."
return
if start_min is None or end_min is None:
yield "❌ Unesite početak i kraj u minutima."
return
if start_min >= end_min:
yield "❌ Početak mora biti manji od kraja."
return
progress(0.0, desc="Učitavanje audio fajla...")
yield "⏳ Učitavanje audio fajla..."
try:
sound = AudioSegment.from_file(audio_path)
except Exception as e:
yield f"❌ Greška pri učitavanju fajla: {e}"
return
total_sec = sound.duration_seconds
start_ms = int(start_min * 60 * 1000)
end_ms = int(end_min * 60 * 1000)
if end_ms > len(sound):
yield f"❌ Kraj ({end_min} min) premašuje trajanje snimka ({total_sec/60:.1f} min)."
return
if start_ms < 0:
start_ms = 0
segment = sound[start_ms:end_ms]
segment_dur = len(segment) / 1000
progress(0.1, desc=f"Isečeno {start_min}-{end_min} min ({segment_dur/60:.1f} min)")
yield f"✂️ Isečen segment {start_min}{end_min} min ({segment_dur/60:.1f} min)"
progress(0.2, desc="Priprema audio segmenta...")
yield "📦 Priprema audio segmenta..."
tmp = tempfile.NamedTemporaryFile(suffix=".mp3", delete=False)
segment.export(tmp.name, format="mp3")
seg_path = tmp.name
api_key_clean = api_key.strip()
last_err = ""
for attempt in range(1, 10):
progress(0.25 + attempt * 0.07, desc=f"Transkripcija (pokušaj {attempt})...")
yield f"🎤 Transkripcija u toku (pokušaj {attempt})..."
print(f"API poziv za {start_min}-{end_min} min, pokušaj {attempt}")
try:
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
future = executor.submit(call_gemini, seg_path, api_key_clean)
text = future.result(timeout=API_TIMEOUT_SEC)
progress(1.0, desc="Gotovo!")
os.unlink(seg_path)
yield f"### {start_min}{end_min} min\n{text}"
return
except concurrent.futures.TimeoutError:
last_err = "API poziv je prekoračio vremensko ograničenje (5 min)."
print(" Timeout!")
break
except Exception as e:
last_err = str(e)
print(f" Greška: {last_err}")
if "503" in last_err or "UNAVAILABLE" in last_err:
wait = 10 * attempt
print(f" 503, čekam {wait}s...")
yield f"⏳ Server nedostupan (503), čekam {wait}s..."
time.sleep(wait)
elif "429" in last_err and "RESOURCE_EXHAUSTED" in last_err:
match = re.search(r"retryDelay':\s*'?(\d+)s?'?", last_err)
wait = int(match.group(1)) + 2 if match else 60
print(f" 429, čekam {wait}s...")
yield f"⏳ Previše zahteva (429), čekam {wait}s..."
time.sleep(wait)
else:
break
# Sve probe neuspešne
try:
os.unlink(seg_path)
except:
pass
yield f"### {start_min}{end_min} min\n*Greška: {last_err}*"
with gr.Blocks() as demo:
gr.Markdown("# 🎙️ Transkripcija po opsezima (progres uživo + formatiranje)")
with gr.Row():
audio_in = gr.Audio(type="filepath", label="Otpremite MP3 fajl")
api_key = gr.Textbox(label="Gemini API ključ", type="password")
with gr.Row():
start_min = gr.Number(label="Početak (minuti)", value=0, precision=1)
end_min = gr.Number(label="Kraj (minuti)", value=20, precision=1)
gr.Markdown("*Otpremite ceo MP3, pa unosite opsege jedan po jedan (npr. 0–20, 20–40…).*")
btn = gr.Button("Pokreni transkripciju ovog opsega")
output = gr.Markdown(label="Transkript")
btn.click(
fn=transcribe_segment,
inputs=[audio_in, api_key, start_min, end_min],
outputs=output
)
if __name__ == "__main__":
demo.launch()