tonyassi commited on
Commit
d101def
Β·
verified Β·
1 Parent(s): fd98488

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +200 -0
app.py ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os, uuid, time
3
+ from gradio_client import Client, handle_file
4
+ from moviepy.editor import VideoFileClip
5
+
6
+ # ── config ───────────────────────────────────────────────────────────
7
+ hf_token = os.environ.get("TOKEN")
8
+ output_dir = "uploads/output"
9
+ os.makedirs(output_dir, exist_ok=True)
10
+ client = Client("tonyassi/vfs2-cpu", hf_token=hf_token, download_files=output_dir)
11
+
12
+ UTM = "utm_source=hugging_face_space&utm_medium=banner&utm_campaign=pro_cta"
13
+ PRO_URL = f"https://www.face-swap.co/?{UTM}"
14
+
15
+ # ── helpers ──────────────────────────────────────────────────────────
16
+ def preprocess_video(path: str, target_fps: int = 12,
17
+ target_size: int = 800, target_len: int = 4) -> str:
18
+ clip = VideoFileClip(path)
19
+ if clip.duration > target_len:
20
+ clip = clip.subclip(0, target_len)
21
+ w, h = clip.size
22
+ clip = clip.resize(width=target_size) if w >= h else clip.resize(height=target_size)
23
+ clip = clip.set_fps(target_fps)
24
+ out_path = os.path.join(output_dir, f"pre_{uuid.uuid4().hex}.mp4")
25
+ clip.write_videofile(out_path, codec="libx264", audio_codec="aac",
26
+ fps=target_fps, verbose=False, logger=None)
27
+ clip.close()
28
+ return out_path
29
+
30
+ # ── main generate ────────────────────────────────────────────────────
31
+ def generate(input_image, input_video, gender):
32
+ # Pre-run nudge (small)
33
+ gr.Warning(
34
+ f'Skip the line β€” HD, no watermark, priority queue at '
35
+ f'<a href="https://www.face-swap.co/?utm_source=hugging_face_space&utm_medium=warning" target="_blank" rel="noopener">face-swap.co</a>'
36
+ )
37
+
38
+ if gender == "all":
39
+ gender = None
40
+
41
+ try:
42
+ pre_video = preprocess_video(input_video)
43
+ job = client.submit(
44
+ input_image=handle_file(input_image),
45
+ input_video={"video": handle_file(pre_video)},
46
+ device='cpu',
47
+ selector='many',
48
+ gender=gender,
49
+ race=None,
50
+ order=None,
51
+ api_name="/predict"
52
+ )
53
+ while not job.done():
54
+ time.sleep(5)
55
+
56
+ if not job.status().success:
57
+ return None
58
+
59
+ # Post-success modal (big)
60
+ gr.Info(
61
+ f"✨ Your preview is ready.<br>"
62
+ f"<strong>Get HD</strong> (4Γ— quality, no watermark, priority) β€” "
63
+ f'<a href="https://www.face-swap.co/?utm_source=hugging_face_space&utm_medium=info" target="_blank" rel="noopener">Upgrade on face-swap.co</a>',
64
+ duration=8
65
+ )
66
+
67
+ video_path = job.outputs()[0]["video"]
68
+ return video_path
69
+
70
+ except Exception as e:
71
+ gr.Error(f"Generation failed: {e}")
72
+ return None
73
+
74
+ def open_side(): # tiny helper
75
+ return gr.Sidebar(open=True)
76
+
77
+ # ── UI (Blocks) ──────────────────────────────────────────────────────
78
+ CUSTOM_CSS = """
79
+ .sticky-cta {
80
+ position: sticky; top: 0; z-index: 1000;
81
+ background: #a5b4fc;
82
+ color: #0f172a;
83
+ padding: 10px 14px;
84
+ text-align: center;
85
+ border-bottom: 1px solid #333;
86
+ display: block; /* full-width clickable */
87
+ text-decoration: none; /* remove underline */
88
+ cursor: pointer;
89
+ }
90
+ .sticky-cta:hover { filter: brightness(0.97); }
91
+ .sticky-cta .pill { background:#4f46e5; color:#fff; padding:4px 10px; border-radius:999px; margin-left:10px; }
92
+ .sticky-cta .cta-link { font-weight:600; text-decoration: underline; }
93
+
94
+
95
+
96
+ /* floating bottom promo */
97
+ .bottom-promo {
98
+ position: fixed; left: 50%; transform: translateX(-50%);
99
+ bottom: 16px; z-index: 1001; background:#0b0b0b; color:#fff;
100
+ border: 1px solid #2a2a2a; border-radius: 12px; padding: 10px 14px;
101
+ box-shadow: 0 8px 24px rgba(0,0,0,0.3);
102
+ }
103
+ .bottom-promo a { color:#4ea1ff; text-decoration:none; font-weight:600; }
104
+
105
+ /* big CTA button */
106
+ .upgrade-btn { width: 100%; font-size: 16px; padding: 10px 14px; }
107
+
108
+ /* hero markdown centering + larger heading */
109
+ #hero-md {
110
+ text-align: center;
111
+ }
112
+ #hero-md h3, /* standard markdown h3 */
113
+ #hero-md .prose h3 { /* some gradio themes wrap markdown with .prose */
114
+ font-size: 2.1rem; /* ~34px */
115
+ line-height: 1.2;
116
+ font-weight: 800;
117
+ margin-bottom: 0.25rem;
118
+ }
119
+ #hero-md p,
120
+ #hero-md .prose p {
121
+ font-size: 1.05rem; /* slightly larger body text */
122
+ }
123
+
124
+ """
125
+
126
+ with gr.Blocks(title="Video Face Swap", theme=gr.themes.Soft(), css=CUSTOM_CSS) as demo:
127
+ # Sticky banner
128
+ gr.HTML(
129
+ f"""<a class="sticky-cta" href="https://www.face-swap.co/?utm_source=hugging_face_space&utm_medium=banner" target="_blank" rel="noopener"
130
+ aria-label="Upgrade to Pro on face-swap.co">
131
+ ⚑ <strong>Upgrade to HD</strong> β€” priority queue & no duration limit!
132
+ <span class="pill">GPU</span>
133
+ </a>"""
134
+ )
135
+
136
+
137
+ gr.Markdown(
138
+ f"""
139
+ ### Video Face Swap (Preview)
140
+ [face-swap.co](https://www.face-swap.co/?utm_source=hugging_face_space&utm_medium=subtitle)
141
+
142
+ **Free preview** is downsampled to 800px β€’ 4s β€’ 12fps to reduce wait time.
143
+ Want full-length **HD deep fake video** with GPU speed? **[Go Pro β†—](https://www.face-swap.co/?utm_source=hugging_face_space&utm_medium=go_pro)**
144
+ """,
145
+ elem_id="hero-md"
146
+ )
147
+
148
+
149
+ with gr.Row():
150
+ with gr.Column(scale=5):
151
+
152
+ in_img = gr.Image(type="filepath", label="Source Image")
153
+ in_vid = gr.Video(label="Target Video")
154
+ in_gen = gr.Radio(choices=["all","female","male"], value="all", label="Gender")
155
+
156
+ go = gr.Button("Generate Preview", variant="primary")
157
+ pro = gr.Button("⚑ Upgrade to HD on face-swap.co", elem_classes=["upgrade-btn"])
158
+
159
+ with gr.Column(scale=5):
160
+ out_vid = gr.Video(label="Result")
161
+
162
+ gr.Examples(
163
+ examples=[["bella.jpg", "wizard.mp4", "all"]],
164
+ inputs=[in_img, in_vid, in_gen],
165
+ outputs=[out_vid],
166
+ fn=generate, # precompute + cache example output
167
+ cache_examples=True, # store the result on build so it loads instantly
168
+ run_on_click=True, # clicking the example triggers generate()
169
+ label="Try an example"
170
+ )
171
+
172
+ # Sidebar CTA
173
+ with gr.Sidebar(open=False) as side:
174
+ gr.Markdown("### Upgrade to HD 1920x1080\n- 4Γ— quality\n- Priority queue\n- 1-5 minute video duration\n- GPU speed")
175
+ pro2 = gr.Button("Open Pro Checkout", variant="primary")
176
+
177
+ # Floating bottom promo
178
+ gr.HTML(
179
+ f'<div class="bottom-promo">'
180
+ f'Want HD & no duration limits? <a href="https://www.face-swap.co/?utm_source=hugging_face_space&utm_medium=upgrade" target="_blank" rel="noopener">Upgrade</a>'
181
+ f'</div>'
182
+ )
183
+
184
+
185
+ go.click(fn=open_side, inputs=None, outputs=side, queue=False) # fire instantly
186
+
187
+ # Wire events
188
+ go.click(fn=generate, inputs=[in_img, in_vid, in_gen], outputs=out_vid)
189
+
190
+ # Open Pro in new tab via JS (no Python call)
191
+ pro.click(fn=None, inputs=None, outputs=None,
192
+ js=f"()=>window.open('https://www.face-swap.co/?utm_source=hugging_face_space&utm_medium=upgrade_to_hd','_blank')")
193
+ pro2.click(fn=None, inputs=None, outputs=None,
194
+ js=f"()=>window.open('https://www.face-swap.co/?utm_source=hugging_face_space&utm_medium=sidebar','_blank')")
195
+
196
+ # Queue for long jobs + to ensure alerts appear as modals
197
+ demo.queue()
198
+
199
+ if __name__ == "__main__":
200
+ demo.launch()