devendergarg14 commited on
Commit
d4fa0c9
·
verified ·
1 Parent(s): 2edbb24

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +77 -27
app.py CHANGED
@@ -76,12 +76,11 @@ def save_frames(v, t, n):
76
 
77
 
78
  def clear_extracted_frames():
79
- """Clear the selected frame sequence and reset trimming tied to that sequence."""
80
  return (
81
  [],
82
  None,
83
- "✅ Extracted frames cleared. The final video will not have a frame intro.",
84
- 0,
85
  )
86
 
87
 
@@ -164,11 +163,9 @@ def make_video(s, video_in, audio_in, trim_frames=0, speed_factor=1.0):
164
  return None, "Initial frames to remove must be 0 or greater."
165
  if not 0.5 <= speed_factor <= 1.5:
166
  return None, "Playback speed must be between 0.5x and 1.5x."
167
- if trim_frames > 0 and not has_frames:
168
- return None, "Extract frames first to use initial-frame trimming."
169
-
170
- # Always use the currently selected input as the original-video segment. The extracted
171
- # frame state is used only to build the intro, so those two parts stay independent.
172
  _, _, source_fps, source_duration = meta(v_path)
173
  trim_seconds = trim_frames / source_fps
174
  if trim_frames and trim_seconds >= source_duration:
@@ -178,27 +175,80 @@ def make_video(s, video_in, audio_in, trim_frames=0, speed_factor=1.0):
178
  )
179
 
180
  if not has_frames:
181
- # With no intro frames, trim the source video if requested (the UI normally expects
182
- # a frame intro for trimming, but this branch remains safe if called directly).
183
- if trim_frames:
184
- return None, "Extract frames first to use initial-frame trimming."
185
  base_out = v_path
186
- msg = "✅ Final video created from the original video. No extracted-frame intro was added."
187
- if audio_path:
188
- base_out = os.path.join(tempfile.mkdtemp(), f"final_audio_{int(time.time() * 1000)}.mp4")
189
- replacement_duration = audio_meta(audio_path)
190
- loop_args = ["-stream_loop", "-1"] if replacement_duration > 0 and (source_duration - replacement_duration) > 1.0 else []
191
- cmd = ["ffmpeg", "-y", "-i", v_path] + loop_args + [
192
- "-i", audio_path,
193
- "-map", "0:v:0", "-map", "1:a:0",
194
- "-t", str(source_duration),
195
- "-c:v", "copy", "-c:a", "aac", "-b:a", "192k",
196
- "-movflags", "+faststart", base_out,
197
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
  result = subprocess.run(cmd, capture_output=True, text=True)
199
  if result.returncode:
200
- return None, result.stderr or "Could not replace the audio."
201
- msg = "✅ Final video created using the original video and replacement audio."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  final_out = apply_speed_to_video(base_out, speed_factor)
203
  return final_out, add_speed_message(msg, speed_factor)
204
 
@@ -345,7 +395,7 @@ with gr.Blocks(title="Video Frame Editor") as demo:
345
  clear_btn.click(
346
  fn=clear_extracted_frames,
347
  inputs=[],
348
- outputs=[gallery, state, status, trim_count],
349
  )
350
  copy_btn.click(
351
  fn=make_video,
 
76
 
77
 
78
  def clear_extracted_frames():
79
+ """Clear only the extracted-frame sequence; keep the independent trim setting unchanged."""
80
  return (
81
  [],
82
  None,
83
+ "✅ Extracted frames cleared. The final video will not have a frame intro. Initial-frame trimming is unchanged.",
 
84
  )
85
 
86
 
 
163
  return None, "Initial frames to remove must be 0 or greater."
164
  if not 0.5 <= speed_factor <= 1.5:
165
  return None, "Playback speed must be between 0.5x and 1.5x."
166
+ # Always use the currently selected input as the original-video segment. Extracted-frame
167
+ # state is used only to build the optional intro. Source-video trimming works independently,
168
+ # whether or not any frames have been extracted.
 
 
169
  _, _, source_fps, source_duration = meta(v_path)
170
  trim_seconds = trim_frames / source_fps
171
  if trim_frames and trim_seconds >= source_duration:
 
175
  )
176
 
177
  if not has_frames:
178
+ # No intro frames: create an output from the original video alone, applying any
179
+ # requested frame trim and/or replacement audio. This is intentionally independent
180
+ # of the extraction controls.
 
181
  base_out = v_path
182
+ body_duration = max(0.0, source_duration - trim_seconds)
183
+ has_source_audio = stream_has_audio(v_path)
184
+ needs_processing = bool(trim_frames or audio_path)
185
+
186
+ if needs_processing:
187
+ base_out = os.path.join(
188
+ tempfile.mkdtemp(), f"source_only_{int(time.time() * 1000)}.mp4"
189
+ )
190
+ video_filter = (
191
+ f"[0:v:0]trim=start_frame={trim_frames},setpts=PTS-STARTPTS[vout]"
192
+ )
193
+ cmd = ["ffmpeg", "-y", "-i", v_path]
194
+
195
+ if audio_path:
196
+ replacement_duration = audio_meta(audio_path)
197
+ loop_args = (
198
+ ["-stream_loop", "-1"]
199
+ if replacement_duration > 0 and (body_duration - replacement_duration) > 1.0
200
+ else []
201
+ )
202
+ cmd += loop_args + ["-i", audio_path]
203
+ cmd += [
204
+ "-filter_complex", video_filter,
205
+ "-map", "[vout]", "-map", "1:a:0",
206
+ "-t", str(body_duration),
207
+ "-c:v", "libx264", "-crf", "18", "-preset", "medium",
208
+ "-pix_fmt", "yuv420p", "-c:a", "aac", "-b:a", "192k",
209
+ "-movflags", "+faststart", base_out,
210
+ ]
211
+ elif has_source_audio:
212
+ audio_filter = (
213
+ f"{video_filter};[0:a:0]atrim=start={trim_seconds:.8f},"
214
+ "asetpts=PTS-STARTPTS[aout]"
215
+ )
216
+ cmd += [
217
+ "-filter_complex", audio_filter,
218
+ "-map", "[vout]", "-map", "[aout]",
219
+ "-t", str(body_duration),
220
+ "-c:v", "libx264", "-crf", "18", "-preset", "medium",
221
+ "-pix_fmt", "yuv420p", "-c:a", "aac", "-b:a", "192k",
222
+ "-movflags", "+faststart", base_out,
223
+ ]
224
+ else:
225
+ cmd += [
226
+ "-filter_complex", video_filter,
227
+ "-map", "[vout]", "-an",
228
+ "-t", str(body_duration),
229
+ "-c:v", "libx264", "-crf", "18", "-preset", "medium",
230
+ "-pix_fmt", "yuv420p", "-movflags", "+faststart", base_out,
231
+ ]
232
+
233
  result = subprocess.run(cmd, capture_output=True, text=True)
234
  if result.returncode:
235
+ return None, result.stderr or "Could not process the original video."
236
+
237
+ if trim_frames:
238
+ msg = (
239
+ "✅ Final video created from the original video with no extracted-frame intro. "
240
+ f"Skipped the first {trim_frames} frame(s) ({trim_seconds:.3f}s at "
241
+ f"{source_fps:.3f} FPS)."
242
+ )
243
+ else:
244
+ msg = "✅ Final video created from the original video. No extracted-frame intro was added."
245
+ if audio_path:
246
+ msg = "✅ Final video created from the original video with replacement audio and no extracted-frame intro."
247
+ elif has_source_audio and trim_frames:
248
+ msg += " Original audio was trimmed to stay aligned with the video."
249
+ elif not has_source_audio:
250
+ msg += " The source video has no audio track."
251
+
252
  final_out = apply_speed_to_video(base_out, speed_factor)
253
  return final_out, add_speed_message(msg, speed_factor)
254
 
 
395
  clear_btn.click(
396
  fn=clear_extracted_frames,
397
  inputs=[],
398
+ outputs=[gallery, state, status],
399
  )
400
  copy_btn.click(
401
  fn=make_video,