Spaces:
Running
Running
import gradio as gr | |
import os, uuid, time | |
from gradio_client import Client, handle_file | |
from moviepy.editor import VideoFileClip # NEW | |
hf_token = os.environ.get("TOKEN") | |
output_dir = "uploads/output" | |
os.makedirs(output_dir, exist_ok=True) | |
client = Client("tonyassi/vfs2-cpu", hf_token=hf_token, download_files=output_dir) | |
# ββ helper βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
def preprocess_video(path: str, target_fps: int = 12, | |
target_size: int = 800, target_len: int = 4) -> str: | |
""" | |
Returns a path to a temp-file that is | |
β€ target_len seconds, target_fps FPS, | |
and resized so the longest side == target_size. | |
""" | |
clip = VideoFileClip(path) | |
# 1) trim | |
if clip.duration > target_len: | |
clip = clip.subclip(0, target_len) | |
# 2) resize (longest side) | |
w, h = clip.size | |
if w >= h: | |
clip = clip.resize(width=target_size) | |
else: | |
clip = clip.resize(height=target_size) | |
# 3) FPS | |
clip = clip.set_fps(target_fps) | |
# 4) write to temp file | |
out_path = os.path.join( | |
output_dir, f"pre_{uuid.uuid4().hex}.mp4" | |
) | |
clip.write_videofile( | |
out_path, | |
codec="libx264", | |
audio_codec="aac", | |
fps=target_fps, | |
verbose=False, | |
logger=None | |
) | |
clip.close() | |
return out_path | |
# ββ main generate ββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
def generate(input_image, input_video, gender): | |
gr.Warning('Skip the line, generate 1-5 minute HD face swap videos at <a href="https://www.face-swap.co/" target="_blank" style="color:#007bff; text-decoration:none;">face-swap.co</a>') | |
if (gender=="all"): gender = None | |
try: | |
# β’ preprocess video locally | |
pre_video = preprocess_video(input_video) | |
# β’ submit job to remote Space | |
job = client.submit( | |
input_image=handle_file(input_image), | |
input_video={"video": handle_file(pre_video)}, | |
device='cpu', | |
selector='many', | |
gender=gender, | |
race=None, | |
order=None, | |
api_name="/predict" | |
) | |
# wait for completion | |
while not job.done(): | |
print("Waiting for remote job to finish...") | |
time.sleep(5) | |
if not job.status().success: | |
print("Job failed or was cancelled:", job.status()) | |
return None | |
video_path = job.outputs()[0]["video"] | |
return video_path | |
except Exception as e: | |
print("Error occurred:", e) | |
return None | |
# ββ Gradio interface (unchanged) βββββββββββββββββββββββββββββββββββββ | |
demo = gr.Interface( | |
fn=generate, | |
title="Video Face Swap", | |
description=""" | |
### [face-swap.co](https://www.face-swap.co/) | |
***Skip the line, generate 1-5 minute HD videos starting at $3.00*** | |
--- | |
Swaps the source image face onto the target video. Videos get downsampled to 800 pixels, 4 sec, and 12 fps to cut down render time (~5 minutes). Please β€οΈ this Space. | |
""", | |
inputs=[ | |
gr.Image(type="filepath", label="Source Image"), | |
gr.Video(label="Target Video"), | |
gr.Radio(choices=["all", "female", "male"], label="Gender", value="all"), | |
], | |
outputs=gr.Video(label="Result"), | |
flagging_mode="never", | |
examples=[['bella.jpg', 'wizard.mp4', 'all']], | |
cache_examples=True, | |
) | |
if __name__ == "__main__": | |
demo.launch() |