# main.py import gradio as gr from fastapi import FastAPI, File, UploadFile, Form from fastapi.middleware.cors import CORSMiddleware import uvicorn import os import tempfile import shutil import traceback import logging from merge_utils import merge_videos_and_audios # move your merge logic to merge_utils.py for clean code! # Setup logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # --- FASTAPI SETUP --- fastapi_app = FastAPI(title="Video & Audio Merger API") # CORS for n8n to call the API fastapi_app.add_middleware( CORSMiddleware, allow_origins=["*"], # You can restrict to your domain if needed allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # FastAPI endpoint to accept binary files from n8n @fastapi_app.post("/merge") async def merge_endpoint( files: list[UploadFile] = File(...), orig_vol: float = Form(1.0), music_vol: float = Form(0.5), ): try: logger.info(f"Received {len(files)} files from n8n") # Save files to temp dir temp_dir = tempfile.mkdtemp() saved_files = [] for upload_file in files: filename = os.path.basename(upload_file.filename) file_path = os.path.join(temp_dir, filename) with open(file_path, "wb") as f: content = await upload_file.read() f.write(content) saved_files.append(file_path) logger.info(f"Saved file: {file_path}") # Split files into video and audio video_files = [f for f in saved_files if f.lower().endswith(".mp4")] audio_files = [f for f in saved_files if f.lower().endswith((".mp3", ".wav"))] # Run merge output_path = merge_videos_and_audios( video_files=video_files, audio_files=audio_files, orig_vol=orig_vol, music_vol=music_vol, temp_dir=temp_dir, ) # Return result as path or error if isinstance(output_path, str) and output_path.startswith("Error"): return {"success": False, "error": output_path} # Move output to persistent location (optional) public_output_dir = "./outputs" os.makedirs(public_output_dir, exist_ok=True) final_output_path = os.path.join(public_output_dir, os.path.basename(output_path)) shutil.move(output_path, final_output_path) logger.info(f"Final output moved to {final_output_path}") return { "success": True, "output_url": f"/outputs/{os.path.basename(final_output_path)}" } except Exception as e: error_msg = f"Exception: {str(e)}\n{traceback.format_exc()}" logger.error(error_msg) return {"success": False, "error": error_msg} # --- GRADIO SETUP --- with gr.Blocks(title="Video and Audio Merger UI") as gradio_app: gr.Markdown("## Video and Audio Merger") file_input = gr.File(label="Upload Files", type="binary", file_count="multiple") orig_vol_input = gr.Slider(minimum=0.0, maximum=1.0, value=1.0, step=0.05, label="Original Video Audio Volume") music_vol_input = gr.Slider(minimum=0.0, maximum=1.0, value=0.5, step=0.05, label="Background Audio Volume") output_video = gr.Video(label="Merged Video") output_audio = gr.Audio(label="Merged Audio") merge_button = gr.Button("Merge Files") def gradio_merge_files(file_binaries, orig_vol, music_vol): # You can call your existing function here (same as before) pass # Keep your old gradio_merge_files() here! merge_button.click( fn=gradio_merge_files, inputs=[file_input, orig_vol_input, music_vol_input], outputs=[output_video, output_audio], ) # --- COMBINE BOTH --- if __name__ == "__main__": import threading # Run FastAPI server in background def run_fastapi(): uvicorn.run(fastapi_app, host="0.0.0.0", port=8000) threading.Thread(target=run_fastapi).start() # Run Gradio app on another port (HuggingFace will pick this) gradio_app.queue(api_open=True) gradio_app.launch(server_port=7860, share=True)