Files
DemonGatanjieu-Anomalous_Mo…/api/scan_summary.py
T
DemonGatanjieuandClaude Opus 5.5 fb21e6c6e9 feat(scan): the scan page, scan runner and scanner changes (completes 5af9b6d)
5af9b6d was committed with only the removal of ui_scan_wizard.js staged;
this adds everything its message describes, so the branch works again:

- ui_scan_page.js / scan_runner.js / 18-scan-page.css: the scan page with
  matched / unmatched / not-scanned counts, one scan button, "look up the
  unmatched models again", progress in the page, Advanced options.
- api/scan_summary.py: GET /anomalous/scan_summary.
- scraper.py: --retry-unmatched (saved hash when the file is unchanged,
  covers kept); display names set in the editor survive scans.
- api/scanner.py: one request-to-switch mapping (_scraper_flags) and one
  cache-clearing helper for both scan routes.
- model_identity.py: sidecar_info / is_unmatched shared by scraper and API.
- Shell wiring (rail, Home, panel lists, close), the provenance switch in
  Settings -> Model settings, locale keys, docs and CHANGELOG.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 01:14:54 +08:00

44 lines
1.5 KiB
Python

"""What the scan page shows before a scan: how many models are matched on Civitai, unmatched, or new."""
import asyncio
import os
from aiohttp import web
from .folder_types import get_active_scan_paths
try:
from ..model_identity import is_unmatched, sidecar_info
except ImportError: # loaded outside the package (tests)
from model_identity import is_unmatched, sidecar_info
def _summarize():
"""Counts the files the scanner handles (.safetensors in the active scan folders), each once."""
seen = set()
total = matched = unmatched = 0
for base_dir in get_active_scan_paths():
for root, _dirs, files in os.walk(base_dir):
for filename in files:
if not filename.endswith(".safetensors"):
continue
path = os.path.join(root, filename)
real = os.path.realpath(path)
if real in seen:
continue
seen.add(real)
total += 1
info = sidecar_info(os.path.splitext(path)[0])
if info is None:
continue
if is_unmatched(info):
unmatched += 1
else:
matched += 1
return {"total": total, "matched": matched, "unmatched": unmatched, "new": total - matched - unmatched}
async def api_scan_summary(request):
"""GET /anomalous/scan_summary - {total, matched, unmatched, new}."""
return web.json_response(await asyncio.to_thread(_summarize))