Update app.py
Browse files
app.py
CHANGED
|
@@ -119,7 +119,12 @@ def generate_cmx_edl(edl_title, segments, source_name, fps=25):
|
|
| 119 |
edl_lines.append(f"{i:03} {reel_id} V C {src_in} {src_out} {rec_in} {rec_out}")
|
| 120 |
edl_lines.append(f"* FROM CLIP NAME: {source_name}")
|
| 121 |
edl_lines.append(f"* {seg.get('note', 'Clip')}\n")
|
| 122 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
return "\n".join(edl_lines)
|
| 124 |
|
| 125 |
def call_gemini_for_edl(transcript_data, story_prompt, api_key):
|
|
@@ -129,15 +134,19 @@ def call_gemini_for_edl(transcript_data, story_prompt, api_key):
|
|
| 129 |
|
| 130 |
url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key={api_key}"
|
| 131 |
|
|
|
|
| 132 |
system_prompt = (
|
| 133 |
"You are an expert Documentary Senior Editor. Use the provided transcript JSON "
|
| 134 |
"(which includes Speaker IDs and word-level timestamps) to create a condensed story. "
|
| 135 |
-
"Output ONLY a valid JSON array of segments with 'src_start', 'src_end', and '
|
| 136 |
"CRITICAL RULES:\n"
|
| 137 |
"1. IGNORE ALL INTERVIEWER COMMENTS: Do not include any speech or segments where the interviewer is speaking.\n"
|
| 138 |
"2. REMOVE FLUFF: Delete 'um', 'ah', repeats, and irrelevant filler.\n"
|
| 139 |
"3. NARRATIVE FLOW: Focus on the subject's high-energy responses and narrative hooks.\n"
|
| 140 |
-
"4. TIMESTAMP INTEGRITY: Use only the exact word-level start and end times from the data."
|
|
|
|
|
|
|
|
|
|
| 141 |
)
|
| 142 |
|
| 143 |
prompt_text = f"Creative Brief: {story_prompt}\n\nTranscript Data:\n{json.dumps(transcript_data)}"
|
|
@@ -219,17 +228,14 @@ if uploaded_file:
|
|
| 219 |
if not ACTIVE_HF_TOKEN or "PASTE_YOUR_HF_TOKEN" in ACTIVE_HF_TOKEN:
|
| 220 |
st.error("Please provide a valid Hugging Face Token in the Sidebar/Secrets.")
|
| 221 |
else:
|
| 222 |
-
# Create a dedicated container for the progress UI so it stays at the top
|
| 223 |
progress_container = st.container()
|
| 224 |
with progress_container:
|
| 225 |
st.info("🤖 **Junior Editor is processing your file...**")
|
| 226 |
-
|
| 227 |
-
# Create the visual elements
|
| 228 |
status_text = st.empty()
|
| 229 |
progress_bar = st.progress(0)
|
| 230 |
|
| 231 |
try:
|
| 232 |
-
# Phase 1: Save File
|
| 233 |
status_text.markdown("**Phase 1/4: Extracting Audio...**")
|
| 234 |
with open("temp_input", "wb") as f:
|
| 235 |
f.write(uploaded_file.getbuffer())
|
|
@@ -241,7 +247,6 @@ if uploaded_file:
|
|
| 241 |
])
|
| 242 |
progress_bar.progress(25)
|
| 243 |
|
| 244 |
-
# Setup Device
|
| 245 |
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 246 |
if device == "cpu":
|
| 247 |
st.warning("⚠️ No GPU detected. This will be slow.")
|
|
@@ -253,11 +258,8 @@ if uploaded_file:
|
|
| 253 |
model = whisperx.load_model(model_size, device, compute_type=compute_type)
|
| 254 |
audio = whisperx.load_audio("temp_audio.wav")
|
| 255 |
|
| 256 |
-
# This step takes the most time. It will sit at 25% until complete.
|
| 257 |
-
# This is safer than chunking manually which can cause transcription errors.
|
| 258 |
result = model.transcribe(audio, batch_size=16, language=target_language)
|
| 259 |
|
| 260 |
-
# Cleanup VRAM
|
| 261 |
del model
|
| 262 |
gc.collect()
|
| 263 |
torch.cuda.empty_cache()
|
|
|
|
| 119 |
edl_lines.append(f"{i:03} {reel_id} V C {src_in} {src_out} {rec_in} {rec_out}")
|
| 120 |
edl_lines.append(f"* FROM CLIP NAME: {source_name}")
|
| 121 |
edl_lines.append(f"* {seg.get('note', 'Clip')}\n")
|
| 122 |
+
|
| 123 |
+
# --- NEW LOGIC: Support Gaps ---
|
| 124 |
+
# Add the duration of this clip AND any requested gap to the record timeline
|
| 125 |
+
gap = seg.get('gap', 0.0)
|
| 126 |
+
rec_start += duration + gap
|
| 127 |
+
|
| 128 |
return "\n".join(edl_lines)
|
| 129 |
|
| 130 |
def call_gemini_for_edl(transcript_data, story_prompt, api_key):
|
|
|
|
| 134 |
|
| 135 |
url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key={api_key}"
|
| 136 |
|
| 137 |
+
# --- UPDATED SYSTEM PROMPT ---
|
| 138 |
system_prompt = (
|
| 139 |
"You are an expert Documentary Senior Editor. Use the provided transcript JSON "
|
| 140 |
"(which includes Speaker IDs and word-level timestamps) to create a condensed story. "
|
| 141 |
+
"Output ONLY a valid JSON array of segments with 'src_start', 'src_end', 'note', and optionally 'gap'. "
|
| 142 |
"CRITICAL RULES:\n"
|
| 143 |
"1. IGNORE ALL INTERVIEWER COMMENTS: Do not include any speech or segments where the interviewer is speaking.\n"
|
| 144 |
"2. REMOVE FLUFF: Delete 'um', 'ah', repeats, and irrelevant filler.\n"
|
| 145 |
"3. NARRATIVE FLOW: Focus on the subject's high-energy responses and narrative hooks.\n"
|
| 146 |
+
"4. TIMESTAMP INTEGRITY: Use only the exact word-level start and end times from the data.\n"
|
| 147 |
+
"5. PACING: Group related clips together so they flow seamlessly. HOWEVER, between distinct ideas or sections, "
|
| 148 |
+
"add a 'gap': 1.0 property (float, in seconds) to the segment preceding the break. "
|
| 149 |
+
"This will leave blank space on the timeline to signal a topic change."
|
| 150 |
)
|
| 151 |
|
| 152 |
prompt_text = f"Creative Brief: {story_prompt}\n\nTranscript Data:\n{json.dumps(transcript_data)}"
|
|
|
|
| 228 |
if not ACTIVE_HF_TOKEN or "PASTE_YOUR_HF_TOKEN" in ACTIVE_HF_TOKEN:
|
| 229 |
st.error("Please provide a valid Hugging Face Token in the Sidebar/Secrets.")
|
| 230 |
else:
|
|
|
|
| 231 |
progress_container = st.container()
|
| 232 |
with progress_container:
|
| 233 |
st.info("🤖 **Junior Editor is processing your file...**")
|
|
|
|
|
|
|
| 234 |
status_text = st.empty()
|
| 235 |
progress_bar = st.progress(0)
|
| 236 |
|
| 237 |
try:
|
| 238 |
+
# Phase 1: Save File
|
| 239 |
status_text.markdown("**Phase 1/4: Extracting Audio...**")
|
| 240 |
with open("temp_input", "wb") as f:
|
| 241 |
f.write(uploaded_file.getbuffer())
|
|
|
|
| 247 |
])
|
| 248 |
progress_bar.progress(25)
|
| 249 |
|
|
|
|
| 250 |
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 251 |
if device == "cpu":
|
| 252 |
st.warning("⚠️ No GPU detected. This will be slow.")
|
|
|
|
| 258 |
model = whisperx.load_model(model_size, device, compute_type=compute_type)
|
| 259 |
audio = whisperx.load_audio("temp_audio.wav")
|
| 260 |
|
|
|
|
|
|
|
| 261 |
result = model.transcribe(audio, batch_size=16, language=target_language)
|
| 262 |
|
|
|
|
| 263 |
del model
|
| 264 |
gc.collect()
|
| 265 |
torch.cuda.empty_cache()
|