From 684a4805d7881dd0f8165843c027d20825f203fe Mon Sep 17 00:00:00 2001 From: mike Date: Sun, 28 Jun 2026 13:12:05 +0200 Subject: [PATCH] =?UTF-8?q?=20=20=20Summary=20=20=20=20=E2=80=A2=20Impleme?= =?UTF-8?q?nted=20global=20offline=20mode=20for=20all=20HuggingFace-depend?= =?UTF-8?q?ent=20modules=20to=20eliminate=20unauthenticated=20request=20wa?= =?UTF-8?q?rnings=20and=20prevent=20external=20connection=20attempts=20dur?= =?UTF-8?q?ing=20startup=20and=20runtime.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes • Global Offline Configuration: Added HF_HUB_OFFLINE=1 and TRANSFORMERS_OFFLINE=1 to the environment variables at the top of edit_api.py, embeddings.py, watcher.py, and pose_llm/pose_llm_api.py. • Explicit Local Files Only: Updated all huggingface_hub.hf_hub_download calls in edit_api.py to include local_files_only=True, ensuring they never attempt to reach the Hub if models are already cached. • LLM Service Optimization: Updated AutoTokenizer and AutoModelForCausalLM calls in pose_llm/pose_llm_api.py to use local_files_only=True. • Consistency: Ensured that shared modules like embeddings.py which uses open_clip are also locked to offline mode to prevent background downloads. --- pose_llm/pose_llm_api.py | 5 ++- tour-comfy/car.html | 74 ++++++++++++++++++++++++--------- tour-comfy/edit_api.py | 88 ++++++++++++++++++++++++++++------------ tour-comfy/embeddings.py | 4 +- tour-comfy/watcher.py | 2 + 5 files changed, 126 insertions(+), 47 deletions(-) diff --git a/pose_llm/pose_llm_api.py b/pose_llm/pose_llm_api.py index 635d0a9..fb6ef84 100755 --- a/pose_llm/pose_llm_api.py +++ b/pose_llm/pose_llm_api.py @@ -21,6 +21,8 @@ Env: import asyncio import json import os +os.environ["HF_HUB_OFFLINE"] = "1" +os.environ["TRANSFORMERS_OFFLINE"] = "1" import subprocess import threading import time @@ -69,7 +71,7 @@ async def lifespan(app: FastAPI): # Boost GPU to high performance mode (avoids power-saving clock throttle) subprocess.run(["/opt/rocm/bin/rocm-smi", "--setperflevel", "high"], capture_output=True) print(f"Loading model {MODEL_ID} (gpu≤{MAX_GPU_MEM} cpu≤{MAX_CPU_MEM})...", flush=True) - tokenizer = AutoTokenizer.from_pretrained(MODEL_ID) + tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, local_files_only=True) if not tokenizer.chat_template: print("No chat_template found — applying Mistral [INST] fallback.", flush=True) tokenizer.chat_template = _MISTRAL_TEMPLATE @@ -78,6 +80,7 @@ async def lifespan(app: FastAPI): torch_dtype=torch.float16, device_map="auto", max_memory={0: MAX_GPU_MEM, "cpu": MAX_CPU_MEM}, + local_files_only=True, ) model.eval() state["tokenizer"] = tokenizer diff --git a/tour-comfy/car.html b/tour-comfy/car.html index c98d6e3..ad0c060 100644 --- a/tour-comfy/car.html +++ b/tour-comfy/car.html @@ -2299,6 +2299,25 @@ // --- HYDRATION_START --- const PRELOADED_IMAGES = [ + "_turntable/up_c8be3aa7/views/view_018_270deg.png", + "_turntable/up_c8be3aa7/views/view_017_255deg.png", + "_turntable/up_c8be3aa7/views/view_016_240deg.png", + "_turntable/up_c8be3aa7/views/view_015_225deg.png", + "_turntable/up_c8be3aa7/views/view_014_210deg.png", + "_turntable/up_c8be3aa7/views/view_013_195deg.png", + "_turntable/up_c8be3aa7/views/view_012_180deg.png", + "_turntable/up_c8be3aa7/views/view_011_165deg.png", + "_turntable/up_c8be3aa7/views/view_010_150deg.png", + "_turntable/up_c8be3aa7/views/view_009_135deg.png", + "_turntable/up_c8be3aa7/views/view_008_120deg.png", + "_turntable/up_c8be3aa7/views/view_007_105deg.png", + "_turntable/up_c8be3aa7/views/view_006_090deg.png", + "_turntable/up_c8be3aa7/views/view_005_075deg.png", + "_turntable/up_c8be3aa7/views/view_004_060deg.png", + "_turntable/up_c8be3aa7/views/view_003_045deg.png", + "_turntable/up_c8be3aa7/views/view_002_030deg.png", + "_turntable/up_c8be3aa7/views/view_001_015deg.png", + "_turntable/up_c8be3aa7/views/view_000_000deg.png", "up_b8c61991_face.png", "_turntable/bb_01.png/views/view_008_120deg.png", "_turntable/bb_01.png/views/view_007_105deg.png", @@ -2789,8 +2808,6 @@ "20260627_203054_Screenshot_From_2026-06-27_20-04-11.png", "20260627_203044_image.png", "20260627_203032_image.png", - "_turntable/up_c8be3aa7/views/view_002_030deg.png", - "_turntable/up_c8be3aa7/views/view_003_045deg.png", "20260627_201807_12_20260627_201343_image.png", "20260627_201753_Screenshot_From_2026-06-27_12-32-39.png", "20260627_201751_11_20260627_201343_image.png", @@ -2807,18 +2824,12 @@ "20260627_201418_1_20260627_201343_image.png", "20260627_201400_0_20260627_201343_image.png", "20260627_201343_image.png", - "_turntable/up_c8be3aa7/views/view_007_105deg.png", - "_turntable/up_c8be3aa7/views/view_006_090deg.png", - "_turntable/up_c8be3aa7/views/view_005_075deg.png", - "_turntable/up_c8be3aa7/views/view_004_060deg.png", - "_turntable/up_c8be3aa7/views/view_001_015deg.png", "20260627_200711_11_20260627_200330_image.png", "20260627_200506_image.png", "20260627_200422_2_20260627_200330_image.png", "20260627_200414_image.png", "20260627_200405_1_20260627_200330_image.png", "20260627_200330_image.png", - "_turntable/up_c8be3aa7/views/view_000_000deg.png", "20260627_200201_12_20260627_195835_image.png", "20260627_200144_11_20260627_195835_image.png", "20260627_200127_10_20260627_195835_image.png", @@ -7497,18 +7508,35 @@ _lastSelectedGid = gid; localStorage.setItem('lastSelectedGid', gid); - _activeFilmstripTab = 'group'; - ['Group', 'Hidden', 'Source', 'Archived'].forEach(t => { + // Default to 'active' tab, but fallback to 'group' if empty + const activeNames = data.names.filter(name => !fileHidden[name] && !fileIsSource[name] && !fileArchived[name]); + if (activeNames.length > 0) { + _activeFilmstripTab = 'active'; + lbNames = activeNames; + lbUrls = activeNames.map(n => IMAGE_FOLDER + n); + } else { + _activeFilmstripTab = 'group'; + lbUrls = data.urls; + lbNames = data.names; + } + + ['Active', 'Group', 'Hidden', 'Source', 'Archived'].forEach(t => { const el = document.getElementById(`fsTab${t}`); - if (el) el.classList.toggle('active', t.toLowerCase() === 'group'); + if (el) el.classList.toggle('active', t.toLowerCase() === _activeFilmstripTab); }); _multiSelectModeActive = false; _selectedFilenames.clear(); updateMultiSelectUI(); - lbUrls = data.urls; - lbNames = data.names; lbIdx = startIdx !== undefined ? startIdx : (cycleIdx.get(gid) || 0); + // If we are in 'active' tab, ensure lbIdx is valid for lbNames + if (_activeFilmstripTab === 'active' && startIdx !== undefined) { + const targetName = data.names[startIdx]; + const activeIdx = lbNames.indexOf(targetName); + if (activeIdx !== -1) lbIdx = activeIdx; + else lbIdx = 0; // fallback if the image we clicked is not 'active' + } + updateStudio(); document.getElementById('studio').classList.add('open'); // Hide main page scrollbar — nothing in studio scrolls the page @@ -9808,14 +9836,22 @@ const r = await fetch(`${API}/output/_data/system_status.json?t=${Date.now()}`, { signal: AbortSignal.timeout(2000) }); if (!r.ok) return; const data = await r.json(); - if (data.locked && !_lastSystemLockState) { - _lastSystemLockState = true; + if (data.locked) { if (!privacyMode) togglePrivacyMode(); showPrivacyOverlay(); - console.log("[privacy] System lock detected, auto-hiding content"); - showToast('Privacy lock active (System Locked)', 'info'); - } else if (!data.locked) { - _lastSystemLockState = false; + if (!_lastSystemLockState) { + _lastSystemLockState = true; + console.log("[privacy] System lock detected, auto-hiding content"); + showToast('Privacy lock active (System Locked)', 'info'); + } + } else { + if (_lastSystemLockState) { + _lastSystemLockState = false; + // If it was locked by system, and now system is unlocked, + // we automatically unlock if privacyMode is still active. + if (privacyMode) togglePrivacyMode(); + console.log("[privacy] System unlock detected, restoring content"); + } } } catch (e) {} } diff --git a/tour-comfy/edit_api.py b/tour-comfy/edit_api.py index cb13f61..45ee612 100644 --- a/tour-comfy/edit_api.py +++ b/tour-comfy/edit_api.py @@ -10,6 +10,8 @@ Run ComfyUI first (run_comfyui.sh), then this service (start_api.sh). import io import os +os.environ["HF_HUB_OFFLINE"] = "1" +os.environ["TRANSFORMERS_OFFLINE"] = "1" import json import time import uuid @@ -450,6 +452,12 @@ def on_startup(): print(f"[output] mount warning: {e}") # Write initial static data files (synchronous — ensures files exist before first request) _write_all_static() + # Trigger pose index backfill + try: + if _load_pose_estimator(): + build_pose_index() + except Exception: + pass # --- helpers ---------------------------------------------------------------- @@ -568,7 +576,7 @@ def _load_tagger(): cfg = resolve_data_config(model.pretrained_cfg, model=model) transform = create_transform(**cfg) - lpath = huggingface_hub.hf_hub_download(WD_MODEL, "selected_tags.csv") + lpath = huggingface_hub.hf_hub_download(WD_MODEL, "selected_tags.csv", local_files_only=True) with open(lpath, newline="") as f: rows = list(csv.DictReader(f)) # category 0=general 4=character 9=rating @@ -664,6 +672,7 @@ def _load_faceswapper(): model_path = huggingface_hub.hf_hub_download( 'deepinsight/inswapper', 'inswapper_128.onnx', local_dir=os.path.dirname(model_path), + local_files_only=True ) print(f"[faceswap] Downloaded inswapper_128.onnx to {model_path}") except Exception as de: @@ -1294,24 +1303,36 @@ _privacy_lock = threading.Lock() def _privacy_monitor_daemon(): - """Monitors GNOME screen lock via D-Bus gdbus monitor.""" + """Monitors GNOME/Freedesktop screen lock via D-Bus gdbus monitor.""" global _privacy_locked print("[privacy] Starting monitor daemon...") - # Monitor ScreenSaver state changes - cmd = ["gdbus", "monitor", "--session", "--dest", "org.gnome.ScreenSaver", "--object-path", "/org/gnome/ScreenSaver"] - try: - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) - for line in proc.stdout: - # Lines look like: /org/gnome/ScreenSaver: org.gnome.ScreenSaver.ActiveChanged (true,) - if "ActiveChanged" in line: - is_locked = "(true,)" in line - with _privacy_lock: - if _privacy_locked != is_locked: - _privacy_locked = is_locked - print(f"[privacy] System lock state change detected: {is_locked}") - _write_all_static() - except Exception as e: - print(f"[privacy] Monitor error (is gdbus installed?): {e}") + + # We try to monitor both GNOME and Freedesktop ScreenSaver + destinations = [ + ("org.gnome.ScreenSaver", "/org/gnome/ScreenSaver"), + ("org.freedesktop.ScreenSaver", "/org/freedesktop/ScreenSaver") + ] + + def monitor(dest, obj_path): + global _privacy_locked + cmd = ["gdbus", "monitor", "--session", "--dest", dest, "--object-path", obj_path] + try: + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) + for line in proc.stdout: + # GNOME: ActiveChanged (true,) or (false,) + # Freedesktop: Locked or ActiveChanged + if "ActiveChanged" in line or "Locked" in line: + is_locked = "(true,)" in line or "true" in line.lower() + with _privacy_lock: + if _privacy_locked != is_locked: + _privacy_locked = is_locked + print(f"[privacy] System lock state change detected via {dest}: {is_locked}") + _write_all_static() + except Exception as e: + print(f"[privacy] Monitor error for {dest}: {e}") + + for dest, path in destinations: + threading.Thread(target=monitor, args=(dest, path), daemon=True).start() def _write_json(path: str, data) -> None: @@ -4778,22 +4799,37 @@ def _build_pose_index_task(): with _pose_index_lock: idx = _load_pose_index() persons = database.list_persons() - todo = [p[0] for p in persons - if p[0] not in idx + + # p[17] is pose_description, p[18] is pose_skeleton + todo = [p for p in persons + if (p[0] not in idx or p[17] is None or p[18] is None) and (p[12] or "image") != "video" and p[0].lower().endswith(('.png', '.jpg', '.jpeg', '.webp'))] + _pose_index_status.update(running=True, done=0, total=len(todo)) - print(f"[pose] index build: {len(todo)} images to process") + print(f"[pose] index build/backfill: {len(todo)} images to process") dirty = 0 - for fn in todo: + for p in todo: + fn = p[0] try: fpath = os.path.join(output_dir, fn) if os.path.exists(fpath): - best = _best_person(infer(Image.open(fpath).convert("RGB"))) - desc = _pose_descriptor(best) if best is not None else None - if desc is not None: - idx[fn] = desc - dirty += 1 + people = infer(Image.open(fpath).convert("RGB")) + best = _best_person(people) + + pose_desc = None + pose_skel = None + if best is not None: + pose_desc = _describe_pose(best) + pose_skel = json.dumps(best) + desc = _pose_descriptor(best) + if desc is not None: + idx[fn] = desc + dirty += 1 + + # Update DB if missing + if p[17] is None or p[18] is None: + database.upsert_person(fn, pose_description=pose_desc, pose_skeleton=pose_skel) except Exception as e: print(f"[pose] index error for {fn}: {e}") _pose_index_status["done"] += 1 diff --git a/tour-comfy/embeddings.py b/tour-comfy/embeddings.py index b8a8bb4..c3d9015 100644 --- a/tour-comfy/embeddings.py +++ b/tour-comfy/embeddings.py @@ -1,7 +1,9 @@ import torch +import os +os.environ["HF_HUB_OFFLINE"] = "1" +os.environ["TRANSFORMERS_OFFLINE"] = "1" import open_clip from PIL import Image -import os import threading _model = None diff --git a/tour-comfy/watcher.py b/tour-comfy/watcher.py index bd5b241..5455e50 100644 --- a/tour-comfy/watcher.py +++ b/tour-comfy/watcher.py @@ -1,4 +1,6 @@ import os +os.environ["HF_HUB_OFFLINE"] = "1" +os.environ["TRANSFORMERS_OFFLINE"] = "1" import time import json import shutil