feat: pool rebuild_manifest recovery
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+16
-2
@@ -1,6 +1,7 @@
|
|||||||
"""Pure storage layer for the Image Pool node. Stdlib only — no torch, no comfy."""
|
"""Pure storage layer for the Image Pool node. Stdlib only — no torch, no comfy."""
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
@@ -112,5 +113,18 @@ def set_label(base_dir, pool_id, index, label):
|
|||||||
|
|
||||||
|
|
||||||
def rebuild_manifest(base_dir, pool_id):
|
def rebuild_manifest(base_dir, pool_id):
|
||||||
# Temporary stub — replaced in Task 7.
|
d = pool_dir(base_dir, pool_id)
|
||||||
return empty_manifest()
|
m = empty_manifest()
|
||||||
|
if not d.exists():
|
||||||
|
return m
|
||||||
|
imgs = sorted(p.name for p in d.glob("img_*.png") if not p.name.endswith(".mask.png"))
|
||||||
|
max_seq = 0
|
||||||
|
for name in imgs:
|
||||||
|
match = re.match(r"img_(\d+)\.png$", name)
|
||||||
|
seq = int(match.group(1)) if match else 0
|
||||||
|
max_seq = max(max_seq, seq)
|
||||||
|
mask_name = name.replace(".png", ".mask.png")
|
||||||
|
mask = mask_name if (d / mask_name).exists() else None
|
||||||
|
m["slots"].append({"image": name, "mask": mask, "label": "", "added": 0})
|
||||||
|
m["next_seq"] = max_seq + 1
|
||||||
|
return m
|
||||||
|
|||||||
@@ -97,3 +97,25 @@ def test_set_label_out_of_range_noop(tmp_path):
|
|||||||
pool.add_image(str(tmp_path), "p1", b"a", ts=1)
|
pool.add_image(str(tmp_path), "p1", b"a", ts=1)
|
||||||
m = pool.set_label(str(tmp_path), "p1", 5, "x")
|
m = pool.set_label(str(tmp_path), "p1", 5, "x")
|
||||||
assert m["slots"][0]["label"] == ""
|
assert m["slots"][0]["label"] == ""
|
||||||
|
|
||||||
|
|
||||||
|
def test_rebuild_from_files(tmp_path):
|
||||||
|
d = tmp_path / "p1"
|
||||||
|
d.mkdir()
|
||||||
|
(d / "img_0001.png").write_bytes(b"a")
|
||||||
|
(d / "img_0001.mask.png").write_bytes(b"m")
|
||||||
|
(d / "img_0003.png").write_bytes(b"c") # gap on purpose
|
||||||
|
m = pool.rebuild_manifest(str(tmp_path), "p1")
|
||||||
|
assert [s["image"] for s in m["slots"]] == ["img_0001.png", "img_0003.png"]
|
||||||
|
assert m["slots"][0]["mask"] == "img_0001.mask.png"
|
||||||
|
assert m["slots"][1]["mask"] is None
|
||||||
|
assert m["next_seq"] == 4 # max seq 3 + 1
|
||||||
|
assert m["active"] == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_read_corrupt_manifest_triggers_rebuild(tmp_path):
|
||||||
|
d = tmp_path / "p1"; d.mkdir()
|
||||||
|
(d / "img_0001.png").write_bytes(b"a")
|
||||||
|
(d / "manifest.json").write_text("{ not json")
|
||||||
|
m = pool.read_manifest(str(tmp_path), "p1")
|
||||||
|
assert [s["image"] for s in m["slots"]] == ["img_0001.png"]
|
||||||
|
|||||||
Reference in New Issue
Block a user