sam12345324 commited on
Commit
c64b325
·
verified ·
1 Parent(s): 46bdb10

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +42 -14
main.py CHANGED
@@ -10,11 +10,12 @@ from moviepy.editor import (
10
  concatenate_videoclips,
11
  AudioFileClip,
12
  CompositeAudioClip,
 
13
  )
14
  import io
15
 
16
  # Configure logging
17
- logging.basicConfig(level=logging.DEBUG) # Set to DEBUG for detailed output
18
  logger = logging.getLogger("main")
19
 
20
  # Enable MoviePy verbose logging
@@ -28,6 +29,7 @@ def safe_video_clip(path):
28
  if clip.reader is None or clip.duration is None or clip.duration <= 0:
29
  clip.close()
30
  raise ValueError(f"Invalid or empty video file: {os.path.basename(path)}")
 
31
  return clip
32
  except Exception as e:
33
  raise ValueError(f"Failed to load video file '{os.path.basename(path)}': {str(e)}")
@@ -39,11 +41,12 @@ def safe_audio_clip(path):
39
  if clip.reader is None or clip.duration is None or clip.duration <= 0:
40
  clip.close()
41
  raise ValueError(f"Invalid or empty audio file: {os.path.basename(path)}")
 
42
  return clip
43
  except Exception as e:
44
  raise ValueError(f"Failed to load audio file '{os.path.basename(path)}': {str(e)}")
45
 
46
- def merge_videos_and_audios(video_paths, audio_paths, output_path, temp_dir, orig_vol=1.0, music_vol=0.5):
47
  logger.debug(f"Merging {len(video_paths)} videos and {len(audio_paths)} audios to {output_path}")
48
 
49
  # Load video clips
@@ -51,6 +54,8 @@ def merge_videos_and_audios(video_paths, audio_paths, output_path, temp_dir, ori
51
  for vp in video_paths:
52
  try:
53
  clip = safe_video_clip(vp)
 
 
54
  video_clips.append(clip)
55
  except Exception as e:
56
  logger.warning(f"Skipping invalid video file {vp}: {e}")
@@ -70,15 +75,36 @@ def merge_videos_and_audios(video_paths, audio_paths, output_path, temp_dir, ori
70
 
71
  # Concatenate video clips
72
  final_video = concatenate_videoclips(video_clips, method="compose")
 
73
 
74
- # Combine audio clips if available
 
 
 
 
 
 
 
75
  if audio_clips:
76
- final_audio = CompositeAudioClip(audio_clips)
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  final_video = final_video.set_audio(final_audio)
 
 
78
  else:
79
- # Optionally adjust original video audio volume
80
- if final_video.audio:
81
- final_video.audio = final_video.audio.volumex(orig_vol)
82
 
83
  # Write output
84
  logger.debug(f"Writing output to {output_path}")
@@ -102,6 +128,8 @@ def merge_videos_and_audios(video_paths, audio_paths, output_path, temp_dir, ori
102
  vc.close()
103
  for ac in audio_clips:
104
  ac.close()
 
 
105
 
106
  app = FastAPI()
107
 
@@ -110,6 +138,7 @@ async def merge_endpoint(
110
  files: list[UploadFile] = File(...),
111
  orig_vol: float = Form(1.0),
112
  music_vol: float = Form(0.5),
 
113
  background_tasks: BackgroundTasks = BackgroundTasks(),
114
  ):
115
  temp_dir = tempfile.mkdtemp()
@@ -141,14 +170,14 @@ async def merge_endpoint(
141
  logger.debug(f"Video files: {video_files}")
142
  logger.debug(f"Audio files: {audio_files}")
143
 
144
- if len(video_files) < 1 and len(audio_files) < 1:
145
- raise HTTPException(status_code=400, detail="Please upload at least one valid video or audio file")
146
 
147
  # Prepare output path
148
  output_path = os.path.join(temp_dir, "merged_output.mp4")
149
 
150
  # Merge videos and audios
151
- merge_videos_and_audios(video_files, audio_files, output_path, temp_dir, orig_vol, music_vol)
152
 
153
  # Verify the output file exists
154
  if not os.path.exists(output_path):
@@ -163,10 +192,10 @@ async def merge_endpoint(
163
  logger.error(f"Output file is empty: {output_path}")
164
  raise HTTPException(status_code=500, detail="Output file is empty")
165
 
166
- # Schedule cleanup as a background task
167
  background_tasks.add_task(shutil.rmtree, temp_dir, ignore_errors=True)
168
 
169
- # Return the file as a streaming response
170
  logger.debug("Sending streaming response")
171
  return StreamingResponse(
172
  io.BytesIO(content),
@@ -180,11 +209,10 @@ async def merge_endpoint(
180
  raise HTTPException(status_code=500, detail=error_msg)
181
 
182
  finally:
183
- # Fallback cleanup in case background task fails
184
  logger.debug(f"Cleaning up temporary directory: {temp_dir}")
185
  shutil.rmtree(temp_dir, ignore_errors=True)
186
 
187
- # Optional: log public URL
188
  def log_api_url():
189
  url = os.getenv("SPACE_PUBLIC_URL")
190
  if url:
 
10
  concatenate_videoclips,
11
  AudioFileClip,
12
  CompositeAudioClip,
13
+ concatenate_audioclips,
14
  )
15
  import io
16
 
17
  # Configure logging
18
+ logging.basicConfig(level=logging.DEBUG)
19
  logger = logging.getLogger("main")
20
 
21
  # Enable MoviePy verbose logging
 
29
  if clip.reader is None or clip.duration is None or clip.duration <= 0:
30
  clip.close()
31
  raise ValueError(f"Invalid or empty video file: {os.path.basename(path)}")
32
+ logger.debug(f"Loaded video: {path}, duration: {clip.duration}s")
33
  return clip
34
  except Exception as e:
35
  raise ValueError(f"Failed to load video file '{os.path.basename(path)}': {str(e)}")
 
41
  if clip.reader is None or clip.duration is None or clip.duration <= 0:
42
  clip.close()
43
  raise ValueError(f"Invalid or empty audio file: {os.path.basename(path)}")
44
+ logger.debug(f"Loaded audio: {path}, duration: {clip.duration}s")
45
  return clip
46
  except Exception as e:
47
  raise ValueError(f"Failed to load audio file '{os.path.basename(path)}': {str(e)}")
48
 
49
+ def merge_videos_and_audios(video_paths, audio_paths, output_path, temp_dir, orig_vol=1.0, music_vol=0.5, audio_mode="composite"):
50
  logger.debug(f"Merging {len(video_paths)} videos and {len(audio_paths)} audios to {output_path}")
51
 
52
  # Load video clips
 
54
  for vp in video_paths:
55
  try:
56
  clip = safe_video_clip(vp)
57
+ # Resize to a common resolution (e.g., 1280x720) for compatibility
58
+ clip = clip.resize((1280, 720))
59
  video_clips.append(clip)
60
  except Exception as e:
61
  logger.warning(f"Skipping invalid video file {vp}: {e}")
 
75
 
76
  # Concatenate video clips
77
  final_video = concatenate_videoclips(video_clips, method="compose")
78
+ logger.debug(f"Concatenated video duration: {final_video.duration}s")
79
 
80
+ # Handle original video audio
81
+ original_audio = None
82
+ if final_video.audio:
83
+ original_audio = final_video.audio.volumex(orig_vol)
84
+ logger.debug(f"Original video audio preserved, duration: {original_audio.duration}s")
85
+
86
+ # Combine audio clips
87
+ final_audio = None
88
  if audio_clips:
89
+ if audio_mode == "concatenate":
90
+ final_audio = concatenate_audioclips(audio_clips) # Play audios sequentially
91
+ logger.debug(f"Concatenated audio duration: {final_audio.duration}s")
92
+ else:
93
+ final_audio = CompositeAudioClip(audio_clips) # Play audios simultaneously
94
+ logger.debug(f"Composite audio duration: {final_audio.duration}s")
95
+
96
+ # If original audio exists, combine it with uploaded audio
97
+ if original_audio:
98
+ final_audio = CompositeAudioClip([final_audio, original_audio])
99
+ logger.debug(f"Combined audio (original + uploaded) duration: {final_audio.duration}s")
100
+
101
+ # Set the audio for the final video
102
+ if final_audio:
103
  final_video = final_video.set_audio(final_audio)
104
+ elif original_audio:
105
+ final_video = final_video.set_audio(original_audio)
106
  else:
107
+ logger.warning("No audio (uploaded or original) available for the final video")
 
 
108
 
109
  # Write output
110
  logger.debug(f"Writing output to {output_path}")
 
128
  vc.close()
129
  for ac in audio_clips:
130
  ac.close()
131
+ if original_audio:
132
+ original_audio.close()
133
 
134
  app = FastAPI()
135
 
 
138
  files: list[UploadFile] = File(...),
139
  orig_vol: float = Form(1.0),
140
  music_vol: float = Form(0.5),
141
+ audio_mode: str = Form("composite"), # New parameter: "composite" or "concatenate"
142
  background_tasks: BackgroundTasks = BackgroundTasks(),
143
  ):
144
  temp_dir = tempfile.mkdtemp()
 
170
  logger.debug(f"Video files: {video_files}")
171
  logger.debug(f"Audio files: {audio_files}")
172
 
173
+ if len(video_files) < 1:
174
+ raise HTTPException(status_code=400, detail="Please upload at least one valid video file")
175
 
176
  # Prepare output path
177
  output_path = os.path.join(temp_dir, "merged_output.mp4")
178
 
179
  # Merge videos and audios
180
+ merge_videos_and_audios(video_files, audio_files, output_path, temp_dir, orig_vol, music_vol, audio_mode)
181
 
182
  # Verify the output file exists
183
  if not os.path.exists(output_path):
 
192
  logger.error(f"Output file is empty: {output_path}")
193
  raise HTTPException(status_code=500, detail="Output file is empty")
194
 
195
+ # Schedule cleanup
196
  background_tasks.add_task(shutil.rmtree, temp_dir, ignore_errors=True)
197
 
198
+ # Return the file
199
  logger.debug("Sending streaming response")
200
  return StreamingResponse(
201
  io.BytesIO(content),
 
209
  raise HTTPException(status_code=500, detail=error_msg)
210
 
211
  finally:
 
212
  logger.debug(f"Cleaning up temporary directory: {temp_dir}")
213
  shutil.rmtree(temp_dir, ignore_errors=True)
214
 
215
+ # Log public URL
216
  def log_api_url():
217
  url = os.getenv("SPACE_PUBLIC_URL")
218
  if url: