sam12345324 commited on
Commit
1553cdb
·
verified ·
1 Parent(s): b384661

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +190 -0
app.py ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import tempfile
3
+ import os
4
+ from moviepy.editor import VideoFileClip, concatenate_videoclips, AudioFileClip, CompositeAudioClip, concatenate_audioclips
5
+ import logging
6
+ import sys
7
+ import traceback
8
+ import socket
9
+
10
+ # Set up logging to debug issues
11
+ logging.basicConfig(level=logging.INFO, handlers=[logging.StreamHandler(sys.stdout)])
12
+ logger = logging.getLogger(__name__)
13
+
14
+ # --- Functions ---
15
+
16
+ def check_port(port):
17
+ """
18
+ Check if a port is available.
19
+ Returns True if the port is free, False if it's in use.
20
+ """
21
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
22
+ try:
23
+ s.bind(("0.0.0.0", port))
24
+ return True
25
+ except socket.error:
26
+ return False
27
+
28
+ def merge_videos_and_audios(video_files, audio_files=None, orig_vol=1.0, music_vol=0.5):
29
+ """
30
+ Merge multiple video clips into one and optionally add a concatenated audio track.
31
+ If audio_files is None, the video will retain its original audio (if any).
32
+ Volumes for original video audio and background audio (if provided) can be controlled.
33
+ Uses method='compose' to avoid cropping in landscape videos.
34
+ Returns the path to the merged video file.
35
+ """
36
+ try:
37
+ logger.info(f"Starting merge with {len(video_files)} video files and {len(audio_files) if audio_files else 0} audio files")
38
+
39
+ # Create a temporary output path
40
+ temp_dir = tempfile.mkdtemp()
41
+ output_path = os.path.join(temp_dir, "merged_output.mp4")
42
+
43
+ # Load and concatenate video clips
44
+ video_clips = [VideoFileClip(video) for video in video_files]
45
+ final_video_clip = concatenate_videoclips(video_clips, method='compose')
46
+
47
+ # Determine final video duration
48
+ video_duration = final_video_clip.duration or sum(clip.duration for clip in video_clips)
49
+ logger.info(f"Total video duration: {video_duration} seconds")
50
+
51
+ # Handle audio (optional)
52
+ if audio_files:
53
+ logger.info("Processing audio files")
54
+ # Load and concatenate audio clips
55
+ audio_clips = [AudioFileClip(audio) for audio in audio_files]
56
+ concatenated_audio = concatenate_audioclips(audio_clips)
57
+
58
+ # Adjust concatenated audio duration to match video duration (trim or loop)
59
+ if concatenated_audio.duration > video_duration:
60
+ concatenated_audio = concatenated_audio.subclip(0, video_duration)
61
+ elif concatenated_audio.duration < video_duration:
62
+ # Loop the audio to match video duration
63
+ concatenated_audio = concatenated_audio.fx(lambda clip: clip.loop(duration=video_duration))
64
+
65
+ # Apply volume to concatenated audio
66
+ concatenated_audio = concatenated_audio.volumex(music_vol)
67
+
68
+ # Get original video audio (if any) and apply volume
69
+ original_audio = final_video_clip.audio.volumex(orig_vol) if final_video_clip.audio else None
70
+
71
+ # Composite the audio tracks
72
+ if original_audio:
73
+ final_audio = CompositeAudioClip([original_audio, concatenated_audio])
74
+ else:
75
+ final_audio = concatenated_audio
76
+ else:
77
+ logger.info("No audio files provided; using original video audio if available")
78
+ # If no audio files provided, retain original video audio (if any)
79
+ final_audio = final_video_clip.audio.volumex(orig_vol) if final_video_clip.audio else None
80
+
81
+ # Set the audio to the final video
82
+ final_video_clip = final_video_clip.set_audio(final_audio)
83
+
84
+ # Write the final video
85
+ logger.info(f"Writing output video to {output_path}")
86
+ final_video_clip.write_videofile(output_path, codec="libx264", fps=30, audio_codec="aac", ffmpeg_params=["-preset", "fast"])
87
+
88
+ # Close resources
89
+ final_video_clip.close()
90
+ for clip in video_clips:
91
+ clip.close()
92
+ if audio_files:
93
+ for clip in audio_clips:
94
+ clip.close()
95
+ concatenated_audio.close()
96
+
97
+ logger.info("Merge completed successfully")
98
+ return output_path
99
+ except Exception as e:
100
+ error_msg = f"Error during video and audio merging: {str(e)}\n{traceback.format_exc()}"
101
+ logger.error(error_msg)
102
+ return error_msg
103
+
104
+ # --- Gradio App Using Blocks ---
105
+
106
+ def gradio_merge_videos(video_files, audio_files, orig_vol, music_vol):
107
+ """
108
+ Gradio endpoint to merge videos and optionally add audio.
109
+ Args:
110
+ video_files: List of video file paths
111
+ audio_files: List of audio file paths (optional)
112
+ orig_vol: Volume for original video audio (0.0 to 1.0)
113
+ music_vol: Volume for background audio (0.0 to 1.0)
114
+ Returns:
115
+ Path to the merged video file or error message
116
+ """
117
+ logger.info(f"Received {len(video_files)} video files and {len(audio_files) if audio_files else 0} audio files")
118
+
119
+ if len(video_files) <= 1:
120
+ error_msg = "Error: Please upload more than 1 video file."
121
+ logger.error(error_msg)
122
+ return error_msg
123
+
124
+ result = merge_videos_and_audios(
125
+ video_files, audio_files, orig_vol=orig_vol, music_vol=music_vol
126
+ )
127
+
128
+ if isinstance(result, str) and result.startswith("Error"):
129
+ logger.error(result)
130
+ return result
131
+ else:
132
+ logger.info(f"Merge successful. Output saved at: {result}")
133
+ return result # Gradio will serve this as a downloadable video
134
+
135
+ # --- Main Execution ---
136
+
137
+ if __name__ == "__main__":
138
+ logger.info(f"Environment: {os.environ.get('HUGGINGFACE_SPACES', 'Not in HF Spaces')}")
139
+ logger.info(f"Arguments: {sys.argv}")
140
+
141
+ # Check ports in a wider range
142
+ default_port = 7860
143
+ ports_to_try = list(range(default_port, default_port + 11)) # 7860 to 7870
144
+
145
+ selected_port = None
146
+ for port in ports_to_try:
147
+ logger.info(f"Checking if port {port} is available")
148
+ if check_port(port):
149
+ logger.info(f"Port {port} is available")
150
+ selected_port = port
151
+ break
152
+ else:
153
+ logger.warning(f"Port {port} is already in use")
154
+
155
+ if selected_port is None:
156
+ logger.error("No available ports found in range 7860-7870")
157
+ sys.exit(1)
158
+
159
+ logger.info("Launching Gradio Blocks interface")
160
+
161
+ with gr.Blocks(title="Video and Audio Merger API") as app:
162
+ gr.Markdown("## Video and Audio Merger API")
163
+ gr.Markdown("Upload more than 1 video file to merge them into a single video. Optionally upload audio files to add as background audio.")
164
+
165
+ with gr.Row():
166
+ video_input = gr.File(label="Video Files (more than 1)", type="filepath", file_count="multiple")
167
+ audio_input = gr.File(label="Audio Files (Optional)", type="filepath", file_count="multiple")
168
+
169
+ with gr.Row():
170
+ orig_vol_input = gr.Slider(minimum=0.0, maximum=1.0, value=1.0, step=0.05, label="Original Video Audio Volume")
171
+ music_vol_input = gr.Slider(minimum=0.0, maximum=1.0, value=0.5, step=0.05, label="Background Audio Volume")
172
+
173
+ output_video = gr.Video(label="Merged Video")
174
+ merge_button = gr.Button("Merge Videos and Audios")
175
+
176
+ merge_button.click(
177
+ fn=gradio_merge_videos,
178
+ inputs=[video_input, audio_input, orig_vol_input, music_vol_input],
179
+ outputs=output_video
180
+ )
181
+
182
+ try:
183
+ logger.info(f"Attempting to launch Gradio app on port {selected_port}")
184
+ # Set share=True to bypass localhost check
185
+ app.launch(server_port=selected_port, share=True)
186
+ logger.info(f"Gradio app launched successfully on port {app.server_port}")
187
+ except Exception as e:
188
+ error_msg = f"Failed to launch Gradio interface: {str(e)}\n{traceback.format_exc()}"
189
+ logger.error(error_msg)
190
+ raise