Merge branch 'claude/confident-bhaskara'

This commit is contained in:
JasonHoku
2026-02-15 02:58:15 -08:00
3 changed files with 137 additions and 12 deletions
+12 -2
View File
@@ -528,7 +528,9 @@ async def scan_directory_route(request):
None, scan_directory_for_images, directory_path
)
print(f"[DirScanner] Found {stats['total']} images ({stats['with_metadata']} with metadata, {stats['skipped']} skipped)")
from_manifest = stats.get("from_manifest", 0)
manifest_info = f", {from_manifest} from existing manifest" if from_manifest > 0 else ""
print(f"[DirScanner] Found {stats['total']} images ({stats['with_metadata']} with metadata, {stats['skipped']} skipped{manifest_info})")
# Build manifest
manifest = {
@@ -576,14 +578,22 @@ async def scan_directory_route(request):
# Whitelist the directory for the view_external route
real_dir = os.path.realpath(directory_path)
_whitelisted_directories.add(real_dir)
print(f"[DirScanner] Whitelisted directory: {real_dir}")
# Also whitelist images/ subdirectory if it exists (generated sessions store images there)
images_subdir = os.path.realpath(os.path.join(directory_path, "images"))
if os.path.isdir(images_subdir):
_whitelisted_directories.add(images_subdir)
print(f"[DirScanner] Whitelisted directory: {real_dir} (+ images/)")
else:
print(f"[DirScanner] Whitelisted directory: {real_dir}")
from_manifest = stats.get("from_manifest", 0)
return web.json_response({
"session_name": session_name,
"item_count": len(items),
"with_metadata": stats["with_metadata"],
"without_metadata": len(items) - stats["with_metadata"],
"skipped": stats["skipped"],
"from_manifest": from_manifest,
})
except ValueError as e:
+113 -7
View File
@@ -241,6 +241,83 @@ def parse_a1111_parameters(parameters_text):
return result
# =============================================================================
# EXISTING MANIFEST DETECTION
# =============================================================================
def _extract_filename_from_url(file_url):
"""
Extract the image filename from a manifest item's file URL.
Handles both generated URLs (/view?filename=xxx&...) and external URLs (/config_tester/view_external?path=...).
"""
if not file_url:
return None
# Generated session format: /view?filename=img_xxx.webp&type=output&subfolder=...
match = re.search(r'filename=([^&]+)', file_url)
if match:
return urllib.parse.unquote(match.group(1))
# External scan format: /config_tester/view_external?path=...
match = re.search(r'path=([^&]+)', file_url)
if match:
decoded = urllib.parse.unquote(match.group(1))
return os.path.basename(decoded)
return None
def _load_existing_manifest_items(directory_path):
"""
Check if a directory (or its parent) contains a manifest.json generated by this node.
If found, returns a dict mapping image filename -> item metadata.
Checks:
1. {directory_path}/manifest.json (user scanned the session root)
2. {directory_path}/../manifest.json (user scanned the images/ subdirectory)
Returns:
tuple: (items_by_filename dict, manifest_meta dict or None)
"""
candidates = [
os.path.join(directory_path, "manifest.json"),
]
# If the user is scanning an "images" subdirectory, check the parent for manifest
if os.path.basename(directory_path).lower() == "images":
candidates.append(os.path.join(directory_path, "..", "manifest.json"))
for manifest_path in candidates:
manifest_path = os.path.normpath(manifest_path)
if not os.path.isfile(manifest_path):
continue
try:
with open(manifest_path, "r") as f:
manifest = json.load(f)
except Exception as e:
print(f"[DirScanner] Warning: Could not read {manifest_path}: {e}")
continue
# Validate it looks like our manifest (must have "items" list)
items_list = manifest.get("items")
if not isinstance(items_list, list) or len(items_list) == 0:
continue
# Build lookup: filename -> item data
items_by_filename = {}
for item in items_list:
file_url = item.get("file", "")
filename = _extract_filename_from_url(file_url)
if filename:
items_by_filename[filename] = item
if items_by_filename:
print(f"[DirScanner] Found existing manifest at {manifest_path} with {len(items_by_filename)} items")
return items_by_filename, manifest.get("meta")
return {}, None
# =============================================================================
# DIRECTORY SCANNER
# =============================================================================
@@ -265,16 +342,26 @@ def scan_directory_for_images(directory_path, max_items=5000):
raise ValueError(f"Directory not found: {directory_path}")
items = []
stats = {"total": 0, "with_metadata": 0, "skipped": 0}
stats = {"total": 0, "with_metadata": 0, "skipped": 0, "from_manifest": 0}
# Check for existing manifest.json generated by this node
manifest_items, manifest_meta = _load_existing_manifest_items(directory_path)
# Collect image files, sorted by name for consistent ordering
# Also scan images/ subdirectory if it exists (generated sessions store images there)
image_files = []
scan_dirs = [directory_path]
images_subdir = os.path.join(directory_path, "images")
if os.path.isdir(images_subdir):
scan_dirs.append(images_subdir)
try:
for entry in os.scandir(directory_path):
if entry.is_file():
ext = os.path.splitext(entry.name)[1].lower()
if ext in IMAGE_EXTENSIONS:
image_files.append(entry.path)
for scan_dir in scan_dirs:
for entry in os.scandir(scan_dir):
if entry.is_file():
ext = os.path.splitext(entry.name)[1].lower()
if ext in IMAGE_EXTENSIONS:
image_files.append(entry.path)
except PermissionError:
raise ValueError(f"Permission denied: {directory_path}")
@@ -285,13 +372,29 @@ def scan_directory_for_images(directory_path, max_items=5000):
for file_path in image_files:
stats["total"] += 1
filename = os.path.basename(file_path)
# If we have manifest data for this image, use it (much richer metadata)
if filename in manifest_items:
item = manifest_items[filename].copy()
# Rewrite file URL to use the external view endpoint
encoded_path = urllib.parse.quote(file_path, safe="")
item["file"] = f"/config_tester/view_external?path={encoded_path}"
item["source_file"] = filename
# Ensure it has an ID
if "id" not in item:
item["id"] = int(time.time() * 100000) + random.randint(0, 1000)
items.append(item)
stats["from_manifest"] += 1
continue
# No manifest entry — fall through to normal image processing
try:
item = _process_single_image(file_path, directory_path)
if item:
items.append(item)
except Exception as e:
print(f"[DirScanner] Warning: Skipping {os.path.basename(file_path)}: {e}")
print(f"[DirScanner] Warning: Skipping {filename}: {e}")
stats["skipped"] += 1
continue
@@ -300,6 +403,9 @@ def scan_directory_for_images(directory_path, max_items=5000):
1 for item in items if item.get("model", "Unknown") != "Unknown"
)
if stats["from_manifest"] > 0:
print(f"[DirScanner] Used existing manifest for {stats['from_manifest']}/{stats['total']} images")
return items, stats
+12 -3
View File
@@ -346,9 +346,18 @@ async function scanDirectory() {
const result = await response.json();
if (response.ok) {
const metaInfo = result.with_metadata > 0
? `${result.with_metadata} with metadata`
: 'no metadata found';
const fromManifest = result.from_manifest || 0;
let metaInfo;
if (fromManifest > 0) {
metaInfo = `${fromManifest} from manifest`;
if (result.with_metadata > fromManifest) {
metaInfo += `, ${result.with_metadata - fromManifest} with embedded metadata`;
}
} else {
metaInfo = result.with_metadata > 0
? `${result.with_metadata} with metadata`
: 'no metadata found';
}
if (statusEl) {
statusEl.innerText = `Found ${result.item_count} images (${metaInfo}). Loading...`;