sam12345324 commited on
Commit
8c05c55
·
verified ·
1 Parent(s): 5dd9ede

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +135 -79
main.py CHANGED
@@ -1,96 +1,152 @@
1
- import os
2
- import shutil
3
- import tempfile
4
- from fastapi import FastAPI, File, UploadFile, Form, BackgroundTasks
5
  from fastapi.responses import FileResponse
6
- from moviepy.editor import VideoFileClip, AudioFileClip, CompositeAudioClip, concatenate_videoclips
7
-
8
- app = FastAPI()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
- def merge_videos_and_audios(video_paths, audio_paths, output_path):
11
- # Load video clips
12
- video_clips = [VideoFileClip(vp) for vp in video_paths]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
- # Load audio clips (skip invalid files)
15
- audio_clips = []
16
- for ap in audio_paths:
17
- try:
18
- audio_clip = AudioFileClip(ap)
19
- audio_clips.append(audio_clip)
20
- except Exception as e:
21
- print(f"Warning: Could not load audio file {ap}: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
- # Combine audio clips into one composite audio clip
24
- final_audio = CompositeAudioClip(audio_clips) if audio_clips else None
25
 
26
- # Concatenate video clips
27
- final_video = concatenate_videoclips(video_clips)
 
28
 
29
- # Set audio if exists
30
- if final_audio:
31
- final_video = final_video.set_audio(final_audio)
32
 
33
- # Write output video file
34
- final_video.write_videofile(output_path, codec="libx264", audio_codec="aac")
 
 
35
 
36
- # Close clips
37
- final_video.close()
38
- for vc in video_clips:
39
- vc.close()
40
- for ac in audio_clips:
41
- ac.close()
42
 
 
43
 
44
  @app.post("/merge")
45
  async def merge_endpoint(
46
- video_files: list[UploadFile] = File(...),
47
- audio_files: list[UploadFile] = File(None),
48
- background_tasks: BackgroundTasks = None
49
  ):
50
- # Create temporary directory for uploaded files
51
  temp_dir = tempfile.mkdtemp()
52
-
53
  try:
54
- # Save uploaded video files to temp_dir
55
- video_paths = []
56
- for vf in video_files:
57
- video_path = os.path.join(temp_dir, vf.filename)
58
- with open(video_path, "wb") as f:
59
- content = await vf.read()
60
- f.write(content)
61
- video_paths.append(video_path)
62
-
63
- # Save uploaded audio files to temp_dir (if any)
64
- audio_paths = []
65
- if audio_files:
66
- for af in audio_files:
67
- audio_path = os.path.join(temp_dir, af.filename)
68
- with open(audio_path, "wb") as f:
69
- content = await af.read()
70
- f.write(content)
71
- audio_paths.append(audio_path)
72
-
73
- # Prepare permanent output directory
74
- output_dir = os.path.abspath("output")
75
- os.makedirs(output_dir, exist_ok=True)
76
-
77
- # Define output video path
78
- output_file = os.path.join(output_dir, "merged_output.mp4")
79
-
80
- # Merge videos and audios
81
- merge_videos_and_audios(video_paths, audio_paths, output_file)
82
-
83
- # Add background task to delete temp_dir after response sent
84
- if background_tasks:
85
- background_tasks.add_task(shutil.rmtree, temp_dir)
86
- else:
87
- # If no background_tasks support, you can delete later manually or omit
88
- pass
89
 
90
- # Return merged video file
91
- return FileResponse(output_file, media_type="video/mp4", filename="merged_output.mp4")
92
 
93
- except Exception as e:
94
- # Cleanup temp_dir if exception occurs
95
- shutil.rmtree(temp_dir)
96
- return {"error": str(e)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, UploadFile, File, Form, HTTPException
 
 
 
2
  from fastapi.responses import FileResponse
3
+ import tempfile
4
+ import shutil
5
+ import os
6
+ import logging
7
+ import traceback
8
+ from moviepy.editor import (
9
+ VideoFileClip,
10
+ concatenate_videoclips,
11
+ AudioFileClip,
12
+ CompositeAudioClip,
13
+ concatenate_audioclips,
14
+ )
15
+
16
+ logging.basicConfig(level=logging.INFO)
17
+ logger = logging.getLogger(__name__)
18
+
19
+ def safe_audio_clip(path):
20
+ try:
21
+ clip = AudioFileClip(path)
22
+ if clip.reader is None or clip.duration is None or clip.duration <= 0:
23
+ clip.close()
24
+ raise ValueError(f"Invalid or empty audio file: {os.path.basename(path)}")
25
+ return clip
26
+ except Exception as e:
27
+ raise ValueError(f"Failed to load audio file '{os.path.basename(path)}': {str(e)}")
28
 
29
+ def merge_videos_and_audios(
30
+ video_files, audio_files, orig_vol=1.0, music_vol=0.5, temp_dir=None
31
+ ):
32
+ try:
33
+ # Use temp_dir for output path
34
+ output_path = os.path.join(temp_dir, "merged_output.mp4" if video_files else "merged_output.mp3")
35
+
36
+ if not video_files and audio_files:
37
+ audio_clips = []
38
+ for a in audio_files:
39
+ try:
40
+ audio_clip = safe_audio_clip(a)
41
+ audio_clips.append(audio_clip)
42
+ except Exception as e:
43
+ logger.warning(f"Skipping invalid audio file: {a} → {str(e)}")
44
+
45
+ if not audio_clips:
46
+ raise ValueError("No valid audio files to merge.")
47
+
48
+ final_audio = concatenate_audioclips(audio_clips)
49
+ final_audio.write_audiofile(output_path)
50
+ for clip in audio_clips:
51
+ clip.close()
52
+ final_audio.close()
53
+ return output_path
54
+
55
+ video_clips = [VideoFileClip(v) for v in video_files]
56
+ final_video = concatenate_videoclips(video_clips, method="compose")
57
 
58
+ if audio_files:
59
+ audio_clips = []
60
+ for a in audio_files:
61
+ try:
62
+ audio_clip = safe_audio_clip(a)
63
+ audio_clips.append(audio_clip)
64
+ except Exception as e:
65
+ logger.warning(f"Skipping invalid audio file: {a} {str(e)}")
66
+
67
+ if audio_clips:
68
+ final_audio = concatenate_audioclips(audio_clips).volumex(music_vol)
69
+ original_audio = final_video.audio.volumex(orig_vol) if final_video.audio else None
70
+
71
+ if original_audio:
72
+ composite_audio = CompositeAudioClip([original_audio, final_audio])
73
+ else:
74
+ composite_audio = final_audio
75
+
76
+ final_video = final_video.set_audio(composite_audio)
77
+
78
+ for clip in audio_clips:
79
+ clip.close()
80
+ else:
81
+ logger.warning("No valid audio files found. Using only video audio (if present).")
82
+ if final_video.audio:
83
+ final_video = final_video.volumex(orig_vol)
84
+ else:
85
+ if final_video.audio:
86
+ final_video = final_video.volumex(orig_vol)
87
 
88
+ final_video.write_videofile(output_path, codec="libx264", audio_codec="aac")
 
89
 
90
+ for clip in video_clips:
91
+ clip.close()
92
+ final_video.close()
93
 
94
+ return output_path
 
 
95
 
96
+ except Exception as e:
97
+ error_msg = f"Error during merge: {str(e)}\n{traceback.format_exc()}"
98
+ logger.error(error_msg)
99
+ raise RuntimeError(error_msg)
100
 
 
 
 
 
 
 
101
 
102
+ app = FastAPI()
103
 
104
  @app.post("/merge")
105
  async def merge_endpoint(
106
+ files: list[UploadFile] = File(...),
107
+ orig_vol: float = Form(1.0),
108
+ music_vol: float = Form(0.5),
109
  ):
 
110
  temp_dir = tempfile.mkdtemp()
 
111
  try:
112
+ saved_files = []
113
+ for uploaded_file in files:
114
+ file_path = os.path.join(temp_dir, uploaded_file.filename)
115
+ with open(file_path, "wb") as out_file:
116
+ content = await uploaded_file.read()
117
+ out_file.write(content)
118
+ saved_files.append(file_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
 
120
+ video_files = [f for f in saved_files if f.lower().endswith(".mp4")]
121
+ audio_files = [f for f in saved_files if f.lower().endswith((".mp3", ".wav", ".aac", ".m4a", ".ogg"))]
122
 
123
+ if len(saved_files) < 2:
124
+ raise HTTPException(status_code=400, detail="Please upload at least 2 files (videos or audios).")
125
+
126
+ result_path = merge_videos_and_audios(video_files, audio_files, orig_vol, music_vol, temp_dir)
127
+
128
+ media_type = "video/mp4" if result_path.lower().endswith(".mp4") else "audio/mpeg"
129
+ filename = os.path.basename(result_path)
130
+
131
+ return FileResponse(result_path, media_type=media_type, filename=filename)
132
+
133
+ except RuntimeError as e:
134
+ raise HTTPException(status_code=500, detail=str(e))
135
+
136
+ finally:
137
+ # Clean up temp directory safely
138
+ try:
139
+ shutil.rmtree(temp_dir)
140
+ except Exception as e:
141
+ logger.warning(f"Failed to remove temp directory {temp_dir}: {e}")
142
+
143
+
144
+ # Optional logging
145
+ def log_api_url():
146
+ url = os.getenv("SPACE_PUBLIC_URL")
147
+ if url:
148
+ logger.info(f"API is available at: {url}/merge")
149
+ else:
150
+ logger.info("SPACE_PUBLIC_URL environment variable not found")
151
+
152
+ log_api_url()