Spaces:
Sleeping
Sleeping
| 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() |