NickVerri commited on
Commit
f59e7a0
·
verified ·
1 Parent(s): 5ff72a8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +6 -116
app.py CHANGED
@@ -18,6 +18,10 @@ HARDCODED_GEMINI_KEY = ""
18
  ENV_HF_TOKEN = os.environ.get("HF_TOKEN", "")
19
  ENV_GEMINI_KEY = os.environ.get("GEMINI_API_KEY", "")
20
 
 
 
 
 
21
  def format_timecode(seconds, fps=25):
22
  """Converts seconds to HH:MM:SS:FF for Resolve/Premiere."""
23
  td = timedelta(seconds=seconds)
@@ -48,7 +52,7 @@ def generate_cmx_edl(edl_title, segments, source_name, fps=25):
48
  def call_gemini_for_edl(transcript_data, story_prompt, api_key):
49
  """Sends diarized, word-level transcript to Gemini Senior Editor."""
50
  if not api_key:
51
- st.error("Gemini API Key is missing.")
52
  return None
53
 
54
  url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key={api_key}"
@@ -66,118 +70,4 @@ def call_gemini_for_edl(transcript_data, story_prompt, api_key):
66
  )
67
 
68
  payload = {
69
- "contents": [{"parts": [{"text": f"Creative Brief: {story_prompt}\n\nTranscript Data:\n{json.dumps(transcript_data)}"}]}],
70
- "systemInstruction": {"parts": [{"text": system_prompt}]},
71
- "generationConfig": {"responseMimeType": "application/json"}
72
- }
73
-
74
- try:
75
- res = requests.post(url, json=payload)
76
- res.raise_for_status()
77
- return json.loads(res.json()['candidates'][0]['content']['parts'][0]['text'])
78
- except Exception as e:
79
- st.error(f"Senior Editor AI Error: {e}")
80
- return None
81
-
82
- # --- Streamlit UI ---
83
- st.set_page_config(page_title="DocAI Editor", layout="wide")
84
- st.title("Documentary AI: Pipeline")
85
-
86
- with st.sidebar:
87
- st.header("Settings")
88
-
89
- # Determine default values for inputs
90
- def_gemini = ENV_GEMINI_KEY if ENV_GEMINI_KEY else HARDCODED_GEMINI_KEY
91
- def_hf = ENV_HF_TOKEN if ENV_HF_TOKEN else HARDCODED_HF_TOKEN
92
-
93
- input_gemini_key = st.text_input("Gemini API Key", type="password", value=def_gemini)
94
- input_hf_token = st.text_input("HF Token (Diarization)", type="password", value=def_hf)
95
-
96
- active_api_key = input_gemini_key
97
- active_hf_token = input_hf_token
98
- fps = st.number_input("Timeline FPS", value=25)
99
-
100
- uploaded_file = st.file_uploader("Upload Video/Audio Clip", type=["mp4", "m4a", "wav", "mp3", "mov"])
101
-
102
- if uploaded_file:
103
- # --- Step 1: Technical Processing ---
104
- if "transcript" not in st.session_state:
105
- if st.button("Step 1: Transcribe & Diarize"):
106
- if not active_hf_token or "PASTE_YOUR_HF_TOKEN" in active_hf_token:
107
- st.error("Please provide a valid Hugging Face Token in the sidebar or hardcode it in app.py.")
108
- else:
109
- with st.spinner("Processing... Extracting audio, identifying speakers, and transcribing:"):
110
- # Save local temp file
111
- with open("temp_input", "wb") as f:
112
- f.write(uploaded_file.getbuffer())
113
-
114
- # Optimized Audio: m4a, 64kbps, 16kHz, mono
115
- subprocess.run([
116
- "ffmpeg", "-i", "temp_input",
117
- "-vn", "-acodec", "aac", "-ab", "64k", "-ar", "16000", "-ac", "1",
118
- "audio_optimized.m4a", "-y"
119
- ])
120
-
121
- # 1. Diarization
122
- try:
123
- # Fix for pyannote.audio 3.0+: 'use_auth_token' is now just 'token'
124
- try:
125
- pipeline = Pipeline.from_pretrained(
126
- "pyannote/speaker-diarization@2.1",
127
- token=active_hf_token
128
- )
129
- except TypeError:
130
- # Fallback for older versions of pyannote.audio
131
- pipeline = Pipeline.from_pretrained(
132
- "pyannote/speaker-diarization@2.1",
133
- use_auth_token=active_hf_token
134
- )
135
-
136
- if torch.cuda.is_available():
137
- pipeline.to(torch.device("cuda"))
138
-
139
- diarization = pipeline("audio_optimized.m4a")
140
- except Exception as e:
141
- st.error(f"Diarization Error: {e}")
142
- st.stop()
143
-
144
- # 2. Whisper Transcription
145
- model = whisper.load_model("base")
146
- result = model.transcribe("audio_optimized.m4a", word_timestamps=True)
147
-
148
- # 3. Alignment
149
- final_segments = []
150
- for segment in result['segments']:
151
- mid_time = (segment['start'] + segment['end']) / 2
152
- speaker = "Unknown"
153
- for turn, _, speaker_id in diarization.itertracks(yield_label=True):
154
- if turn.start <= mid_time <= turn.end:
155
- speaker = speaker_id
156
- break
157
-
158
- final_segments.append({
159
- "speaker": speaker,
160
- "text": segment['text'],
161
- "start": segment['start'],
162
- "end": segment['end'],
163
- "words": segment.get('words', [])
164
- })
165
-
166
- st.session_state.transcript = final_segments
167
- st.success("Transcription and Diarization Complete.")
168
-
169
- if "transcript" in st.session_state:
170
- st.divider()
171
- brief = st.text_area("Creative Brief", placeholder="e.g. Focus on the yeast story, remove the interviewer.")
172
-
173
- if st.button("Step 2: Create EDL"):
174
- if not active_api_key:
175
- st.error("Gemini API Key required.")
176
- else:
177
- with st.spinner("Analyzing..."):
178
- edl_segments = call_gemini_for_edl(st.session_state.transcript, brief, active_api_key)
179
- if edl_segments:
180
- final_edl = generate_cmx_edl("AI_Senior_Editor_Cut", edl_segments, uploaded_file.name, fps)
181
- st.subheader("EDL Preview")
182
- st.code(final_edl, language="text")
183
- st.download_button("Download EDL", data=final_edl, file_name="edit.edl")
 
18
  ENV_HF_TOKEN = os.environ.get("HF_TOKEN", "")
19
  ENV_GEMINI_KEY = os.environ.get("GEMINI_API_KEY", "")
20
 
21
+ # Determine active keys (Prioritize Secrets > Hardcoded)
22
+ ACTIVE_HF_TOKEN = ENV_HF_TOKEN if ENV_HF_TOKEN else HARDCODED_HF_TOKEN
23
+ ACTIVE_GEMINI_KEY = ENV_GEMINI_KEY if ENV_GEMINI_KEY else HARDCODED_GEMINI_KEY
24
+
25
  def format_timecode(seconds, fps=25):
26
  """Converts seconds to HH:MM:SS:FF for Resolve/Premiere."""
27
  td = timedelta(seconds=seconds)
 
52
  def call_gemini_for_edl(transcript_data, story_prompt, api_key):
53
  """Sends diarized, word-level transcript to Gemini Senior Editor."""
54
  if not api_key:
55
+ st.error("Gemini API Key is missing. Set it in Space Secrets or app.py.")
56
  return None
57
 
58
  url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key={api_key}"
 
70
  )
71
 
72
  payload = {
73
+ "contents": [{"parts": [{"text": f"Creative Brief: {story_prompt}\n\nTranscript Data:\n{json