Files
plugin-helper/src/plugin_helper/scanner.py
T
2026-06-28 14:36:14 -07:00

65 lines
2.2 KiB
Python

from __future__ import annotations
from pathlib import Path
from typing import Any
from .fsutil import sha256_file
SCAN_DIRS = ("Plugins", "Libs", "IPA/Pending")
BOOTSTRAP_DIRS = ("Libs", "IPA")
BOOTSTRAP_ROOT_GLOBS = ("winhttp.dll", "IPA.exe*")
def scan_instance(instance_path: Path, include_hashes: bool = False) -> dict[str, Any]:
files: list[dict[str, Any]] = []
for dirname in SCAN_DIRS:
root = instance_path / dirname
if not root.exists():
continue
for path in sorted(item for item in root.rglob("*") if item.is_file()):
rel = path.relative_to(instance_path).as_posix()
entry: dict[str, Any] = {"path": rel, "size": path.stat().st_size}
if include_hashes:
entry["sha256"] = sha256_file(path)
files.append(entry)
return {
"instancePath": str(instance_path),
"files": files,
"counts": {
"files": len(files),
"plugins": sum(1 for item in files if item["path"].startswith("Plugins/")),
"libs": sum(1 for item in files if item["path"].startswith("Libs/")),
"pending": sum(1 for item in files if item["path"].startswith("IPA/Pending/")),
},
}
def scan_bootstrap_files(instance_path: Path) -> list[dict[str, Any]]:
files_by_path: dict[str, dict[str, Any]] = {}
for pattern in BOOTSTRAP_ROOT_GLOBS:
for path in sorted(instance_path.glob(pattern)):
if not path.is_file():
continue
rel = path.relative_to(instance_path).as_posix()
files_by_path[rel] = {
"path": rel,
"size": path.stat().st_size,
"sha256": sha256_file(path),
}
for dirname in BOOTSTRAP_DIRS:
root = instance_path / dirname
if not root.exists():
continue
for path in sorted(item for item in root.rglob("*") if item.is_file()):
rel = path.relative_to(instance_path).as_posix()
files_by_path[rel] = {
"path": rel,
"size": path.stat().st_size,
"sha256": sha256_file(path),
}
return [files_by_path[path] for path in sorted(files_by_path)]