LogicGoInfotechSpaces commited on
Commit
6968b9f
·
1 Parent(s): 6ee66f7

Clean up project: remove unnecessary files and fix insightface installation

Browse files

- Fixed insightface installation: install from git directly for better compatibility
- Removed test files: images, videos, database files
- Removed duplicate/unused files: app.py, app2.py, app3.py, del.py, run.py, api/main.py
- Removed config files: apt.txt, gitattributes, mypy.ini, requirements2.txt
- Added gfpgan dependency for face enhancement feature
- Simplified Dockerfile with better error handling

Files changed (13) hide show
  1. DeepFakeAI/feed.db +0 -0
  2. DeepFakeAI/images.db +0 -0
  3. Dockerfile +10 -7
  4. api/main.py +0 -374
  5. app.py +0 -267
  6. app2.py +0 -4
  7. app3.py +0 -114
  8. apt.txt +0 -3
  9. del.py +0 -9
  10. gitattributes +0 -38
  11. mypy.ini +0 -7
  12. requirements2.txt +0 -18
  13. run.py +0 -6
DeepFakeAI/feed.db DELETED
File without changes
DeepFakeAI/images.db DELETED
File without changes
Dockerfile CHANGED
@@ -66,7 +66,8 @@ RUN pip install --no-cache-dir \
66
  tqdm==4.65.0 \
67
  Pillow \
68
  imageio-ffmpeg \
69
- huggingface_hub>=0.23.0
 
70
 
71
  # Install OpenCV first (needed by insightface)
72
  RUN pip install --no-cache-dir opencv-python-headless
@@ -87,13 +88,15 @@ RUN python -c "import onnxruntime; print('ONNX Runtime version:', onnxruntime.__
87
  # Install moviepy after other dependencies
88
  RUN pip install --no-cache-dir moviepy==1.0.3
89
 
90
- # Install insightface last - try multiple approaches with better error handling
91
- RUN pip install --no-cache-dir --no-build-isolation insightface==0.7.3 || \
92
- pip install --no-cache-dir "insightface>=0.7.0,<0.8.0" || \
93
  pip install --no-cache-dir insightface || \
94
- pip install --no-cache-dir --no-build-isolation git+https://github.com/deepinsight/insightface.git@v0.7.3 || \
95
- pip install --no-cache-dir git+https://github.com/deepinsight/insightface.git || \
96
- (echo "ERROR: All insightface installation methods failed. Check build logs above for details." && exit 1)
 
 
 
97
 
98
  # Copy only essential application files
99
  COPY --chown=user:user api_server.py /app/
 
66
  tqdm==4.65.0 \
67
  Pillow \
68
  imageio-ffmpeg \
69
+ huggingface_hub>=0.23.0 \
70
+ gfpgan
71
 
72
  # Install OpenCV first (needed by insightface)
73
  RUN pip install --no-cache-dir opencv-python-headless
 
88
  # Install moviepy after other dependencies
89
  RUN pip install --no-cache-dir moviepy==1.0.3
90
 
91
+ # Install insightface - install from git directly for better compatibility
92
+ RUN pip install --no-cache-dir --no-build-isolation git+https://github.com/deepinsight/insightface.git || \
 
93
  pip install --no-cache-dir insightface || \
94
+ pip install --no-cache-dir "insightface>=0.7.0" || \
95
+ (echo "ERROR: Failed to install insightface" && exit 1)
96
+
97
+ # Verify insightface installation
98
+ RUN python -c "import insightface; print('InsightFace installed successfully')" || \
99
+ (echo "ERROR: InsightFace verification failed" && exit 1)
100
 
101
  # Copy only essential application files
102
  COPY --chown=user:user api_server.py /app/
api/main.py DELETED
@@ -1,374 +0,0 @@
1
- from fastapi import FastAPI, File, UploadFile, HTTPException, BackgroundTasks
2
- from fastapi.middleware.cors import CORSMiddleware
3
- from pydantic import BaseModel
4
- from typing import Optional, List
5
- import os
6
- import uuid
7
- import asyncio
8
- from datetime import datetime
9
- import motor.motor_asyncio
10
- from bson import ObjectId
11
- import json
12
- import shutil
13
- from pathlib import Path
14
- from fastapi.responses import FileResponse, StreamingResponse, JSONResponse
15
-
16
- # Import face swap functionality
17
- import sys
18
- sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
19
- from app import _run_local_faceswap
20
-
21
- app = FastAPI(title="Face Swap Video API", version="1.0.0")
22
-
23
- # CORS middleware
24
- app.add_middleware(
25
- CORSMiddleware,
26
- allow_origins=["*"],
27
- allow_credentials=True,
28
- allow_methods=["*"],
29
- allow_headers=["*"],
30
- )
31
-
32
- # MongoDB connection
33
- MONGODB_URL = os.getenv("MONGODB_URL", "mongodb+srv://itishalogicgo_db_user:HR837xi0B9yh2vZK@cluster0.jeeytpz.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0")
34
- DATABASE_NAME = "face_swap_video"
35
- client = motor.motor_asyncio.AsyncIOMotorClient(MONGODB_URL)
36
- db = client[DATABASE_NAME]
37
-
38
- # Collections
39
- source_images_collection = db["source_images"]
40
- target_videos_collection = db["target_videos"]
41
- result_videos_collection = db["result_videos"]
42
- jobs_collection = db["processing_jobs"]
43
-
44
- # Upload directories
45
- UPLOAD_DIR = Path("uploads")
46
- SOURCE_IMAGES_DIR = UPLOAD_DIR / "source_images"
47
- TARGET_VIDEOS_DIR = UPLOAD_DIR / "target_videos"
48
- RESULT_VIDEOS_DIR = UPLOAD_DIR / "result_videos"
49
-
50
- # Create directories
51
- for dir_path in [UPLOAD_DIR, SOURCE_IMAGES_DIR, TARGET_VIDEOS_DIR, RESULT_VIDEOS_DIR]:
52
- dir_path.mkdir(parents=True, exist_ok=True)
53
-
54
- # Pydantic models
55
- class SourceImageResponse(BaseModel):
56
- id: str
57
- filename: str
58
- file_path: str
59
- uploaded_at: datetime
60
- status: str
61
-
62
- class TargetVideoResponse(BaseModel):
63
- id: str
64
- filename: str
65
- file_path: str
66
- uploaded_at: datetime
67
- status: str
68
-
69
- class ResultVideoResponse(BaseModel):
70
- id: str
71
- source_image_id: str
72
- target_video_id: str
73
- result_file_path: str
74
- created_at: datetime
75
- status: str
76
- processing_time: Optional[float] = None
77
-
78
- class FaceSwapRequest(BaseModel):
79
- source_image_id: str
80
- target_video_id: str
81
-
82
- class JobStatus(BaseModel):
83
- job_id: str
84
- status: str
85
- progress: Optional[float] = None
86
- result_video_id: Optional[str] = None
87
- result_video_url: Optional[str] = None # HTTPS download URL
88
- error: Optional[str] = None
89
-
90
- # Base URL for generating download links
91
- BASE_URL = os.getenv("BASE_URL", "https://logicgoinfotechspaces-face-swap-video.hf.space")
92
-
93
- def get_result_video_url(result_video_id: str) -> str:
94
- """Generate HTTPS download URL for result video"""
95
- return f"{BASE_URL}/api/result-video/{result_video_id}"
96
-
97
- # Helper functions
98
- def save_file_to_disk(file: UploadFile, directory: Path) -> str:
99
- """Save uploaded file to disk and return the file path"""
100
- file_extension = Path(file.filename).suffix
101
- unique_filename = f"{uuid.uuid4().hex}{file_extension}"
102
- file_path = directory / unique_filename
103
-
104
- with open(file_path, "wb") as buffer:
105
- shutil.copyfileobj(file.file, buffer)
106
-
107
- return str(file_path)
108
-
109
- async def process_face_swap(job_id: str, source_image_path: str, target_video_path: str):
110
- """Background task to process face swap"""
111
- try:
112
- # Update job status to processing
113
- await jobs_collection.update_one(
114
- {"job_id": job_id},
115
- {"$set": {"status": "processing", "progress": 0.0}}
116
- )
117
-
118
- # Run face swap
119
- result_path = _run_local_faceswap(source_image_path, target_video_path)
120
-
121
- if result_path and os.path.exists(result_path):
122
- # Save result to MongoDB
123
- result_doc = {
124
- "source_image_path": source_image_path,
125
- "target_video_path": target_video_path,
126
- "result_file_path": result_path,
127
- "created_at": datetime.utcnow(),
128
- "status": "completed",
129
- "job_id": job_id
130
- }
131
-
132
- result = await result_videos_collection.insert_one(result_doc)
133
- result_video_id = str(result.inserted_id)
134
-
135
- # Update job status to completed
136
- await jobs_collection.update_one(
137
- {"job_id": job_id},
138
- {"$set": {
139
- "status": "completed",
140
- "progress": 100.0,
141
- "result_video_id": result_video_id,
142
- "result_video_url": get_result_video_url(result_video_id)
143
- }}
144
- )
145
- else:
146
- # Update job status to failed
147
- await jobs_collection.update_one(
148
- {"job_id": job_id},
149
- {"$set": {
150
- "status": "failed",
151
- "error": "Face swap processing failed"
152
- }}
153
- )
154
-
155
- except Exception as e:
156
- # Update job status to failed
157
- await jobs_collection.update_one(
158
- {"job_id": job_id},
159
- {"$set": {
160
- "status": "failed",
161
- "error": str(e)
162
- }}
163
- )
164
-
165
- # API Endpoints
166
-
167
- @app.post("/api/source-image", response_model=SourceImageResponse)
168
- async def upload_source_image(file: UploadFile = File(...)):
169
- """Upload and store source image in MongoDB"""
170
- if not file.content_type.startswith('image/'):
171
- raise HTTPException(status_code=400, detail="File must be an image")
172
-
173
- try:
174
- # Save file to disk
175
- file_path = save_file_to_disk(file, SOURCE_IMAGES_DIR)
176
-
177
- # Store metadata in MongoDB
178
- doc = {
179
- "filename": file.filename,
180
- "file_path": file_path,
181
- "uploaded_at": datetime.utcnow(),
182
- "status": "uploaded",
183
- "content_type": file.content_type,
184
- "file_size": os.path.getsize(file_path)
185
- }
186
-
187
- result = await source_images_collection.insert_one(doc)
188
-
189
- return SourceImageResponse(
190
- id=str(result.inserted_id),
191
- filename=file.filename,
192
- file_path=file_path,
193
- uploaded_at=doc["uploaded_at"],
194
- status=doc["status"]
195
- )
196
-
197
- except Exception as e:
198
- raise HTTPException(status_code=500, detail=f"Error uploading source image: {str(e)}")
199
-
200
- @app.post("/api/target-video", response_model=TargetVideoResponse)
201
- async def upload_target_video(file: UploadFile = File(...)):
202
- """Upload and store target video in MongoDB"""
203
- if not file.content_type.startswith('video/'):
204
- raise HTTPException(status_code=400, detail="File must be a video")
205
-
206
- try:
207
- # Save file to disk
208
- file_path = save_file_to_disk(file, TARGET_VIDEOS_DIR)
209
-
210
- # Store metadata in MongoDB
211
- doc = {
212
- "filename": file.filename,
213
- "file_path": file_path,
214
- "uploaded_at": datetime.utcnow(),
215
- "status": "uploaded",
216
- "content_type": file.content_type,
217
- "file_size": os.path.getsize(file_path)
218
- }
219
-
220
- result = await target_videos_collection.insert_one(doc)
221
-
222
- return TargetVideoResponse(
223
- id=str(result.inserted_id),
224
- filename=file.filename,
225
- file_path=file_path,
226
- uploaded_at=doc["uploaded_at"],
227
- status=doc["status"]
228
- )
229
-
230
- except Exception as e:
231
- raise HTTPException(status_code=500, detail=f"Error uploading target video: {str(e)}")
232
-
233
- @app.post("/api/face-swap", response_model=JobStatus)
234
- async def start_face_swap(request: FaceSwapRequest, background_tasks: BackgroundTasks):
235
- """Start face swap processing"""
236
- try:
237
- # Get source image and target video from MongoDB
238
- source_image = await source_images_collection.find_one({"_id": ObjectId(request.source_image_id)})
239
- target_video = await target_videos_collection.find_one({"_id": ObjectId(request.target_video_id)})
240
-
241
- if not source_image:
242
- raise HTTPException(status_code=404, detail="Source image not found")
243
- if not target_video:
244
- raise HTTPException(status_code=404, detail="Target video not found")
245
-
246
- # Create job record
247
- job_id = str(uuid.uuid4())
248
- job_doc = {
249
- "job_id": job_id,
250
- "source_image_id": request.source_image_id,
251
- "target_video_id": request.target_video_id,
252
- "status": "queued",
253
- "created_at": datetime.utcnow(),
254
- "progress": 0.0
255
- }
256
-
257
- await jobs_collection.insert_one(job_doc)
258
-
259
- # Start background processing
260
- background_tasks.add_task(
261
- process_face_swap,
262
- job_id,
263
- source_image["file_path"],
264
- target_video["file_path"]
265
- )
266
-
267
- return JobStatus(
268
- job_id=job_id,
269
- status="queued",
270
- progress=0.0
271
- )
272
-
273
- except Exception as e:
274
- raise HTTPException(status_code=500, detail=f"Error starting face swap: {str(e)}")
275
-
276
- @app.get("/api/job/{job_id}", response_model=JobStatus)
277
- async def get_job_status(job_id: str):
278
- """Get job status"""
279
- job = await jobs_collection.find_one({"job_id": job_id})
280
- if not job:
281
- raise HTTPException(status_code=404, detail="Job not found")
282
-
283
- result_video_url = None
284
- if job.get("result_video_id"):
285
- result_video_url = get_result_video_url(job["result_video_id"])
286
-
287
- return JobStatus(
288
- job_id=job["job_id"],
289
- status=job["status"],
290
- progress=job.get("progress"),
291
- result_video_id=job.get("result_video_id"),
292
- result_video_url=result_video_url,
293
- error=job.get("error")
294
- )
295
-
296
- @app.get("/api/result-video/{result_video_id}")
297
- async def get_result_video(result_video_id: str):
298
- """Get result video file"""
299
- result = await result_videos_collection.find_one({"_id": ObjectId(result_video_id)})
300
- if not result:
301
- raise HTTPException(status_code=404, detail="Result video not found")
302
-
303
- if not os.path.exists(result["result_file_path"]):
304
- raise HTTPException(status_code=404, detail="Result video file not found")
305
-
306
- return FileResponse(
307
- path=result["result_file_path"],
308
- media_type="video/mp4",
309
- filename=f"face_swap_result_{result_video_id}.mp4"
310
- )
311
-
312
- @app.get("/api/source-images", response_model=List[SourceImageResponse])
313
- async def list_source_images():
314
- """List all source images"""
315
- cursor = source_images_collection.find().sort("uploaded_at", -1)
316
- images = []
317
- async for doc in cursor:
318
- images.append(SourceImageResponse(
319
- id=str(doc["_id"]),
320
- filename=doc["filename"],
321
- file_path=doc["file_path"],
322
- uploaded_at=doc["uploaded_at"],
323
- status=doc["status"]
324
- ))
325
- return images
326
-
327
- @app.get("/api/target-videos", response_model=List[TargetVideoResponse])
328
- async def list_target_videos():
329
- """List all target videos"""
330
- cursor = target_videos_collection.find().sort("uploaded_at", -1)
331
- videos = []
332
- async for doc in cursor:
333
- videos.append(TargetVideoResponse(
334
- id=str(doc["_id"]),
335
- filename=doc["filename"],
336
- file_path=doc["file_path"],
337
- uploaded_at=doc["uploaded_at"],
338
- status=doc["status"]
339
- ))
340
- return videos
341
-
342
- @app.get("/api/result-videos", response_model=List[ResultVideoResponse])
343
- async def list_result_videos():
344
- """List all result videos"""
345
- cursor = result_videos_collection.find().sort("created_at", -1)
346
- results = []
347
- async for doc in cursor:
348
- results.append(ResultVideoResponse(
349
- id=str(doc["_id"]),
350
- source_image_id=doc.get("source_image_path", ""),
351
- target_video_id=doc.get("target_video_path", ""),
352
- result_file_path=doc["result_file_path"],
353
- created_at=doc["created_at"],
354
- status=doc["status"],
355
- processing_time=doc.get("processing_time")
356
- ))
357
- return results
358
-
359
- @app.get("/api/health")
360
- async def api_health():
361
- return {"status": "ok", "time": datetime.utcnow().isoformat()}
362
-
363
- # -------- Optional: GridFS variants (disabled - using file system storage) -------- #
364
- # GridFS endpoints removed - using file system storage instead
365
- # If needed, can be re-enabled with motor's GridFSBucketAsync for async operations
366
-
367
- @app.get("/")
368
- async def root():
369
- """Health check endpoint"""
370
- return {"message": "Face Swap Video API is running", "version": "1.0.0"}
371
-
372
- if __name__ == "__main__":
373
- import uvicorn
374
- uvicorn.run(app, host="0.0.0.0", port=8000)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app.py DELETED
@@ -1,267 +0,0 @@
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
- from typing import Optional
6
- import traceback
7
- import os as _os
8
-
9
- # Ensure sane OMP threads on startup (prevents libgomp warning in Spaces)
10
- try:
11
- _os.environ["OMP_NUM_THREADS"] = "1"
12
- except Exception:
13
- pass
14
-
15
- # Local pipeline imports
16
- import DeepFakeAI.globals as DF_G
17
- from DeepFakeAI import utilities as DF_U
18
- from DeepFakeAI.processors.frame.modules import face_swapper as DF_FS
19
- from huggingface_hub import hf_hub_download
20
-
21
- # ── config ───────────────────────────────────────────────────────────
22
- hf_token = os.environ.get("TOKEN")
23
- backend_space = os.environ.get("BACKEND_SPACE", "tonyassi/vfs2-cpu")
24
- output_dir = "uploads/output"
25
- os.makedirs(output_dir, exist_ok=True)
26
-
27
- # Initialize client to upstream backend Space; handle unauthorized/404 gracefully
28
- client = None
29
- client_init_error = None
30
- try:
31
- client = Client(backend_space, hf_token=hf_token, download_files=output_dir)
32
- except Exception as e:
33
- client_init_error = str(e)
34
- client = None
35
-
36
- UTM = "utm_source=hugging_face_space&utm_medium=banner&utm_campaign=pro_cta"
37
- PRO_URL = f"https://www.face-swap.co/?{UTM}"
38
-
39
- # ── helpers ──────────────────────────────────────────────────────────
40
- def preprocess_video(path: str, target_fps: int = 12,
41
- target_size: int = 800, target_len: int = 4) -> str:
42
- clip = VideoFileClip(path)
43
- if clip.duration > target_len:
44
- clip = clip.subclip(0, target_len)
45
- w, h = clip.size
46
- clip = clip.resize(width=target_size) if w >= h else clip.resize(height=target_size)
47
- clip = clip.set_fps(target_fps)
48
- out_path = os.path.join(output_dir, f"pre_{uuid.uuid4().hex}.mp4")
49
- clip.write_videofile(out_path, codec="libx264", audio_codec="aac",
50
- fps=target_fps, verbose=False, logger=None)
51
- clip.close()
52
- return out_path
53
-
54
- def _run_local_faceswap(source_image_path: str, target_video_path: str) -> Optional[str]:
55
- # Configure defaults for local pipeline
56
- DF_G.source_path = source_image_path
57
- DF_G.target_path = target_video_path
58
- DF_G.output_video_encoder = 'libx264'
59
- DF_G.output_video_quality = 20
60
- DF_G.temp_frame_format = 'png'
61
- DF_G.temp_frame_quality = 95
62
- DF_G.keep_temp = False
63
- DF_G.skip_audio = False
64
- # Face processing options
65
- DF_G.face_recognition = ['many']
66
- DF_G.reference_frame_number = 0
67
- DF_G.execution_thread_count = 2
68
- DF_G.execution_queue_count = 2
69
- # Prefer CUDA (T4 GPU in HF Spaces); will fall back internally if unavailable
70
- DF_G.execution_providers = DF_U.decode_execution_providers(['cuda'])
71
- # Fix invalid OMP thread settings in container
72
- try:
73
- _os.environ["OMP_NUM_THREADS"] = "1"
74
- except Exception:
75
- pass
76
-
77
- # Ensure model exists (try bundled path; if missing, download via hf_hub)
78
- model_dir = DF_U.resolve_relative_path('../.assets/models')
79
- os.makedirs(model_dir, exist_ok=True)
80
- model_path = os.path.join(model_dir, 'inswapper_128.onnx')
81
- if not os.path.exists(model_path):
82
- token = os.environ.get('TOKEN') or os.environ.get('HF_TOKEN')
83
- # Prefer HF mirrors (avoid GitHub 404 in Spaces). Try most reliable first.
84
- for repo_id in [
85
- 'zihaomu/inswapper_128.onnx',
86
- 'linyi/inswapper_128.onnx',
87
- 'banodoco/inswapper_128.onnx',
88
- ]:
89
- try:
90
- model_path = hf_hub_download(repo_id=repo_id, filename='inswapper_128.onnx', token=token)
91
- break
92
- except Exception:
93
- continue
94
- # Expose resolved model path for the frame processor
95
- if os.path.exists(model_path):
96
- os.environ['INSWAPPER_PATH'] = model_path
97
- DF_FS.pre_check()
98
-
99
- # Extract frames
100
- fps = DF_U.detect_fps(target_video_path) or 12.0
101
- DF_U.create_temp(target_video_path)
102
- ok = DF_U.extract_frames(target_video_path, fps)
103
- if not ok:
104
- return None
105
- temp_frames = DF_U.get_temp_frame_paths(target_video_path)
106
- if not temp_frames:
107
- return None
108
-
109
- # Process frames
110
- DF_FS.process_video(source_image_path, temp_frames)
111
-
112
- # Rebuild video and restore audio
113
- if not DF_U.create_video(target_video_path, fps):
114
- return None
115
- out_path = DF_U.normalize_output_path(source_image_path, target_video_path, os.path.join(output_dir, f"out_{uuid.uuid4().hex}.mp4"))
116
- DF_U.restore_audio(target_video_path, out_path)
117
- DF_U.clear_temp(target_video_path)
118
- return out_path
119
-
120
-
121
- # ── main generate ────────────────────────────────────────────────────
122
- def generate(input_image, input_video):
123
- # Minimal mode: no gender selector; if remote client is missing, we fall back to local
124
-
125
- try:
126
- gr.Info("Processing started. This may take 1-3 minutes on first run.")
127
- pre_video = preprocess_video(input_video)
128
-
129
- if client is not None:
130
- job = client.submit(
131
- input_image=handle_file(input_image),
132
- input_video={"video": handle_file(pre_video)},
133
- device='cpu',
134
- selector='many',
135
- gender=None,
136
- race=None,
137
- order=None,
138
- api_name="/predict"
139
- )
140
- while not job.done():
141
- time.sleep(5)
142
- if not job.status().success:
143
- return None
144
- outp = job.outputs()[0]["video"]
145
- gr.Info("Done.")
146
- return outp
147
- else:
148
- # Local fallback
149
- outp = _run_local_faceswap(input_image, pre_video)
150
- if outp:
151
- gr.Info("Done.")
152
- else:
153
- gr.Error("Local processing failed. Check logs for details.")
154
- return outp
155
-
156
- except Exception as e:
157
- gr.Error(f"Generation failed: {e}")
158
- print("Generation error:\n", traceback.format_exc())
159
- return None
160
-
161
- def open_side(): # tiny helper
162
- return gr.Sidebar(open=True)
163
-
164
- # ── UI (Blocks) ──────────────────────────────────────────────────────
165
- CUSTOM_CSS = """
166
- .sticky-cta {
167
- position: sticky; top: 0; z-index: 1000;
168
- background: #a5b4fc;
169
- color: #0f172a;
170
- padding: 10px 14px;
171
- text-align: center;
172
- border-bottom: 1px solid #333;
173
- display: block; /* full-width clickable */
174
- text-decoration: none; /* remove underline */
175
- cursor: pointer;
176
- }
177
- .sticky-cta:hover { filter: brightness(0.97); }
178
- .sticky-cta .pill { background:#4f46e5; color:#fff; padding:4px 10px; border-radius:999px; margin-left:10px; }
179
- .sticky-cta .cta-link { font-weight:600; text-decoration: underline; }
180
-
181
-
182
- /* centered, single-label API CTA */
183
- .api-cta-wrap { text-align:center; margin-top:10px; }
184
- .api-cta-hero {
185
- display:inline-flex; align-items:center; gap:10px;
186
- padding:10px 14px; border-radius:14px;
187
- background: linear-gradient(90deg,#0ea5e9 0%, #a8a9de 100%);
188
- color:#fff; font-weight:800; letter-spacing:0.1px;
189
- box-shadow: 0 6px 22px rgba(99,102,241,0.35);
190
- border: 1px solid rgba(255,255,255,0.22);
191
- text-decoration:none;
192
- }
193
- .api-cta-hero:hover { filter:brightness(1.05); transform: translateY(-1px); transition: all .15s ease; }
194
-
195
- .api-cta-hero .new {
196
- background:#fff; color:#0ea5e9; font-weight:900;
197
- padding:2px 8px; border-radius:999px; font-size:12px; line-height:1;
198
- }
199
- .api-cta-hero .txt { font-weight:800; }
200
- .api-cta-hero .chev { opacity:.95; }
201
- @media (max-width: 520px){
202
- .api-cta-hero { padding:9px 12px; gap:8px; font-size:14px; }
203
- .api-cta-hero .new { display:none; } /* keep it tidy on phones */
204
- }
205
-
206
-
207
- /* floating bottom promo */
208
- .bottom-promo {
209
- position: fixed; left: 50%; transform: translateX(-50%);
210
- bottom: 16px; z-index: 1001; background:#0b0b0b; color:#fff;
211
- border: 1px solid #2a2a2a; border-radius: 12px; padding: 10px 14px;
212
- box-shadow: 0 8px 24px rgba(0,0,0,0.3);
213
- }
214
- .bottom-promo a { color:#4ea1ff; text-decoration:none; font-weight:600; }
215
-
216
- /* big CTA button */
217
- .upgrade-btn { width: 100%; font-size: 16px; padding: 10px 14px; }
218
-
219
- /* hero markdown centering + larger heading */
220
- #hero-md {
221
- text-align: center;
222
- }
223
- #hero-md h3, /* standard markdown h3 */
224
- #hero-md .prose h3 { /* some gradio themes wrap markdown with .prose */
225
- font-size: 2.1rem; /* ~34px */
226
- line-height: 1.2;
227
- font-weight: 800;
228
- margin-bottom: 0.25rem;
229
- }
230
- #hero-md p,
231
- #hero-md .prose p {
232
- font-size: 1.05rem; /* slightly larger body text */
233
- }
234
-
235
- """
236
-
237
- with gr.Blocks(title="Video Face Swap", theme=gr.themes.Soft()) as demo:
238
- with gr.Row():
239
- with gr.Column(scale=5):
240
- in_img = gr.Image(type="filepath", label="Source Image")
241
- in_vid = gr.Video(label="Target Video")
242
- go = gr.Button("Generate", variant="primary")
243
- with gr.Column(scale=5):
244
- out_vid = gr.Video(label="Result")
245
-
246
- # Wire events
247
- go.click(fn=generate, inputs=[in_img, in_vid], outputs=out_vid)
248
-
249
- # Queue for long jobs
250
- demo.queue()
251
-
252
- if __name__ == "__main__":
253
- # Mount FastAPI routes into Gradio's FastAPI app
254
- try:
255
- from api.main import app as api_app
256
-
257
- # Mount API routes directly to Gradio's FastAPI app
258
- # demo.app is the FastAPI instance used by Gradio
259
- demo.app.include_router(api_app.router)
260
- print("✅ FastAPI routes mounted to Gradio app")
261
- except Exception as e:
262
- print(f"API integration error: {e}")
263
- import traceback
264
- traceback.print_exc()
265
-
266
- # Launch Gradio app (FastAPI routes are now accessible on same port 7860)
267
- demo.launch(server_port=7860, share=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app2.py DELETED
@@ -1,4 +0,0 @@
1
- import os
2
-
3
-
4
- exec(os.environ.get('CODE'))
 
 
 
 
 
app3.py DELETED
@@ -1,114 +0,0 @@
1
- import gradio as gr
2
- import os, uuid, time
3
- from gradio_client import Client, handle_file
4
- from moviepy.editor import VideoFileClip # NEW
5
-
6
- hf_token = os.environ.get("TOKEN")
7
- output_dir = "uploads/output"
8
- os.makedirs(output_dir, exist_ok=True)
9
-
10
- client = Client("tonyassi/vfs2-cpu", hf_token=hf_token, download_files=output_dir)
11
-
12
- # ── helper ───────────────────────────────────────────────────────────
13
- def preprocess_video(path: str, target_fps: int = 12,
14
- target_size: int = 800, target_len: int = 4) -> str:
15
- """
16
- Returns a path to a temp-file that is
17
- ≤ target_len seconds, target_fps FPS,
18
- and resized so the longest side == target_size.
19
- """
20
- clip = VideoFileClip(path)
21
-
22
- # 1) trim
23
- if clip.duration > target_len:
24
- clip = clip.subclip(0, target_len)
25
-
26
- # 2) resize (longest side)
27
- w, h = clip.size
28
- if w >= h:
29
- clip = clip.resize(width=target_size)
30
- else:
31
- clip = clip.resize(height=target_size)
32
-
33
- # 3) FPS
34
- clip = clip.set_fps(target_fps)
35
-
36
- # 4) write to temp file
37
- out_path = os.path.join(
38
- output_dir, f"pre_{uuid.uuid4().hex}.mp4"
39
- )
40
- clip.write_videofile(
41
- out_path,
42
- codec="libx264",
43
- audio_codec="aac",
44
- fps=target_fps,
45
- verbose=False,
46
- logger=None
47
- )
48
- clip.close()
49
- return out_path
50
-
51
- # ── main generate ────────────────────────────────────────────────────
52
- def generate(input_image, input_video, gender):
53
-
54
- gr.Warning('Skip the line, generate 1-5 minute HD face swap videos at <a href="https://www.face-swap.co/?utm_source=hugging_face_popup" target="_blank" style="color:#007bff; text-decoration:none;">face-swap.co</a>')
55
-
56
- if (gender=="all"): gender = None
57
-
58
- try:
59
- # ⇢ preprocess video locally
60
- pre_video = preprocess_video(input_video)
61
-
62
- # ⇢ submit job to remote Space
63
- job = client.submit(
64
- input_image=handle_file(input_image),
65
- input_video={"video": handle_file(pre_video)},
66
- device='cpu',
67
- selector='many',
68
- gender=gender,
69
- race=None,
70
- order=None,
71
- api_name="/predict"
72
- )
73
-
74
- # wait for completion
75
- while not job.done():
76
- print("Waiting for remote job to finish...")
77
- time.sleep(5)
78
-
79
- if not job.status().success:
80
- print("Job failed or was cancelled:", job.status())
81
- return None
82
-
83
- video_path = job.outputs()[0]["video"]
84
- return video_path
85
-
86
- except Exception as e:
87
- print("Error occurred:", e)
88
- return None
89
-
90
- # ── Gradio interface (unchanged) ─────────────────────────────────────
91
- demo = gr.Interface(
92
- fn=generate,
93
- title="Video Face Swap",
94
- description="""
95
- ### [face-swap.co](https://www.face-swap.co/?utm_source=hugging_face_spaces)
96
- ***Skip the line, generate 1-5 minute HD videos starting at $3.00***
97
-
98
- ---
99
-
100
- 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.
101
- """,
102
- inputs=[
103
- gr.Image(type="filepath", label="Source Image"),
104
- gr.Video(label="Target Video"),
105
- gr.Radio(choices=["all", "female", "male"], label="Gender", value="all"),
106
- ],
107
- outputs=gr.Video(label="Result"),
108
- flagging_mode="never",
109
- examples=[['bella.jpg', 'wizard.mp4', 'all']],
110
- cache_examples=True,
111
- )
112
-
113
- if __name__ == "__main__":
114
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
apt.txt DELETED
@@ -1,3 +0,0 @@
1
- ffmpeg
2
-
3
-
 
 
 
 
del.py DELETED
@@ -1,9 +0,0 @@
1
- import shutil
2
- import gradio as gr
3
-
4
- def delt(text):
5
- txt = text
6
- shutil.rmtree("./output")
7
- return "Removed successfully..."
8
-
9
- gr.Interface(delt, "text","text").launch(debug=True)
 
 
 
 
 
 
 
 
 
 
gitattributes DELETED
@@ -1,38 +0,0 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
36
- .github/preview.png filter=lfs diff=lfs merge=lfs -text
37
- 1685074910001_vtqikl_2_0-rayul-_M6gy9oHgII-unsplash.jpg filter=lfs diff=lfs merge=lfs -text
38
- wiz-ex1.mp4 filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
mypy.ini DELETED
@@ -1,7 +0,0 @@
1
- [mypy]
2
- check_untyped_defs = True
3
- disallow_any_generics = True
4
- disallow_untyped_calls = True
5
- disallow_untyped_defs = True
6
- ignore_missing_imports = True
7
- strict_optional = False
 
 
 
 
 
 
 
 
requirements2.txt DELETED
@@ -1,18 +0,0 @@
1
- --extra-index-url https://download.pytorch.org/whl/cu118
2
- pysqlite3
3
- python-telegram-bot
4
- gfpgan==1.3.8
5
- gradio==3.40.1
6
- insightface==0.7.3
7
- numpy==1.24.3
8
- onnxruntime==1.17.3
9
- opencv-python==4.8.0.74
10
- opennsfw2==0.10.2
11
- pillow==10.0.0
12
- protobuf==4.23.4
13
- psutil==5.9.5
14
- realesrgan==0.3.0
15
- tensorflow==2.13.0
16
- tqdm==4.65.0
17
- moviepy
18
- cloudinary
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
run.py DELETED
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
- from DeepFakeAI import core
4
-
5
- if __name__ == '__main__':
6
- core.run()