File size: 8,421 Bytes
bf6265f
 
 
19eeaab
e396863
19eeaab
bf6265f
 
 
 
 
19eeaab
 
 
 
bf6265f
 
 
 
 
 
19eeaab
bf6265f
19eeaab
 
 
bf6265f
 
 
 
 
19eeaab
 
 
 
 
 
 
 
bf6265f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19eeaab
 
 
 
 
 
 
 
bf6265f
 
 
 
19eeaab
bf6265f
 
19eeaab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a2af975
19eeaab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f026bf2
19eeaab
 
 
 
 
 
 
 
 
 
 
e2705da
 
 
19eeaab
 
 
 
 
 
 
 
bf6265f
19eeaab
 
 
 
 
 
 
 
e2705da
 
 
 
 
 
19eeaab
 
 
bf6265f
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
import gradio as gr
import os, uuid, time
from gradio_client import Client, handle_file
from moviepy.editor import VideoFileClip

# ── config ───────────────────────────────────────────────────────────
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)

UTM = "utm_source=hugging_face_space&utm_medium=banner&utm_campaign=pro_cta"
PRO_URL = f"https://www.face-swap.co/?{UTM}"

# ── helpers ──────────────────────────────────────────────────────────
def preprocess_video(path: str, target_fps: int = 12,
                     target_size: int = 800, target_len: int = 4) -> str:
    clip = VideoFileClip(path)
    if clip.duration > target_len:
        clip = clip.subclip(0, target_len)
    w, h = clip.size
    clip = clip.resize(width=target_size) if w >= h else clip.resize(height=target_size)
    clip = clip.set_fps(target_fps)
    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):
    # Pre-run nudge (small)
    gr.Warning(
        f'Skip the line β€” HD, no watermark, priority queue at '
        f'<a href="https://www.face-swap.co/?utm_source=hfspace_deepfakevideo&utm_medium=warning" target="_blank" rel="noopener">face-swap.co</a>'
    )

    if gender == "all":
        gender = None

    try:
        pre_video = preprocess_video(input_video)
        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"
        )
        while not job.done():
            time.sleep(5)

        if not job.status().success:
            return None

        # Post-success modal (big)
        gr.Info(
            f"✨ Your preview is ready.<br>"
            f"<strong>Get HD</strong> (4Γ— quality, no watermark, priority) β€” "
            f'<a href="https://www.face-swap.co/?utm_source=hfspace_deepfakevideo&utm_medium=info" target="_blank" rel="noopener">Upgrade on face-swap.co</a>',
            duration=8
        )

        video_path = job.outputs()[0]["video"]
        return video_path

    except Exception as e:
        gr.Error(f"Generation failed: {e}")
        return None

def open_side():  # tiny helper
    return gr.Sidebar(open=True)

# ── UI (Blocks) ──────────────────────────────────────────────────────
CUSTOM_CSS = """
.sticky-cta {
  position: sticky; top: 0; z-index: 1000;
  background: #a5b4fc;
  color: #0f172a;
  padding: 10px 14px;
  text-align: center;
  border-bottom: 1px solid #333;
  display: block;                 /* full-width clickable */
  text-decoration: none;          /* remove underline */
  cursor: pointer;
}
.sticky-cta:hover { filter: brightness(0.97); }
.sticky-cta .pill { background:#4f46e5; color:#fff; padding:4px 10px; border-radius:999px; margin-left:10px; }
.sticky-cta .cta-link { font-weight:600; text-decoration: underline; }



/* floating bottom promo */
.bottom-promo {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: 16px; z-index: 1001; background:#0b0b0b; color:#fff;
  border: 1px solid #2a2a2a; border-radius: 12px; padding: 10px 14px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.3);
}
.bottom-promo a { color:#4ea1ff; text-decoration:none; font-weight:600; }

/* big CTA button */
.upgrade-btn { width: 100%; font-size: 16px; padding: 10px 14px; }

/* hero markdown centering + larger heading */
#hero-md {
  text-align: center;
}
#hero-md h3, /* standard markdown h3 */
#hero-md .prose h3 { /* some gradio themes wrap markdown with .prose */
  font-size: 2.1rem;   /* ~34px */
  line-height: 1.2;
  font-weight: 800;
  margin-bottom: 0.25rem;
}
#hero-md p,
#hero-md .prose p {
  font-size: 1.05rem;  /* slightly larger body text */
}

"""

with gr.Blocks(title="Video Face Swap", theme=gr.themes.Soft(), css=CUSTOM_CSS) as demo:
    # Sticky banner
    gr.HTML(
        f"""<a class="sticky-cta" href="https://www.face-swap.co/?utm_source=hfspace_deepfakevideo&utm_medium=banner" target="_blank" rel="noopener"
              aria-label="Upgrade to Pro on face-swap.co">
              ⚑ <strong>Upgrade to HD</strong> β€” priority queue & no duration limit!
              <span class="pill">GPU</span>
            </a>"""
    )


    gr.Markdown(
        f"""
        ### Deep Fake Video (Preview)
        [face-swap.co](https://www.face-swap.co/?utm_source=hfspace_deepfakevideo&utm_medium=subtitle)
        
        **Free preview** is downsampled to 800px β€’ 4s β€’ 12fps to reduce wait time.  
        Want full-length **HD deep fake video** with GPU speed? **[Go Pro β†—](https://www.face-swap.co/?utm_source=hfspace_deepfakevideo&utm_medium=go_pro)**
        """,
        elem_id="hero-md"
    )


    with gr.Row():
        with gr.Column(scale=5):
            
            in_img  = gr.Image(type="filepath", label="Source Image")
            in_vid  = gr.Video(label="Target Video")
            in_gen  = gr.Radio(choices=["all","female","male"], value="all", label="Gender")

            go = gr.Button("Generate Preview", variant="primary")
            pro = gr.Button("⚑ Upgrade to HD on face-swap.co", elem_classes=["upgrade-btn"])

        with gr.Column(scale=5):
            out_vid = gr.Video(label="Result")

    gr.Examples(
        examples=[["elon.png", "ironman.mp4", "all"], ["bella.jpg", "wizard.mp4", "all"]],
        inputs=[in_img, in_vid, in_gen],
        outputs=[out_vid],
        fn=generate,            # precompute + cache example output
        cache_examples=True,    # store the result on build so it loads instantly
        run_on_click=True,      # clicking the example triggers generate()
        label="Try an example"
    )

    # Sidebar CTA
    with gr.Sidebar(open=False) as side:
        gr.Markdown("### Upgrade to HD 1920x1080\n- 4Γ— quality\n- Priority queue\n- 1-5 minute video duration\n- GPU speed")
        pro_paypergen = gr.Button("Pay per Generation", variant="primary")
        pro_subscription = gr.Button("Monthly Subscription", variant="primary")
        pro_api = gr.Button("Face Swap API", variant="primary")

    # Floating bottom promo
    gr.HTML(
        f'<div class="bottom-promo">'
        f'Want HD & no duration limits? <a href="https://www.face-swap.co/?utm_source=hfspace_deepfakevideo&utm_medium=upgrade" target="_blank" rel="noopener">Upgrade</a>'
        f'</div>'
    )

    
    go.click(fn=open_side, inputs=None, outputs=side, queue=False)  # fire instantly

    # Wire events
    go.click(fn=generate, inputs=[in_img, in_vid, in_gen], outputs=out_vid)

    # Open Pro in new tab via JS (no Python call)
    pro.click(fn=None, inputs=None, outputs=None,
              js=f"()=>window.open('https://www.face-swap.co/?utm_source=hfspace_deepfakevideo&utm_medium=upgrade_to_hd','_blank')")
    pro_paypergen.click(fn=None, inputs=None, outputs=None,
               js=f"()=>window.open('https://www.face-swap.co/?utm_source=hfspace_deepfakevideo&utm_medium=sidebar_paypergen','_blank')")
    pro_subscription.click(fn=None, inputs=None, outputs=None,
               js=f"()=>window.open('https://www.face-swap.co/?utm_source=hfspace_deepfakevideo&utm_medium=sidebar_subscription','_blank')")
    pro_api.click(fn=None, inputs=None, outputs=None,
               js=f"()=>window.open('https://www.face-swap.co/?utm_source=hfspace_deepfakevideo&utm_medium=sidebar_api','_blank')")

    # Queue for long jobs + to ensure alerts appear as modals
    demo.queue()

if __name__ == "__main__":
    demo.launch()