sam12345324 commited on
Commit
e174367
·
verified ·
1 Parent(s): 4ff4cc1

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +57 -43
main.py CHANGED
@@ -12,33 +12,58 @@ from moviepy.editor import (
12
  CompositeAudioClip,
13
  concatenate_audioclips,
14
  )
15
- import zipfile
16
 
17
  # Configure logging
18
  logging.basicConfig(level=logging.INFO)
19
  logger = logging.getLogger("main")
20
 
21
- # Merge function
22
- def merge_videos_and_audios(video_paths, audio_paths, output_path):
 
 
 
 
 
 
 
 
 
 
 
23
  video_clips = [VideoFileClip(vp) for vp in video_paths]
24
 
 
25
  audio_clips = []
26
  for ap in audio_paths:
27
  try:
28
- audio_clip = AudioFileClip(ap)
29
  audio_clips.append(audio_clip)
30
  except Exception as e:
31
- logger.warning(f"Skipping invalid audio file: {ap}: {str(e)}")
32
 
33
- final_audio = CompositeAudioClip(audio_clips) if audio_clips else None
 
 
 
 
 
 
34
  final_video = concatenate_videoclips(video_clips, method="compose")
35
 
 
36
  if final_audio is not None:
37
  final_video = final_video.set_audio(final_audio)
38
 
39
- final_video.write_videofile(output_path, codec="libx264", audio_codec="aac")
 
 
 
 
 
 
 
40
 
41
- # Cleanup
42
  final_video.close()
43
  for vc in video_clips:
44
  vc.close()
@@ -49,60 +74,49 @@ app = FastAPI()
49
 
50
  @app.post("/merge")
51
  async def merge_endpoint(
52
- zip_file: UploadFile = File(...),
53
  orig_vol: float = Form(1.0),
54
  music_vol: float = Form(0.5),
55
  ):
56
  temp_dir = tempfile.mkdtemp()
57
  try:
58
- # Save uploaded ZIP
59
- zip_path = os.path.join(temp_dir, zip_file.filename)
60
- with open(zip_path, "wb") as f:
61
- f.write(await zip_file.read())
62
-
63
- # Extract ZIP
64
- extract_dir = os.path.join(temp_dir, "extracted")
65
- os.makedirs(extract_dir, exist_ok=True)
66
-
67
- with zipfile.ZipFile(zip_path, "r") as zip_ref:
68
- zip_ref.extractall(extract_dir)
69
-
70
- # Collect files
71
- files = os.listdir(extract_dir)
72
- video_files = [
73
- os.path.join(extract_dir, f)
74
- for f in files
75
- if f.lower().endswith(".mp4")
76
- ]
77
  audio_files = [
78
- os.path.join(extract_dir, f)
79
- for f in files
80
  if f.lower().endswith((".mp3", ".wav", ".aac", ".m4a", ".ogg"))
81
  ]
82
 
83
- if len(video_files) == 0 and len(audio_files) == 0:
84
- return {"error": "No valid video or audio files found in ZIP."}
85
 
86
- # Output path
87
  output_path = os.path.join(temp_dir, "merged_output.mp4")
88
 
89
- # Merge
90
- merge_videos_and_audios(video_files, audio_files, output_path)
91
 
92
- # Return result
93
- return FileResponse(
94
- output_path,
95
- media_type="video/mp4",
96
- filename="merged_output.mp4"
97
- )
98
 
99
  except Exception as e:
100
- error_msg = f"Error: {str(e)}\n{traceback.format_exc()}"
101
  logger.error(error_msg)
102
  return {"error": error_msg}
103
 
104
  finally:
105
- shutil.rmtree(temp_dir)
106
 
107
  # Optional: log public URL
108
  def log_api_url():
 
12
  CompositeAudioClip,
13
  concatenate_audioclips,
14
  )
 
15
 
16
  # Configure logging
17
  logging.basicConfig(level=logging.INFO)
18
  logger = logging.getLogger("main")
19
 
20
+ # Safe audio clip loader
21
+ def safe_audio_clip(path):
22
+ try:
23
+ clip = AudioFileClip(path)
24
+ if clip.reader is None or clip.duration is None or clip.duration <= 0:
25
+ clip.close()
26
+ raise ValueError(f"Invalid or empty audio file: {os.path.basename(path)}")
27
+ return clip
28
+ except Exception as e:
29
+ raise ValueError(f"Failed to load audio file '{os.path.basename(path)}': {str(e)}")
30
+
31
+ def merge_videos_and_audios(video_paths, audio_paths, output_path, temp_dir):
32
+ # Load video clips
33
  video_clips = [VideoFileClip(vp) for vp in video_paths]
34
 
35
+ # Load audio clips, only if valid files
36
  audio_clips = []
37
  for ap in audio_paths:
38
  try:
39
+ audio_clip = safe_audio_clip(ap)
40
  audio_clips.append(audio_clip)
41
  except Exception as e:
42
+ logger.warning(f"Skipping invalid audio file {ap}: {e}")
43
 
44
+ # Combine all audio clips into one composite audio clip (optional)
45
+ if audio_clips:
46
+ final_audio = CompositeAudioClip(audio_clips)
47
+ else:
48
+ final_audio = None
49
+
50
+ # Concatenate video clips
51
  final_video = concatenate_videoclips(video_clips, method="compose")
52
 
53
+ # Set the audio if available
54
  if final_audio is not None:
55
  final_video = final_video.set_audio(final_audio)
56
 
57
+ # Write output — FIX: use temp_audiofile in /tmp
58
+ final_video.write_videofile(
59
+ output_path,
60
+ codec="libx264",
61
+ audio_codec="aac",
62
+ temp_audiofile=os.path.join(temp_dir, "temp-audio.m4a"),
63
+ remove_temp=True
64
+ )
65
 
66
+ # Close clips to release resources
67
  final_video.close()
68
  for vc in video_clips:
69
  vc.close()
 
74
 
75
  @app.post("/merge")
76
  async def merge_endpoint(
77
+ files: list[UploadFile] = File(...),
78
  orig_vol: float = Form(1.0),
79
  music_vol: float = Form(0.5),
80
  ):
81
  temp_dir = tempfile.mkdtemp()
82
  try:
83
+ saved_files = []
84
+
85
+ # Save uploaded files
86
+ for uploaded_file in files:
87
+ file_path = os.path.join(temp_dir, uploaded_file.filename)
88
+ with open(file_path, "wb") as out_file:
89
+ content = await uploaded_file.read()
90
+ out_file.write(content)
91
+ saved_files.append(file_path)
92
+
93
+ # Separate video and audio files
94
+ video_files = [f for f in saved_files if f.lower().endswith(".mp4")]
 
 
 
 
 
 
 
95
  audio_files = [
96
+ f
97
+ for f in saved_files
98
  if f.lower().endswith((".mp3", ".wav", ".aac", ".m4a", ".ogg"))
99
  ]
100
 
101
+ if len(saved_files) < 2:
102
+ return {"error": "Please upload at least 2 files (videos or audios)."}
103
 
104
+ # Prepare output path
105
  output_path = os.path.join(temp_dir, "merged_output.mp4")
106
 
107
+ # Merge videos and audios
108
+ merge_videos_and_audios(video_files, audio_files, output_path, temp_dir)
109
 
110
+ # Return merged file
111
+ return FileResponse(output_path, media_type="video/mp4", filename="merged_output.mp4")
 
 
 
 
112
 
113
  except Exception as e:
114
+ error_msg = f"Error: {str(e)}\n\n{traceback.format_exc()}"
115
  logger.error(error_msg)
116
  return {"error": error_msg}
117
 
118
  finally:
119
+ shutil.rmtree(temp_dir, ignore_errors=True)
120
 
121
  # Optional: log public URL
122
  def log_api_url():