import gradio as gr import subprocess import os import shutil import uuid def get_sorted_files(file_list): """Sorts files by name (00.mp4, 01.mp4...)""" if not file_list: return [] file_list.sort(key=lambda x: x.name) return file_list def process_and_stitch(video_files, progress=gr.Progress()): if not video_files: return None, "Error: Please upload at least one video file." # Use Absolute Paths to prevent "File Not Found" errors on Hugging Face base_path = os.path.abspath(".") session_id = str(uuid.uuid4()) temp_dir = os.path.join(base_path, "temp_clips", session_id) os.makedirs(temp_dir, exist_ok=True) sorted_videos = get_sorted_files(video_files) processed_clips = [] clip_list_path = os.path.join(temp_dir, "clips_list.txt") output_filename = os.path.join(temp_dir, "final_tiktok_stitch.mp4") try: # 1. Process each clip for i, file_obj in enumerate(progress.tqdm(sorted_videos, desc="Styling Clips")): input_path = file_obj.name clip_name = os.path.join(temp_dir, f"processed_{i:03d}.mp4") # Viral Square Aesthetic Filter filter_complex = ( f"[0:v]scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,boxblur=40:5[bg];" f"[0:v]scale=1080:1080:force_original_aspect_ratio=decrease,pad=1080:1080:(ow-iw)/2:(oh-ih)/2[fg];" f"[bg][fg]overlay=(W-w)/2:(H-h)/2" ) cmd = [ "ffmpeg", "-y", "-i", input_path, "-filter_complex", filter_complex, "-c:v", "libx264", "-preset", "ultrafast", "-pix_fmt", "yuv420p", "-c:a", "aac", "-b:a", "128k", clip_name ] result = subprocess.run(cmd, capture_output=True, text=True) if result.returncode != 0: return None, f"FFmpeg Error in Clip {i}: {result.stderr}" processed_clips.append(f"file '{clip_name}'") # 2. Create the list file for concatenation with open(clip_list_path, "w") as f: f.write("\n".join(processed_clips)) # 3. Merge clips # Added -safe 0 and absolute pathing concat_cmd = [ "ffmpeg", "-y", "-f", "concat", "-safe", "0", "-i", clip_list_path, "-c", "copy", output_filename ] result_merge = subprocess.run(concat_cmd, capture_output=True, text=True) if result_merge.returncode != 0: return None, f"Merge Error: {result_merge.stderr}" if not os.path.exists(output_filename): return None, "File was not created. Check logs." return output_filename, f"Success! {len(sorted_videos)} clips combined." except Exception as e: return None, f"Python Error: {str(e)}" # --- Gradio Interface --- with gr.Interface( fn=process_and_stitch, inputs=[ gr.File(file_count="multiple", label="Upload Video Clips (00.mp4, 01.mp4...)") ], outputs=[ gr.Video(label="Final TikTok Video"), gr.Textbox(label="Status") ], title="TikTok Video Stitcher (Square Style)", description="Upload clips named 00.mp4, 01.mp4 etc. It will make them square and join them." ) as app: app.launch()