MacBook pro commited on
Commit
6f09ee6
·
1 Parent(s): a026a7c

chore: remove deploy script; add persistent model storage & diagnostics

Browse files
Files changed (6) hide show
  1. README.md +66 -0
  2. app.py +18 -0
  3. deploy.sh +0 -130
  4. entrypoint.sh +100 -10
  5. model_downloader.py +15 -4
  6. models/liveportrait/README.md +0 -16
README.md CHANGED
@@ -147,6 +147,8 @@ Pipeline stats (subset) from swap pipeline:
147
  | `MIRAGE_TURN_TLS_ONLY` | Filter TURN to TLS/TCP | `1` |
148
  | `MIRAGE_PREFER_H264` | Prefer H264 codec in SDP munging | `0` |
149
  | `MIRAGE_VOICE_ENABLE` | Enable voice processor stub | `0` |
 
 
150
 
151
  CodeFormer fidelity example:
152
  ```bash
@@ -230,6 +232,70 @@ If problems persist, capture the Container log stack trace and open an issue.
230
  ## Model Auto-Download
231
  `model_downloader.py` manages required weights with atomic file locks. It supports overriding sources via env variables and gracefully continues if optional enhancers fail to download.
232
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
233
  ### Endpoints Recap
234
  See Metrics Endpoints section above. Typical usage examples:
235
 
 
147
  | `MIRAGE_TURN_TLS_ONLY` | Filter TURN to TLS/TCP | `1` |
148
  | `MIRAGE_PREFER_H264` | Prefer H264 codec in SDP munging | `0` |
149
  | `MIRAGE_VOICE_ENABLE` | Enable voice processor stub | `0` |
150
+ | `MIRAGE_PERSIST_MODELS` | Persist models under /data and symlink /app/models | `1` |
151
+ | `MIRAGE_PERSIST_MODELS` | Persist models in /data (HF Space) via symlink | `1` |
152
 
153
  CodeFormer fidelity example:
154
  ```bash
 
232
  ## Model Auto-Download
233
  `model_downloader.py` manages required weights with atomic file locks. It supports overriding sources via env variables and gracefully continues if optional enhancers fail to download.
234
 
235
+ ### Persistent Storage Strategy (Hugging Face Spaces)
236
+
237
+ By default (`MIRAGE_PERSIST_MODELS=1`), the container will:
238
+
239
+ 1. Create (if missing) a persistent directory: `/data/mirage_models`.
240
+ 2. Migrate any existing files from an earlier ephemeral `/app/models` (first run only, if the persistent dir is empty).
241
+ 3. Symlink `/app/models -> /data/mirage_models`.
242
+ 4. Run integrity checks each startup: if the sentinel `.provisioned` exists but any required model (currently `inswapper/inswapper_128_fp16.onnx`) is missing, the downloader is re-invoked automatically.
243
+
244
+ Disable persistence with:
245
+ ```bash
246
+ MIRAGE_PERSIST_MODELS=0
247
+ ```
248
+ This forces models to re-download on each cold start (not recommended for production latency / rate-limits).
249
+
250
+ Sentinel files:
251
+ - `.provisioned` – marker indicating a successful prior provisioning.
252
+ - `.provisioned_meta.json` – sizes and metadata of required models at provisioning time (informational).
253
+
254
+ If you set `MIRAGE_PROVISION_FRESH=1`, the sentinel is removed and a full re-download is attempted (useful when updating model versions or clearing partial/corrupt files).
255
+
256
+ Troubleshooting missing models:
257
+ - Call `/debug/models` – it now reports symlink status, sentinel presence, and sizes.
258
+ - If `inswapper` is missing but sentinel present, integrity logic should already trigger re-provision. If not, trigger manually with `MIRAGE_PROVISION_FRESH=1`.
259
+
260
+
261
+ ### Persistence Strategy (Hugging Face Spaces)
262
+ By default (`MIRAGE_PERSIST_MODELS=1`) the container will:
263
+ 1. Create a persistent directory at `/data/mirage_models`.
264
+ 2. Symlink `/app/models -> /data/mirage_models` so model downloads survive restarts.
265
+ 3. Maintain a sentinel file `.provisioned` plus a meta JSON summarizing required model sizes.
266
+ 4. On startup, if the sentinel exists but required files are missing (stale / manual deletion), it forces a re-download.
267
+
268
+ Disable persistence (always ephemeral) with:
269
+ ```bash
270
+ MIRAGE_PERSIST_MODELS=0
271
+ ```
272
+
273
+ You can also force a fresh provisioning ignoring the sentinel:
274
+ ```bash
275
+ MIRAGE_PROVISION_FRESH=1
276
+ ```
277
+
278
+ Debug current model status:
279
+ ```bash
280
+ curl -s https://<space-subdomain>.hf.space/debug/models | jq
281
+ ```
282
+
283
+ Example response:
284
+ ```json
285
+ {
286
+ "inswapper": {"exists": true, "size": 87916544},
287
+ "codeformer": {"exists": true, "size": 178140560},
288
+ "sentinel": {"exists": true, "meta_exists": true},
289
+ "storage": {
290
+ "root_is_symlink": true,
291
+ "root_path": "/app/models",
292
+ "target": "/data/mirage_models",
293
+ "persist_mode_env": "1"
294
+ },
295
+ "pipeline_initialized": false
296
+ }
297
+ ```
298
+
299
  ### Endpoints Recap
300
  See Metrics Endpoints section above. Typical usage examples:
301
 
app.py CHANGED
@@ -161,9 +161,27 @@ async def debug_models():
161
  root = Path(__file__).parent / 'models'
162
  ins = root / 'inswapper' / 'inswapper_128_fp16.onnx'
163
  codef = root / 'codeformer' / 'codeformer.pth'
 
 
 
 
 
 
 
 
 
 
 
164
  return {
165
  'inswapper': {'exists': ins.exists(), 'size': ins.stat().st_size if ins.exists() else 0},
166
  'codeformer': {'exists': codef.exists(), 'size': codef.stat().st_size if codef.exists() else 0},
 
 
 
 
 
 
 
167
  'pipeline_initialized': pipeline_initialized
168
  }
169
 
 
161
  root = Path(__file__).parent / 'models'
162
  ins = root / 'inswapper' / 'inswapper_128_fp16.onnx'
163
  codef = root / 'codeformer' / 'codeformer.pth'
164
+ sentinel = root / '.provisioned'
165
+ meta = root / '.provisioned_meta.json'
166
+ # Detect symlink & target
167
+ is_symlink = root.is_symlink()
168
+ target = None
169
+ if is_symlink:
170
+ try:
171
+ target = root.resolve()
172
+ except Exception:
173
+ target = None
174
+ storage_mode = os.environ.get('MIRAGE_PERSIST_MODELS', '1')
175
  return {
176
  'inswapper': {'exists': ins.exists(), 'size': ins.stat().st_size if ins.exists() else 0},
177
  'codeformer': {'exists': codef.exists(), 'size': codef.stat().st_size if codef.exists() else 0},
178
+ 'sentinel': {'exists': sentinel.exists(), 'meta_exists': meta.exists()},
179
+ 'storage': {
180
+ 'root_is_symlink': is_symlink,
181
+ 'root_path': str(root),
182
+ 'target': str(target) if target else None,
183
+ 'persist_mode_env': storage_mode
184
+ },
185
  'pipeline_initialized': pipeline_initialized
186
  }
187
 
deploy.sh DELETED
@@ -1,130 +0,0 @@
1
- #!/bin/bash
2
- # Deployment script for Mirage Real-time AI Avatar System
3
-
4
- set -e
5
-
6
- echo "🎭 Mirage Real-time AI Avatar - Deployment Script"
7
- echo "=================================================="
8
-
9
- # Check if we're deploying to HuggingFace Spaces
10
- if [[ "${SPACE_ID}" ]]; then
11
- echo "📡 Deploying to HuggingFace Spaces: ${SPACE_ID}"
12
- DEPLOYMENT_TARGET="huggingface"
13
- else
14
- echo "🐳 Local Docker deployment"
15
- DEPLOYMENT_TARGET="local"
16
- fi
17
-
18
- # Set environment variables for optimal A10G performance
19
- export CUDA_VISIBLE_DEVICES=0
20
- export TORCH_CUDA_ARCH_LIST="8.6" # A10G architecture
21
- export CUDA_LAUNCH_BLOCKING=0
22
- export MIRAGE_VOICE_ENABLE=1
23
- export MIRAGE_CHUNK_MS=160
24
- export MIRAGE_VIDEO_MAX_FPS=20
25
-
26
- echo "🔧 Environment configured for A10G GPU"
27
-
28
- # Download required models
29
- echo "📥 Downloading AI models..."
30
-
31
- # Create model directories
32
- mkdir -p models/{liveportrait,rvc,hubert,rmvpe}
33
- mkdir -p checkpoints
34
-
35
- # Function to download from HuggingFace with retry
36
- download_hf_model() {
37
- local repo=$1
38
- local filename=$2
39
- local output_dir=$3
40
- local max_retries=3
41
- local retry_count=0
42
-
43
- while [ $retry_count -lt $max_retries ]; do
44
- if python3 -c "
45
- from huggingface_hub import hf_hub_download
46
- import os
47
- try:
48
- hf_hub_download('$repo', '$filename', local_dir='$output_dir', local_dir_use_symlinks=False)
49
- print('✅ Downloaded $filename')
50
- except Exception as e:
51
- print(f'❌ Failed to download $filename: {e}')
52
- exit(1)
53
- "; then
54
- break
55
- fi
56
-
57
- retry_count=$((retry_count + 1))
58
- echo "⏳ Retry $retry_count/$max_retries for $filename"
59
- sleep 2
60
- done
61
-
62
- if [ $retry_count -eq $max_retries ]; then
63
- echo "❌ Failed to download $filename after $max_retries retries"
64
- return 1
65
- fi
66
- }
67
-
68
- # Download LivePortrait models (if available)
69
- if python3 -c "from huggingface_hub import HfApi; api = HfApi(); print('✅ HuggingFace available')" 2>/dev/null; then
70
- echo "🎨 Attempting to download LivePortrait models..."
71
- # Note: These would be the actual model files when available
72
- # download_hf_model "KwaiVGI/LivePortrait" "appearance_feature_extractor.pth" "models/liveportrait"
73
- # download_hf_model "KwaiVGI/LivePortrait" "motion_extractor.pth" "models/liveportrait"
74
- # download_hf_model "KwaiVGI/LivePortrait" "warping_module.pth" "models/liveportrait"
75
- # download_hf_model "KwaiVGI/LivePortrait" "spade_generator.pth" "models/liveportrait"
76
- echo "ℹ️ LivePortrait models will be downloaded on first use"
77
- else
78
- echo "⚠️ HuggingFace Hub not available, models will be downloaded at runtime"
79
- fi
80
-
81
- # Verify GPU availability
82
- echo "🔍 Checking GPU configuration..."
83
- python3 -c "
84
- import torch
85
- print(f'PyTorch version: {torch.__version__}')
86
- print(f'CUDA available: {torch.cuda.is_available()}')
87
- if torch.cuda.is_available():
88
- print(f'GPU: {torch.cuda.get_device_name(0)}')
89
- print(f'CUDA version: {torch.version.cuda}')
90
- print(f'GPU memory: {torch.cuda.get_device_properties(0).total_memory / 1024**3:.1f} GB')
91
- else:
92
- print('⚠️ GPU not available - running in CPU mode')
93
- "
94
-
95
- # Setup virtual camera (Linux only)
96
- if [[ "$OSTYPE" == "linux-gnu"* ]]; then
97
- echo "📹 Setting up virtual camera (v4l2loopback)..."
98
-
99
- # Check if v4l2loopback is available
100
- if modprobe v4l2loopback devices=1 video_nr=10 card_label="Mirage Virtual Camera" 2>/dev/null; then
101
- echo "✅ Virtual camera device created: /dev/video10"
102
- else
103
- echo "⚠️ Could not create virtual camera device (requires sudo)"
104
- echo "💡 Run: sudo modprobe v4l2loopback devices=1 video_nr=10 card_label='Mirage Virtual Camera'"
105
- fi
106
- fi
107
-
108
- # Start the application
109
- echo "🚀 Starting Mirage AI Avatar System..."
110
-
111
- if [[ "${DEPLOYMENT_TARGET}" == "huggingface" ]]; then
112
- # HuggingFace Spaces deployment
113
- echo "🤗 Running on HuggingFace Spaces with A10G GPU"
114
- exec python3 -u app.py
115
- else
116
- # Local deployment
117
- echo "💻 Running locally"
118
-
119
- # Check if port 7860 is available
120
- if lsof -Pi :7860 -sTCP:LISTEN -t >/dev/null; then
121
- echo "⚠️ Port 7860 is already in use"
122
- PORT=7861
123
- else
124
- PORT=7860
125
- fi
126
-
127
- echo "🌐 Server will be available at: http://localhost:${PORT}"
128
- export PORT=${PORT}
129
- exec python3 -u app.py
130
- fi
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
entrypoint.sh CHANGED
@@ -4,32 +4,102 @@ set -euo pipefail
4
  echo "[entrypoint] Starting Mirage container..."
5
  echo "[entrypoint] Python: $(python3 --version 2>&1)"
6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  # Updated provisioning for face swap pipeline (InSwapper + optional CodeFormer)
8
  MODEL_ROOT="/app/models"
9
  SENTINEL="${MODEL_ROOT}/.provisioned"
 
10
  AUDIT_FILE="${MODEL_ROOT}/_download_audit.jsonl"
11
  REQ_FILES=("inswapper/inswapper_128_fp16.onnx")
12
  DL_TAG="${MIRAGE_DL_TAG:-startup}"
13
 
 
14
  echo "[entrypoint] Ensuring model directory exists: ${MODEL_ROOT}"
15
- mkdir -p "${MODEL_ROOT}"
16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  should_download=0
 
 
18
  if [[ ! -f "${SENTINEL}" ]]; then
19
  should_download=1
20
- echo "[entrypoint] Sentinel missing; provisioning required"
 
 
 
 
 
21
  else
22
- if [[ "${MIRAGE_PROVISION_FRESH:-0}" =~ ^(1|true|yes|on)$ ]]; then
23
- echo "[entrypoint] MIRAGE_PROVISION_FRESH set; forcing fresh provisioning"
24
- rm -f "${SENTINEL}" || true
25
  should_download=1
26
- else
27
- echo "[entrypoint] Sentinel present; skipping download (idempotent)"
28
- # Audit skip
29
- ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
30
- echo "{\"ts\":\"${ts}\",\"event\":\"skip_provision\",\"tag\":\"${DL_TAG}\"}" >> "${AUDIT_FILE}" 2>/dev/null || true
31
  fi
32
  fi
 
 
 
 
 
33
 
34
  if [[ $should_download -eq 1 ]]; then
35
  if [[ "${MIRAGE_DOWNLOAD_MODELS:-1}" =~ ^(1|true|TRUE|yes|on)$ ]]; then
@@ -39,7 +109,27 @@ if [[ $should_download -eq 1 ]]; then
39
  if python3 /app/model_downloader.py; then
40
  ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
41
  echo "{\"ts\":\"${ts}\",\"event\":\"provision_success\",\"tag\":\"${DL_TAG}\"}" >> "${AUDIT_FILE}" 2>/dev/null || true
 
42
  touch "${SENTINEL}" || true
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  else
44
  ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
45
  echo "{\"ts\":\"${ts}\",\"event\":\"provision_error\",\"tag\":\"${DL_TAG}\"}" >> "${AUDIT_FILE}" 2>/dev/null || true
 
4
  echo "[entrypoint] Starting Mirage container..."
5
  echo "[entrypoint] Python: $(python3 --version 2>&1)"
6
 
7
+ #############################################
8
+ # Model persistence & provisioning strategy #
9
+ #############################################
10
+ # We support persistent storage on Hugging Face Spaces by symlinking /app/models
11
+ # to /data/mirage_models (HF persists /data across restarts). Controlled by
12
+ # MIRAGE_PERSIST_MODELS (default=1). If disabled, we keep ephemeral /app/models.
13
+
14
+ PERSIST_DEFAULT=1
15
+ if [[ "${MIRAGE_PERSIST_MODELS:-}" =~ ^(0|false|no|off)$ ]]; then
16
+ PERSIST_DEFAULT=0
17
+ fi
18
+
19
+ if [[ $PERSIST_DEFAULT -eq 1 ]]; then
20
+ PERSIST_ROOT="/data/mirage_models"
21
+ mkdir -p "$PERSIST_ROOT" || true
22
+ # If /app/models exists and is not a symlink, migrate its contents (first run)
23
+ if [[ -d /app/models && ! -L /app/models ]]; then
24
+ # Only migrate if persistent dir is empty (avoid overwriting existing cache)
25
+ if [[ -z "$(ls -A "$PERSIST_ROOT" 2>/dev/null)" ]]; then
26
+ echo "[entrypoint] Migrating existing /app/models/* -> $PERSIST_ROOT (first persistent run)"
27
+ shopt -s dotglob nullglob
28
+ for f in /app/models/*; do
29
+ mv "$f" "$PERSIST_ROOT/" 2>/dev/null || true
30
+ done
31
+ shopt -u dotglob nullglob
32
+ fi
33
+ rm -rf /app/models
34
+ ln -s "$PERSIST_ROOT" /app/models
35
+ echo "[entrypoint] Symlinked /app/models -> $PERSIST_ROOT"
36
+ elif [[ -L /app/models ]]; then
37
+ echo "[entrypoint] /app/models already symlinked"
38
+ else
39
+ # Create then symlink if missing
40
+ rm -rf /app/models 2>/dev/null || true
41
+ ln -s "$PERSIST_ROOT" /app/models
42
+ echo "[entrypoint] Initialized persistent symlink /app/models -> $PERSIST_ROOT"
43
+ fi
44
+ STORAGE_MODE="persistent"
45
+ STORAGE_PATH="$PERSIST_ROOT"
46
+ else
47
+ mkdir -p /app/models || true
48
+ STORAGE_MODE="ephemeral"
49
+ STORAGE_PATH="/app/models"
50
+ fi
51
+
52
  # Updated provisioning for face swap pipeline (InSwapper + optional CodeFormer)
53
  MODEL_ROOT="/app/models"
54
  SENTINEL="${MODEL_ROOT}/.provisioned"
55
+ SENTINEL_META="${MODEL_ROOT}/.provisioned_meta.json"
56
  AUDIT_FILE="${MODEL_ROOT}/_download_audit.jsonl"
57
  REQ_FILES=("inswapper/inswapper_128_fp16.onnx")
58
  DL_TAG="${MIRAGE_DL_TAG:-startup}"
59
 
60
+ echo "[entrypoint] Storage mode: ${STORAGE_MODE} (root=${STORAGE_PATH})"
61
  echo "[entrypoint] Ensuring model directory exists: ${MODEL_ROOT}"
62
+ mkdir -p "${MODEL_ROOT}" || true
63
 
64
+ # Function to validate required model presence
65
+ validate_models() {
66
+ local missing=0
67
+ for f in "${REQ_FILES[@]}"; do
68
+ if [[ ! -f "${MODEL_ROOT}/$f" ]]; then
69
+ missing=1
70
+ fi
71
+ done
72
+ return $missing
73
+ }
74
+
75
+ # Decide whether to (re)download. Conditions forcing provisioning:
76
+ # - Sentinel missing
77
+ # - MIRAGE_PROVISION_FRESH set
78
+ # - Sentinel exists but required files missing (stale sentinel)
79
+ # - Sentinel meta hash mismatch (future extension; placeholder now)
80
  should_download=0
81
+
82
+ SENTINEL_REASON="skip"
83
  if [[ ! -f "${SENTINEL}" ]]; then
84
  should_download=1
85
+ SENTINEL_REASON="no_sentinel"
86
+ elif [[ "${MIRAGE_PROVISION_FRESH:-0}" =~ ^(1|true|yes|on)$ ]]; then
87
+ echo "[entrypoint] MIRAGE_PROVISION_FRESH set; forcing fresh provisioning"
88
+ rm -f "${SENTINEL}" "${SENTINEL_META}" || true
89
+ should_download=1
90
+ SENTINEL_REASON="forced_fresh"
91
  else
92
+ if ! validate_models; then
93
+ echo "[entrypoint] Sentinel present but required model(s) missing; will re-provision"
 
94
  should_download=1
95
+ SENTINEL_REASON="stale_sentinel"
 
 
 
 
96
  fi
97
  fi
98
+ if [[ $should_download -eq 0 ]]; then
99
+ echo "[entrypoint] Sentinel OK; skipping download (reason=${SENTINEL_REASON})"
100
+ ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
101
+ echo "{\"ts\":\"${ts}\",\"event\":\"skip_provision\",\"reason\":\"${SENTINEL_REASON}\",\"tag\":\"${DL_TAG}\"}" >> "${AUDIT_FILE}" 2>/dev/null || true
102
+ fi
103
 
104
  if [[ $should_download -eq 1 ]]; then
105
  if [[ "${MIRAGE_DOWNLOAD_MODELS:-1}" =~ ^(1|true|TRUE|yes|on)$ ]]; then
 
109
  if python3 /app/model_downloader.py; then
110
  ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
111
  echo "{\"ts\":\"${ts}\",\"event\":\"provision_success\",\"tag\":\"${DL_TAG}\"}" >> "${AUDIT_FILE}" 2>/dev/null || true
112
+ # Write sentinel + meta summary (sizes of required models)
113
  touch "${SENTINEL}" || true
114
+ {
115
+ echo '{'
116
+ echo ' "ts": "'"${ts}"'",'
117
+ echo ' "storage_mode": "'"${STORAGE_MODE}"'",'
118
+ echo ' "required_models": {'
119
+ comma=0
120
+ for f in "${REQ_FILES[@]}"; do
121
+ size=0
122
+ if [[ -f "${MODEL_ROOT}/$f" ]]; then
123
+ size=$(stat -c%s "${MODEL_ROOT}/$f" 2>/dev/null || wc -c <"${MODEL_ROOT}/$f")
124
+ fi
125
+ if [[ $comma -eq 1 ]]; then echo ','; fi
126
+ printf ' "%s": {"size": %s}' "$f" "$size"
127
+ comma=1
128
+ done
129
+ echo ''
130
+ echo ' }'
131
+ echo '}'
132
+ } > "${SENTINEL_META}" 2>/dev/null || true
133
  else
134
  ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
135
  echo "{\"ts\":\"${ts}\",\"event\":\"provision_error\",\"tag\":\"${DL_TAG}\"}" >> "${AUDIT_FILE}" 2>/dev/null || true
model_downloader.py CHANGED
@@ -285,14 +285,25 @@ def maybe_download() -> bool:
285
  codef_dest = CODEFORMER_DIR / 'codeformer.pth'
286
  if not codef_dest.exists():
287
  try:
288
- print('[downloader] Downloading CodeFormer model...')
289
  codef_dest.parent.mkdir(parents=True, exist_ok=True)
290
  with _FileLock(codef_dest):
291
  if not codef_dest.exists():
292
  _attempt_urls(codeformer_urls, codef_dest)
293
- print(f'[downloader] CodeFormer ready: {codef_dest}')
294
- _audit('download_ok', model='codeformer', path=str(codef_dest))
295
- except Exception as e:
 
 
 
 
 
 
 
 
 
 
 
296
  print(f'[downloader] ⚠️ CodeFormer download failed (continuing): {e}')
297
  _audit('download_error', model='codeformer', error=str(e))
298
  else:
 
285
  codef_dest = CODEFORMER_DIR / 'codeformer.pth'
286
  if not codef_dest.exists():
287
  try:
288
+ print('[downloader] Downloading CodeFormer model (optional)...')
289
  codef_dest.parent.mkdir(parents=True, exist_ok=True)
290
  with _FileLock(codef_dest):
291
  if not codef_dest.exists():
292
  _attempt_urls(codeformer_urls, codef_dest)
293
+ if codef_dest.exists() and codef_dest.stat().st_size > 1048576: # >1MB sanity threshold
294
+ print(f'[downloader] CodeFormer ready: {codef_dest}')
295
+ _audit('download_ok', model='codeformer', path=str(codef_dest))
296
+ else:
297
+ if codef_dest.exists():
298
+ try:
299
+ size = codef_dest.stat().st_size
300
+ print(f'[downloader] ⚠️ CodeFormer file too small ({size} bytes); removing partial')
301
+ codef_dest.unlink()
302
+ except Exception:
303
+ pass
304
+ print('[downloader] ⚠️ CodeFormer unavailable after attempts (continuing without enhancer)')
305
+ _audit('download_incomplete', model='codeformer')
306
+ except Exception as e: # pragma: no cover - best effort optional
307
  print(f'[downloader] ⚠️ CodeFormer download failed (continuing): {e}')
308
  _audit('download_error', model='codeformer', error=str(e))
309
  else:
models/liveportrait/README.md DELETED
@@ -1,16 +0,0 @@
1
- # LivePortrait (ONNX) Models
2
-
3
- Place the following ONNX files in this directory:
4
-
5
- - appearance_feature_extractor.onnx (required)
6
- - motion_extractor.onnx (required)
7
- - generator.onnx (optional, improves fidelity; if missing we use motion-based warping or landmark fallback)
8
-
9
- Environment variables for runtime downloader (optional):
10
-
11
- - MIRAGE_DOWNLOAD_MODELS=1
12
- - MIRAGE_LP_APPEARANCE_URL
13
- - MIRAGE_LP_MOTION_URL
14
- - MIRAGE_LP_GENERATOR_URL (optional)
15
-
16
- At startup, the app will try to download models to this folder if URLs are configured. You can also drop files here manually. The /debug/models endpoint reports presence and sizes.