mirror of
https://github.com/hacksider/Deep-Live-Cam.git
synced 2026-09-01 21:40:52 +02:00
179 lines
5.7 KiB
Python
179 lines
5.7 KiB
Python
import os
|
|
import platform
|
|
import ssl
|
|
import threading
|
|
import urllib.error
|
|
import urllib.request
|
|
from typing import Dict, List, Optional
|
|
|
|
from tqdm import tqdm
|
|
|
|
from modules.paths import MODELS_DIR
|
|
|
|
HF_REPO_ID = "hacksider/deep-live-cam"
|
|
HF_RESOLVE_BASE = f"https://huggingface.co/{HF_REPO_ID}/resolve/main/"
|
|
|
|
MODEL_SIZES: Dict[str, int] = {
|
|
"inswapper_128.onnx": 554253681,
|
|
"inswapper_128_fp16.onnx": 277680638,
|
|
"gfpgan-1024.onnx": 365875079,
|
|
"GPEN-BFR-256.onnx": 75715262,
|
|
"GPEN-BFR-512.onnx": 284244491,
|
|
"buffalo_l/buffalo_l/1k3d68.onnx": 143607619,
|
|
"buffalo_l/buffalo_l/2d106det.onnx": 5030888,
|
|
"buffalo_l/buffalo_l/det_10g.onnx": 16923827,
|
|
"buffalo_l/buffalo_l/genderage.onnx": 1322532,
|
|
"buffalo_l/buffalo_l/w600k_r50.onnx": 174383860,
|
|
}
|
|
|
|
_LOCKS: Dict[str, threading.Lock] = {}
|
|
_LOCKS_GUARD = threading.Lock()
|
|
|
|
CHUNK_SIZE = 1024 * 256
|
|
|
|
|
|
def _ssl_context():
|
|
if platform.system().lower() == "darwin":
|
|
return ssl._create_unverified_context()
|
|
return None
|
|
|
|
|
|
def _lock_for(key: str) -> threading.Lock:
|
|
with _LOCKS_GUARD:
|
|
if key not in _LOCKS:
|
|
_LOCKS[key] = threading.Lock()
|
|
return _LOCKS[key]
|
|
|
|
|
|
def resolve_url(name: str) -> str:
|
|
return HF_RESOLVE_BASE + name.replace(os.sep, "/")
|
|
|
|
|
|
def local_path(name: str, dest_dir: Optional[str] = None) -> str:
|
|
if dest_dir is not None:
|
|
return os.path.join(dest_dir, os.path.basename(name))
|
|
return os.path.join(MODELS_DIR, *name.replace("/", os.sep).split(os.sep))
|
|
|
|
|
|
def expected_size(name: str) -> Optional[int]:
|
|
return MODEL_SIZES.get(name.replace(os.sep, "/"))
|
|
|
|
|
|
def is_present(name: str, dest_dir: Optional[str] = None) -> bool:
|
|
path = local_path(name, dest_dir)
|
|
return os.path.isfile(path) and os.path.getsize(path) > 0
|
|
|
|
|
|
def _download(name: str, url: str, target: str, size: Optional[int]) -> bool:
|
|
os.makedirs(os.path.dirname(target) or MODELS_DIR, exist_ok=True)
|
|
partial = target + ".part"
|
|
resume_from = os.path.getsize(partial) if os.path.isfile(partial) else 0
|
|
|
|
headers = {"User-Agent": "Deep-Live-Cam"}
|
|
if resume_from:
|
|
headers["Range"] = f"bytes={resume_from}-"
|
|
|
|
try:
|
|
request = urllib.request.Request(url, headers=headers)
|
|
response = urllib.request.urlopen(request, context=_ssl_context(), timeout=60)
|
|
except urllib.error.HTTPError as error:
|
|
if resume_from and error.code in (416, 501):
|
|
try:
|
|
os.remove(partial)
|
|
except OSError:
|
|
pass
|
|
return _download(name, url, target, size)
|
|
print(f"[DLC.MODELS] Failed to download {name}: HTTP {error.code}")
|
|
return False
|
|
except (urllib.error.URLError, OSError) as error:
|
|
print(f"[DLC.MODELS] Failed to download {name}: {error}")
|
|
return False
|
|
|
|
with response:
|
|
if resume_from and getattr(response, "status", 200) != 206:
|
|
resume_from = 0
|
|
remaining = int(response.headers.get("Content-Length", 0) or 0)
|
|
total = size or (resume_from + remaining) or None
|
|
mode = "ab" if resume_from else "wb"
|
|
try:
|
|
with open(partial, mode) as handle:
|
|
with tqdm(
|
|
total=total,
|
|
initial=resume_from,
|
|
desc=f"Downloading {os.path.basename(name)}",
|
|
unit="B",
|
|
unit_scale=True,
|
|
unit_divisor=1024,
|
|
) as progress:
|
|
while True:
|
|
buffer = response.read(CHUNK_SIZE)
|
|
if not buffer:
|
|
break
|
|
handle.write(buffer)
|
|
progress.update(len(buffer))
|
|
except (urllib.error.URLError, OSError) as error:
|
|
print(f"[DLC.MODELS] Download of {name} interrupted: {error}")
|
|
return False
|
|
|
|
downloaded = os.path.getsize(partial)
|
|
if size is not None and downloaded != size:
|
|
print(f"[DLC.MODELS] {name} is {downloaded} bytes, expected {size}. Discarding.")
|
|
try:
|
|
os.remove(partial)
|
|
except OSError:
|
|
pass
|
|
return False
|
|
|
|
try:
|
|
os.replace(partial, target)
|
|
except OSError as error:
|
|
print(f"[DLC.MODELS] Could not finalise {name}: {error}")
|
|
return False
|
|
return True
|
|
|
|
|
|
def ensure_model(
|
|
name: str, quiet: bool = False, dest_dir: Optional[str] = None
|
|
) -> Optional[str]:
|
|
name = name.replace(os.sep, "/")
|
|
target = local_path(name, dest_dir)
|
|
|
|
with _lock_for(target):
|
|
if is_present(name, dest_dir):
|
|
return target
|
|
if not quiet:
|
|
print(f"[DLC.MODELS] {name} not found in models folder, downloading...")
|
|
if _download(name, resolve_url(name), target, expected_size(name)):
|
|
return target
|
|
return None
|
|
|
|
|
|
def ensure_any(names: List[str]) -> Optional[str]:
|
|
for name in names:
|
|
if is_present(name):
|
|
return local_path(name)
|
|
for name in names:
|
|
path = ensure_model(name)
|
|
if path is not None:
|
|
return path
|
|
return None
|
|
|
|
|
|
def ensure_insightface_pack(name: str = "buffalo_l") -> bool:
|
|
members = [n for n in MODEL_SIZES if n.startswith(f"{name}/")]
|
|
if not members:
|
|
return False
|
|
|
|
dest_dir = os.path.join(os.path.expanduser("~"), ".insightface", "models", name)
|
|
if all(is_present(member, dest_dir) for member in members):
|
|
return True
|
|
|
|
print(f"[DLC.MODELS] insightface pack '{name}' is missing, downloading...")
|
|
ok = True
|
|
for member in members:
|
|
if ensure_model(member, quiet=True, dest_dir=dest_dir) is None:
|
|
ok = False
|
|
if not ok:
|
|
print(f"[DLC.MODELS] Could not pre-fill '{name}'; insightface will retry.")
|
|
return ok
|