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

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +82 -135
main.py CHANGED
@@ -1,149 +1,96 @@
1
- from fastapi import FastAPI, UploadFile, File, Form
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
- # Configure logging
17
- logging.basicConfig(level=logging.INFO)
18
- logger = logging.getLogger(__name__)
19
-
20
- def safe_audio_clip(path):
21
- try:
22
- clip = AudioFileClip(path)
23
- if clip.reader is None or clip.duration is None or clip.duration <= 0:
24
- clip.close()
25
- raise ValueError(f"Invalid or empty audio file: {os.path.basename(path)}")
26
- return clip
27
- except Exception as e:
28
- raise ValueError(f"Failed to load audio file '{os.path.basename(path)}': {str(e)}")
29
-
30
- def merge_videos_and_audios(
31
- video_files, audio_files, output_path, temp_dir,
32
- orig_vol=1.0, music_vol=0.5,
33
- ):
34
- try:
35
- # Load video clips
36
- video_clips = [VideoFileClip(v) for v in video_files]
37
-
38
- # Load audio clips safely
39
- audio_clips = []
40
- for a in audio_files:
41
- try:
42
- clip = safe_audio_clip(a)
43
- audio_clips.append(clip)
44
- except Exception as e:
45
- logger.warning(f"Skipping invalid audio file: {a} → {str(e)}")
46
-
47
- # Prepare final audio composite if audio clips exist
48
- if audio_clips:
49
- final_audio = concatenate_audioclips(audio_clips).volumex(music_vol)
50
- else:
51
- final_audio = None
52
-
53
- # Concatenate video clips with method 'compose' for safety
54
- final_video = concatenate_videoclips(video_clips, method="compose")
55
-
56
- # Combine video audio with provided original volume
57
- original_audio = final_video.audio.volumex(orig_vol) if final_video.audio else None
58
-
59
- # Combine audios if both exist
60
- if original_audio and final_audio:
61
- composite_audio = CompositeAudioClip([original_audio, final_audio])
62
- final_video = final_video.set_audio(composite_audio)
63
- elif final_audio:
64
- final_video = final_video.set_audio(final_audio)
65
- elif original_audio:
66
- final_video = final_video.set_audio(original_audio)
67
- else:
68
- final_video = final_video.set_audio(None)
69
-
70
- # Explicit writable temp audio file path inside temp_dir
71
- temp_audiofile_path = os.path.join(temp_dir, "temp_audiofile.m4a")
72
-
73
- # Write output video with explicit temp audio file to avoid permission errors
74
- final_video.write_videofile(
75
- output_path,
76
- codec="libx264",
77
- audio_codec="aac",
78
- temp_audiofile=temp_audiofile_path,
79
- remove_temp=True,
80
- verbose=False,
81
- logger=None,
82
- )
83
-
84
- # Cleanup
85
- final_video.close()
86
- for vc in video_clips:
87
- vc.close()
88
- for ac in audio_clips:
89
- ac.close()
90
-
91
- return output_path
92
-
93
- except Exception as e:
94
- error_msg = f"Error during merge: {str(e)}\n{traceback.format_exc()}"
95
- logger.error(error_msg)
96
- return error_msg
97
 
98
  app = FastAPI()
99
 
100
- @app.post("/merge")
101
- async def merge_endpoint(
102
- files: list[UploadFile] = File(...),
103
- orig_vol: float = Form(1.0),
104
- music_vol: float = Form(0.5),
105
- ):
106
- temp_dir = tempfile.mkdtemp()
107
- try:
108
- saved_files = []
109
- for uploaded_file in files:
110
- file_path = os.path.join(temp_dir, uploaded_file.filename)
111
- with open(file_path, "wb") as out_file:
112
- content = await uploaded_file.read()
113
- out_file.write(content)
114
- saved_files.append(file_path)
115
 
116
- video_files = [f for f in saved_files if f.lower().endswith(".mp4")]
117
- audio_files = [
118
- f for f in saved_files if f.lower().endswith((".mp3", ".wav", ".aac", ".m4a", ".ogg"))
119
- ]
 
 
 
 
120
 
121
- if len(saved_files) < 2:
122
- return {"error": "Please upload at least 2 files (videos or audios)."}
123
 
124
- output_file = os.path.join(temp_dir, "merged_output.mp4")
 
125
 
126
- result_path = merge_videos_and_audios(
127
- video_files, audio_files, output_file, temp_dir, orig_vol, music_vol
128
- )
129
 
130
- if isinstance(result_path, str) and result_path.startswith("Error"):
131
- return {"error": result_path}
132
 
133
- media_type = "video/mp4" if result_path.lower().endswith(".mp4") else "audio/mpeg"
134
- filename = os.path.basename(result_path)
 
 
 
 
135
 
136
- return FileResponse(result_path, media_type=media_type, filename=filename)
137
 
138
- finally:
139
- shutil.rmtree(temp_dir)
 
 
 
 
 
 
140
 
141
- # Optional: log space public URL
142
- def log_api_url():
143
- url = os.getenv("SPACE_PUBLIC_URL")
144
- if url:
145
- logger.info(f"API is available at: {url}/merge")
146
- else:
147
- logger.info("SPACE_PUBLIC_URL environment variable not found")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
 
149
- log_api_url()
 
 
 
 
 
 
 
 
 
 
 
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)}