NickVerri commited on
Commit
286e35f
·
verified ·
1 Parent(s): 4a14a2f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +72 -13
app.py CHANGED
@@ -149,7 +149,7 @@ def generate_cmx_edl(edl_title, segments, clip_metadata, fps=25):
149
  src_out_frames = seconds_to_frames(src_end_val, fps) + start_offset_frames
150
  duration_frames = src_out_frames - src_in_frames
151
 
152
- current_source = source_clip.replace(" ", "_")[:8] # Short EDL reel ID
153
  note_text = seg.get('note', 'Clip')
154
  source_name = source_clip
155
 
@@ -473,7 +473,7 @@ def call_gemini_for_edl(transcripts_dict, story_prompt, api_key, previous_edit=N
473
  st.set_page_config(page_title="Junior Editor Pro", layout="wide")
474
  st.title("Junior Editor (Multi-Clip Edition)")
475
 
476
- with st.expander("Instructions - Please Read First", expanded=False):
477
  st.markdown("""
478
  * This program will generate an EDL or XML containing multiple interview cut down and interwoven as directed by you.
479
  * Set your export timeline FPS and EDL or XML. If you are doing sequences it will have to be XML, but this will only work in Resolve (I haven’t tried FCP). If you are exporting sequences have the TC start at 00:00:00:00 for speed later.
@@ -483,8 +483,30 @@ with st.expander("Instructions - Please Read First", expanded=False):
483
  * Junior Editor will transcribe and separate speakers. This takes approx 10 mins per hour of audio. Once completed.
484
  * You can then instruct it to find engaging bits or construct a narrative. You can ask it to add in suggested VO or Graphics cards if you are planning to use voice over.
485
  * It will create an EDL or XML to import back into Resolve.
486
- * If you don’t like the first attempt, give it new instructions and ask it to generate a new sequence.
487
  * Resolve Users: Uncheck "Automatically import source clips into media pool" during import.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
488
  """)
489
 
490
  st.divider()
@@ -507,6 +529,8 @@ if "transcripts" not in st.session_state:
507
  st.session_state.transcripts = {}
508
  if "clip_metadata" not in st.session_state:
509
  st.session_state.clip_metadata = {}
 
 
510
 
511
  uploaded_files = st.file_uploader("Upload Clip(s)", type=["mp4", "m4a", "wav", "mp3", "mov"], accept_multiple_files=True)
512
 
@@ -572,7 +596,7 @@ if uploaded_files and len(st.session_state.transcripts) == 0:
572
  gc.collect()
573
  torch.cuda.empty_cache()
574
 
575
- diarize_segments = diarize_model(audio) # Removed min/max speaker restrictions
576
  final_result = whisperx.assign_word_speakers(diarize_segments, result)
577
 
578
  processed_segments = []
@@ -608,7 +632,7 @@ if uploaded_files and len(st.session_state.transcripts) == 0:
608
  st.rerun()
609
 
610
  # -- Chat / Generation Area --
611
- if len(st.session_state.transcripts) > 0:
612
  st.divider()
613
 
614
  with st.expander("View Source Transcripts", expanded=False):
@@ -620,7 +644,7 @@ if len(st.session_state.transcripts) > 0:
620
  st.divider()
621
 
622
  st.subheader("Your Instruction")
623
- brief = st.text_area("What should the Junior Editor do?", placeholder="e.g. Build a 1-minute intro cutting between Cam_A and Cam_B.")
624
 
625
  if st.button("Generate Multi-Clip Edit", type="primary"):
626
  with st.spinner("Analyzing all transcripts and building sequence..."):
@@ -630,13 +654,13 @@ if len(st.session_state.transcripts) > 0:
630
  safe_title = "".join([c for c in sequence_title if c.isalnum() or c in (' ', '_', '-')]).strip().replace(' ', '_')
631
 
632
  if "XML" in export_format:
633
- final_output = generate_xml(safe_title, edl_segments, st.session_state.clip_metadata, fps)
634
  ext = "xml"
635
  else:
636
- final_output = generate_cmx_edl(safe_title, edl_segments, st.session_state.clip_metadata, fps)
637
  ext = "edl"
638
 
639
- transcript_txt = generate_transcript_txt(safe_title, edl_segments, st.session_state.clip_metadata, fps, st.session_state.transcripts)
640
 
641
  st.session_state.edit_generated = True
642
  st.session_state.revision_count = 1
@@ -646,7 +670,9 @@ if len(st.session_state.transcripts) > 0:
646
  st.session_state.transcript_txt = transcript_txt
647
  st.session_state.used_model = used_model
648
  st.session_state.edl_segments = edl_segments
 
649
 
 
650
  if st.session_state.get("edit_generated"):
651
  st.success(f"✅ Multi-clip sequence generated successfully using **{st.session_state.used_model}**.")
652
 
@@ -656,7 +682,40 @@ if st.session_state.get("edit_generated"):
656
  with col2:
657
  st.download_button(f"Download Transcript (.TXT)", data=st.session_state.transcript_txt, file_name=f"{st.session_state.safe_title}_V{st.session_state.revision_count}_Transcript.txt")
658
 
659
- if st.button("Start Over / Clear Bin"):
660
- for key in ["transcripts", "clip_metadata", "edit_generated"]:
661
- if key in st.session_state: del st.session_state[key]
662
- st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  src_out_frames = seconds_to_frames(src_end_val, fps) + start_offset_frames
150
  duration_frames = src_out_frames - src_in_frames
151
 
152
+ current_source = source_clip.replace(" ", "_")[:8]
153
  note_text = seg.get('note', 'Clip')
154
  source_name = source_clip
155
 
 
473
  st.set_page_config(page_title="Junior Editor Pro", layout="wide")
474
  st.title("Junior Editor (Multi-Clip Edition)")
475
 
476
+ with st.expander("Instructions & Prompt Guide - Please Read First", expanded=False):
477
  st.markdown("""
478
  * This program will generate an EDL or XML containing multiple interview cut down and interwoven as directed by you.
479
  * Set your export timeline FPS and EDL or XML. If you are doing sequences it will have to be XML, but this will only work in Resolve (I haven’t tried FCP). If you are exporting sequences have the TC start at 00:00:00:00 for speed later.
 
483
  * Junior Editor will transcribe and separate speakers. This takes approx 10 mins per hour of audio. Once completed.
484
  * You can then instruct it to find engaging bits or construct a narrative. You can ask it to add in suggested VO or Graphics cards if you are planning to use voice over.
485
  * It will create an EDL or XML to import back into Resolve.
 
486
  * Resolve Users: Uncheck "Automatically import source clips into media pool" during import.
487
+
488
+ ---
489
+ ### 💡 Pro-Tips for Prompting the AI
490
+ **1. Think in Clips, Not Minutes:** The AI cannot accurately calculate running time. If you want a video longer than 3-5 minutes, **do not ask for a time limit**. Instead, ask for a specific number of clips (assume roughly 8-10 clips per minute).
491
+
492
+ **2. Use Bullet Points:** The AI follows technical, structured lists much better than paragraphs.
493
+
494
+ **3. Do the Math for It:** If you want specific ratios, give the AI hard numbers (e.g., "Use 30 clips from Cam A and 10 from Cam B") instead of percentages (e.g., "75% Cam A").
495
+
496
+ **Example of a Great Prompt:**
497
+ > **TASK:** Build a comprehensive highlights reel from the provided event transcripts.
498
+ >
499
+ > **LENGTH CONSTRAINT:** You MUST generate a minimum of 75 to 80 individual clip segments. Do not stop early.
500
+ >
501
+ > **SOURCE ALLOCATION:**
502
+ > * **~32 clips** from `BeutlerClips2` (Use heavily as the inspiring backbone).
503
+ > * **~8 clips** from `Bruce Interview`.
504
+ > * **~40 clips** mixed from the other attendee interviews.
505
+ >
506
+ > **NARRATIVE & PACING RULES:**
507
+ > * **No VO/Graphics.** > * **The Hook:** Start with an intriguing hook, followed by a clip explaining the event.
508
+ > * **The Chain:** Each clip must organically lead into the next. Do not repeat the same info.
509
+ > * **The Ending:** Finish with a highly uplifting statement.
510
  """)
511
 
512
  st.divider()
 
529
  st.session_state.transcripts = {}
530
  if "clip_metadata" not in st.session_state:
531
  st.session_state.clip_metadata = {}
532
+ if "revision_count" not in st.session_state:
533
+ st.session_state.revision_count = 1
534
 
535
  uploaded_files = st.file_uploader("Upload Clip(s)", type=["mp4", "m4a", "wav", "mp3", "mov"], accept_multiple_files=True)
536
 
 
596
  gc.collect()
597
  torch.cuda.empty_cache()
598
 
599
+ diarize_segments = diarize_model(audio)
600
  final_result = whisperx.assign_word_speakers(diarize_segments, result)
601
 
602
  processed_segments = []
 
632
  st.rerun()
633
 
634
  # -- Chat / Generation Area --
635
+ if len(st.session_state.transcripts) > 0 and not st.session_state.get("edit_generated"):
636
  st.divider()
637
 
638
  with st.expander("View Source Transcripts", expanded=False):
 
644
  st.divider()
645
 
646
  st.subheader("Your Instruction")
647
+ brief = st.text_area("What should the Junior Editor do?", placeholder="e.g. Build a sequence using 40 clips, starting with an intriguing hook...")
648
 
649
  if st.button("Generate Multi-Clip Edit", type="primary"):
650
  with st.spinner("Analyzing all transcripts and building sequence..."):
 
654
  safe_title = "".join([c for c in sequence_title if c.isalnum() or c in (' ', '_', '-')]).strip().replace(' ', '_')
655
 
656
  if "XML" in export_format:
657
+ final_output = generate_xml(f"{safe_title}_V1", edl_segments, st.session_state.clip_metadata, fps)
658
  ext = "xml"
659
  else:
660
+ final_output = generate_cmx_edl(f"{safe_title}_V1", edl_segments, st.session_state.clip_metadata, fps)
661
  ext = "edl"
662
 
663
+ transcript_txt = generate_transcript_txt(f"{safe_title}_V1", edl_segments, st.session_state.clip_metadata, fps, st.session_state.transcripts)
664
 
665
  st.session_state.edit_generated = True
666
  st.session_state.revision_count = 1
 
670
  st.session_state.transcript_txt = transcript_txt
671
  st.session_state.used_model = used_model
672
  st.session_state.edl_segments = edl_segments
673
+ st.rerun()
674
 
675
+ # -- Post-Generation / Revision Area --
676
  if st.session_state.get("edit_generated"):
677
  st.success(f"✅ Multi-clip sequence generated successfully using **{st.session_state.used_model}**.")
678
 
 
682
  with col2:
683
  st.download_button(f"Download Transcript (.TXT)", data=st.session_state.transcript_txt, file_name=f"{st.session_state.safe_title}_V{st.session_state.revision_count}_Transcript.txt")
684
 
685
+ st.divider()
686
+
687
+ st.subheader("Revise This Edit")
688
+ revision_brief = st.text_area("Want changes? Give Junior Editor new instructions based on the current cut:", placeholder="e.g. Make it longer (add 20 more clips), swap the opening clip, or focus more on AstraZeneca leaders.")
689
+
690
+ col3, col4 = st.columns([1, 4])
691
+ with col3:
692
+ if st.button("Apply Revisions", type="primary"):
693
+ with st.spinner("Junior Editor is revising the sequence..."):
694
+ new_edl_segments, used_model, used_endpoint = call_gemini_for_edl(
695
+ st.session_state.transcripts,
696
+ revision_brief,
697
+ ACTIVE_GEMINI_KEY,
698
+ previous_edit=st.session_state.edl_segments
699
+ )
700
+
701
+ if new_edl_segments:
702
+ st.session_state.revision_count += 1
703
+ safe_title = st.session_state.safe_title
704
+
705
+ if st.session_state.ext == "xml":
706
+ final_output = generate_xml(f"{safe_title}_V{st.session_state.revision_count}", new_edl_segments, st.session_state.clip_metadata, fps)
707
+ else:
708
+ final_output = generate_cmx_edl(f"{safe_title}_V{st.session_state.revision_count}", new_edl_segments, st.session_state.clip_metadata, fps)
709
+
710
+ transcript_txt = generate_transcript_txt(f"{safe_title}_V{st.session_state.revision_count}", new_edl_segments, st.session_state.clip_metadata, fps, st.session_state.transcripts)
711
+
712
+ st.session_state.final_output = final_output
713
+ st.session_state.transcript_txt = transcript_txt
714
+ st.session_state.used_model = used_model
715
+ st.session_state.edl_segments = new_edl_segments
716
+ st.rerun()
717
+ with col4:
718
+ if st.button("Start Over / Clear Bin"):
719
+ for key in ["transcripts", "clip_metadata", "edit_generated", "revision_count"]:
720
+ if key in st.session_state: del st.session_state[key]
721
+ st.rerun()