sam12345324 commited on
Commit
7c25f19
·
verified ·
1 Parent(s): 2bad8b6

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +39 -95
main.py CHANGED
@@ -7,102 +7,51 @@ import logging
7
  import traceback
8
  from moviepy.editor import (
9
  VideoFileClip,
10
- concatenate_videoclips,
11
  AudioFileClip,
12
  CompositeAudioClip,
13
- concatenate_audioclips,
14
  )
15
- import numpy as np
16
 
17
  # Configure logging
18
  logging.basicConfig(level=logging.INFO)
19
  logger = logging.getLogger(__name__)
20
 
21
- # Safe audio clip loader
22
- def safe_audio_clip(path):
23
  try:
24
- clip = AudioFileClip(path)
25
- if clip.reader is None or clip.duration is None or clip.duration <= 0:
26
- clip.close()
27
- raise ValueError(f"Invalid or empty audio file: {os.path.basename(path)}")
28
- return clip
29
- except Exception as e:
30
- raise ValueError(f"Failed to load audio file '{os.path.basename(path)}': {str(e)}")
 
 
 
 
 
 
 
 
 
 
31
 
32
- def merge_videos_and_audios(
33
- video_files, audio_files, orig_vol=1.0, music_vol=0.5, temp_dir=None
34
- ):
35
- try:
36
- output_path = os.path.join(
37
- temp_dir, "merged_output.mp4" if video_files else "merged_output.mp3"
38
- )
39
-
40
- # If no videos, just concatenate audios
41
- if not video_files and audio_files:
42
- audio_clips = []
43
- for a in audio_files:
44
- try:
45
- audio_clip = safe_audio_clip(a)
46
- audio_clips.append(audio_clip)
47
- except Exception as e:
48
- logger.warning(f"Skipping invalid audio file: {a} → {str(e)}")
49
-
50
- if not audio_clips:
51
- raise ValueError("No valid audio files to merge.")
52
-
53
- final_audio = concatenate_audioclips(audio_clips)
54
- final_audio.write_audiofile(output_path)
55
- for clip in audio_clips:
56
- clip.close()
57
- final_audio.close()
58
- return output_path
59
-
60
- # If videos are present:
61
- video_clips = [VideoFileClip(v) for v in video_files]
62
  final_video = concatenate_videoclips(video_clips, method="compose")
63
 
64
- if audio_files:
65
- audio_clips = []
66
- for a in audio_files:
67
- try:
68
- audio_clip = safe_audio_clip(a)
69
- audio_clips.append(audio_clip)
70
- except Exception as e:
71
- logger.warning(f"Skipping invalid audio file: {a} → {str(e)}")
72
-
73
- if audio_clips:
74
- final_audio = concatenate_audioclips(audio_clips).volumex(music_vol)
75
-
76
- original_audio = (
77
- final_video.audio.volumex(orig_vol)
78
- if final_video.audio is not None
79
- else None
80
- )
81
-
82
- if original_audio:
83
- composite_audio = CompositeAudioClip([original_audio, final_audio])
84
- else:
85
- composite_audio = final_audio
86
-
87
- final_video = final_video.set_audio(composite_audio)
88
-
89
- for clip in audio_clips:
90
- clip.close()
91
- else:
92
- logger.warning("No valid audio files found. Using only video audio (if present).")
93
- if final_video.audio is not None:
94
- final_video = final_video.volumex(orig_vol)
95
-
96
- else:
97
- # No audio files provided
98
- if final_video.audio is not None:
99
- final_video = final_video.volumex(orig_vol)
100
 
 
101
  final_video.write_videofile(output_path, codec="libx264", audio_codec="aac")
102
 
103
- for clip in video_clips:
104
- clip.close()
105
  final_video.close()
 
 
 
 
106
 
107
  return output_path
108
 
@@ -116,8 +65,6 @@ app = FastAPI()
116
  @app.post("/merge")
117
  async def merge_endpoint(
118
  files: list[UploadFile] = File(...),
119
- orig_vol: float = Form(1.0),
120
- music_vol: float = Form(0.5),
121
  ):
122
  temp_dir = tempfile.mkdtemp()
123
  try:
@@ -131,30 +78,27 @@ async def merge_endpoint(
131
 
132
  video_files = [f for f in saved_files if f.lower().endswith(".mp4")]
133
  audio_files = [
134
- f
135
- for f in saved_files
136
- if f.lower().endswith((".mp3", ".wav", ".aac", ".m4a", ".ogg"))
137
  ]
138
 
139
  if len(saved_files) < 2:
140
  return {"error": "Please upload at least 2 files (videos or audios)."}
141
 
142
- result_path = merge_videos_and_audios(
143
- video_files, audio_files, orig_vol, music_vol, temp_dir
144
- )
145
 
146
  if isinstance(result_path, str) and result_path.startswith("Error"):
147
  return {"error": result_path}
148
 
149
- media_type = (
150
- "video/mp4" if result_path.lower().endswith(".mp4") else "audio/mpeg"
151
- )
152
- filename = os.path.basename(result_path)
153
-
154
- return FileResponse(result_path, media_type=media_type, filename=filename)
155
 
156
  finally:
157
- shutil.rmtree(temp_dir)
 
 
 
 
 
158
 
159
  # Optional: log space public URL
160
  def log_api_url():
 
7
  import traceback
8
  from moviepy.editor import (
9
  VideoFileClip,
 
10
  AudioFileClip,
11
  CompositeAudioClip,
12
+ concatenate_videoclips,
13
  )
 
14
 
15
  # Configure logging
16
  logging.basicConfig(level=logging.INFO)
17
  logger = logging.getLogger(__name__)
18
 
19
+ def merge_videos_and_audios(video_paths, audio_paths, output_path):
 
20
  try:
21
+ # Load video clips
22
+ video_clips = [VideoFileClip(vp) for vp in video_paths]
23
+
24
+ # Load audio clips, only if valid files
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"Could not load audio file {ap}: {e}")
32
+
33
+ # Combine all audio clips into one composite audio clip (plays simultaneously)
34
+ if audio_clips:
35
+ final_audio = CompositeAudioClip(audio_clips)
36
+ else:
37
+ final_audio = None
38
 
39
+ # Concatenate video clips
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  final_video = concatenate_videoclips(video_clips, method="compose")
41
 
42
+ # Set the audio if available
43
+ if final_audio is not None:
44
+ final_video = final_video.set_audio(final_audio)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
+ # Write output video file
47
  final_video.write_videofile(output_path, codec="libx264", audio_codec="aac")
48
 
49
+ # Close clips to release resources
 
50
  final_video.close()
51
+ for vc in video_clips:
52
+ vc.close()
53
+ for ac in audio_clips:
54
+ ac.close()
55
 
56
  return output_path
57
 
 
65
  @app.post("/merge")
66
  async def merge_endpoint(
67
  files: list[UploadFile] = File(...),
 
 
68
  ):
69
  temp_dir = tempfile.mkdtemp()
70
  try:
 
78
 
79
  video_files = [f for f in saved_files if f.lower().endswith(".mp4")]
80
  audio_files = [
81
+ f for f in saved_files if f.lower().endswith((".mp3", ".wav", ".aac", ".m4a", ".ogg"))
 
 
82
  ]
83
 
84
  if len(saved_files) < 2:
85
  return {"error": "Please upload at least 2 files (videos or audios)."}
86
 
87
+ output_file = os.path.join(temp_dir, "merged_output.mp4")
88
+ result_path = merge_videos_and_audios(video_files, audio_files, output_file)
 
89
 
90
  if isinstance(result_path, str) and result_path.startswith("Error"):
91
  return {"error": result_path}
92
 
93
+ return FileResponse(result_path, media_type="video/mp4", filename="merged_output.mp4")
 
 
 
 
 
94
 
95
  finally:
96
+ # Delay deleting temp_dir to allow FileResponse to send file (FastAPI streams file)
97
+ # So do NOT delete temp_dir immediately here if you want to serve the file correctly.
98
+ # Instead, you might want a background task or periodic cleanup.
99
+ # For now, comment out or remove this line if the file disappears before sending:
100
+ # shutil.rmtree(temp_dir)
101
+ pass
102
 
103
  # Optional: log space public URL
104
  def log_api_url():