Spaceuser777 commited on
Commit
b109e92
Β·
verified Β·
1 Parent(s): 7a54f10

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +246 -0
app.py ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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=hfspace_deepfakevideo&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=hfspace_deepfakevideo&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
+ /* centered, single-label API CTA */
94
+ .api-cta-wrap { text-align:center; margin-top:10px; }
95
+ .api-cta-hero {
96
+ display:inline-flex; align-items:center; gap:10px;
97
+ padding:10px 14px; border-radius:14px;
98
+ background: linear-gradient(90deg,#0ea5e9 0%, #a8a9de 100%);
99
+ color:#fff; font-weight:800; letter-spacing:0.1px;
100
+ box-shadow: 0 6px 22px rgba(99,102,241,0.35);
101
+ border: 1px solid rgba(255,255,255,0.22);
102
+ text-decoration:none;
103
+ }
104
+ .api-cta-hero:hover { filter:brightness(1.05); transform: translateY(-1px); transition: all .15s ease; }
105
+ .api-cta-hero .new {
106
+ background:#fff; color:#0ea5e9; font-weight:900;
107
+ padding:2px 8px; border-radius:999px; font-size:12px; line-height:1;
108
+ }
109
+ .api-cta-hero .txt { font-weight:800; }
110
+ .api-cta-hero .chev { opacity:.95; }
111
+ @media (max-width: 520px){
112
+ .api-cta-hero { padding:9px 12px; gap:8px; font-size:14px; }
113
+ .api-cta-hero .new { display:none; } /* keep it tidy on phones */
114
+ }
115
+ /* floating bottom promo */
116
+ .bottom-promo {
117
+ position: fixed; left: 50%; transform: translateX(-50%);
118
+ bottom: 16px; z-index: 1001; background:#0b0b0b; color:#fff;
119
+ border: 1px solid #2a2a2a; border-radius: 12px; padding: 10px 14px;
120
+ box-shadow: 0 8px 24px rgba(0,0,0,0.3);
121
+ }
122
+ .bottom-promo a { color:#4ea1ff; text-decoration:none; font-weight:600; }
123
+ /* big CTA button */
124
+ .upgrade-btn { width: 100%; font-size: 16px; padding: 10px 14px; }
125
+ /* hero markdown centering + larger heading */
126
+ #hero-md {
127
+ text-align: center;
128
+ }
129
+ #hero-md h3, /* standard markdown h3 */
130
+ #hero-md .prose h3 { /* some gradio themes wrap markdown with .prose */
131
+ font-size: 2.1rem; /* ~34px */
132
+ line-height: 1.2;
133
+ font-weight: 800;
134
+ margin-bottom: 0.25rem;
135
+ }
136
+ #hero-md p,
137
+ #hero-md .prose p {
138
+ font-size: 1.05rem; /* slightly larger body text */
139
+ }
140
+ """
141
+
142
+ with gr.Blocks(title="Video Face Swap", theme=gr.themes.Soft(), css=CUSTOM_CSS) as demo:
143
+ # Sticky banner
144
+ gr.HTML(
145
+ f"""<a class="sticky-cta" href="https://www.face-swap.co/?utm_source=hfspace_deepfakevideo&utm_medium=banner" target="_blank" rel="noopener"
146
+ aria-label="Upgrade to Pro on face-swap.co">
147
+ ⚑ <strong>Upgrade to HD</strong> β€” priority queue & no duration limit!
148
+ <span class="pill">GPU</span>
149
+ </a>"""
150
+ )
151
+
152
+
153
+ gr.Markdown(
154
+ f"""
155
+ ### Deep Fake Video (Preview)
156
+ [face-swap.co](https://www.face-swap.co/?utm_source=hfspace_deepfakevideo&utm_medium=subtitle)
157
+
158
+ **Free preview** is downsampled to 800px β€’ 4s β€’ 12fps to reduce wait time.
159
+ 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)**
160
+ """,
161
+ elem_id="hero-md"
162
+ )
163
+
164
+
165
+ gr.HTML(
166
+ """
167
+ <div class="api-cta-wrap" style="display:flex; justify-content:center; gap:12px; flex-wrap:wrap; margin-top:10px;">
168
+ <a class="api-cta-hero"
169
+ style="background: linear-gradient(90deg,#22c55e 0%, #16a34a 100%);"
170
+ href="https://www.face-swap.co/?utm_source=hfspace_deepfakevideo&utm_medium=hero_upgrade"
171
+ target="_blank" rel="noopener" aria-label="Upgrade to HD (no code)">
172
+ <span class="txt">⚑ Upgrade to HD (No code)</span>
173
+ <span class="chev">β†—</span>
174
+ </a>
175
+
176
+ <a class="api-cta-hero"
177
+ href="https://www.face-swap.co/api?utm_source=hfspace_deepfakevideo&utm_medium=hero_api_new"
178
+ target="_blank" rel="noopener" aria-label="Face Swap API">
179
+ <span class="new">DEV</span>
180
+ <span class="txt">Face Swap API</span>
181
+ <span class="chev">β†—</span>
182
+ </a>
183
+ </div>
184
+
185
+ """
186
+ )
187
+
188
+
189
+ with gr.Row():
190
+ with gr.Column(scale=5):
191
+
192
+ in_img = gr.Image(type="filepath", label="Source Image")
193
+ in_vid = gr.Video(label="Target Video")
194
+ in_gen = gr.Radio(choices=["all","female","male"], value="all", label="Gender")
195
+
196
+ go = gr.Button("Generate Preview", variant="primary")
197
+ pro = gr.Button("⚑ Upgrade to HD on face-swap.co", elem_classes=["upgrade-btn"])
198
+
199
+ with gr.Column(scale=5):
200
+ out_vid = gr.Video(label="Result")
201
+
202
+ gr.Examples(
203
+ examples=[["elon.png", "ironman.mp4", "all"], ["bella.jpg", "wizard.mp4", "all"], ["wife.png", "beach.mp4", "all"]],
204
+ inputs=[in_img, in_vid, in_gen],
205
+ outputs=[out_vid],
206
+ fn=generate, # precompute + cache example output
207
+ cache_examples=True, # store the result on build so it loads instantly
208
+ run_on_click=True, # clicking the example triggers generate()
209
+ label="Try an example"
210
+ )
211
+
212
+ # Sidebar CTA
213
+ with gr.Sidebar(open=False) as side:
214
+ gr.Markdown("### Upgrade to HD 1920x1080\n- 4Γ— quality\n- Priority queue\n- 1-5 minute video duration\n- GPU speed")
215
+ pro_pro = gr.Button("Open Pro Checkout", variant="primary")
216
+ #pro_subscription = gr.Button("Monthly Subscription", variant="primary")
217
+ pro_api = gr.Button("Face Swap API", variant="primary")
218
+
219
+ # Floating bottom promo
220
+ gr.HTML(
221
+ f'<div class="bottom-promo">'
222
+ 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>'
223
+ f'</div>'
224
+ )
225
+
226
+
227
+ go.click(fn=open_side, inputs=None, outputs=side, queue=False) # fire instantly
228
+
229
+ # Wire events
230
+ go.click(fn=generate, inputs=[in_img, in_vid, in_gen], outputs=out_vid)
231
+
232
+ # Open Pro in new tab via JS (no Python call)
233
+ pro.click(fn=None, inputs=None, outputs=None,
234
+ js=f"()=>window.open('https://www.face-swap.co/?utm_source=hfspace_deepfakevideo&utm_medium=upgrade_to_hd','_blank')")
235
+ pro_pro.click(fn=None, inputs=None, outputs=None,
236
+ js=f"()=>window.open('https://www.face-swap.co/?utm_source=hfspace_deepfakevideo&utm_medium=sidebar_pro','_blank')")
237
+ #pro_subscription.click(fn=None, inputs=None, outputs=None,
238
+ # js=f"()=>window.open('https://www.face-swap.co/?utm_source=hfspace_deepfakevideo&utm_medium=sidebar_subscription','_blank')")
239
+ pro_api.click(fn=None, inputs=None, outputs=None,
240
+ js=f"()=>window.open('https://www.face-swap.co/api?utm_source=hfspace_deepfakevideo&utm_medium=sidebar_api','_blank')")
241
+
242
+ # Queue for long jobs + to ensure alerts appear as modals
243
+ demo.queue()
244
+
245
+ if __name__ == "__main__":
246
+ demo.launch()