#!/usr/bin/env python3 """Delete failed (near-silent) generations and rename every SFX candidate to a canonical `SFX---.v.mp3` scheme taken from the contract's SFX ledger, keeping processed files, sidecars and raw archives aligned. """ from __future__ import annotations import json import re from pathlib import Path ROOT = Path(__file__).resolve().parents[2] CANDIDATES = ROOT / "docs/audio/sfx/candidates/sfx" RAW = ROOT / "docs/audio/sfx/candidates/sfx/raw" CONTRACT = ROOT / "docs/04_ART_ANIMATION_AUDIO.md" FAILED = [ "SFX-EQP-015-cloth_wrap_drawn_back_from_an_artifact.v01.mp3", "SFX-ORD-002-tile_moved_a_short_distance_across_felt.v01.mp3", "SFX-ORD-004-tile_bumped_against_a_blocked_edge_and_r.v01.mp3", "SFX-ORD-015-new_tile_added_to_the_board_with_a_firm_.v01.mp3", "SFX-RES-010-rope_snapped_taut_and_the_channel_releas.v01.mp3", "SFX-UI-003-muted_cloth-covered_tap_that_goes_nowher.v01.mp3", "SFX-UI-019-brass_stamp_lifted_away_before_contact_a.v01.mp3", "SFX-UI-023-cloth_wiped_across_a_wooden_desk.v01.mp3", ] PATTERN = re.compile(r"^(SFX-(?:UI|SHP|EQP|ORD|RES|RUN)-\d{3})-.*?\.(v\d+)\.mp3$") def load_event_names() -> dict[str, str]: names: dict[str, str] = {} for line in CONTRACT.read_text().splitlines(): match = re.match(r"^(SFX-(?:UI|SHP|EQP|ORD|RES|RUN)-\d{3})\s+(\S+)\s*$", line.strip()) if match: names[match.group(1)] = match.group(2) return names def main() -> int: names = load_event_names() removed = 0 for name in FAILED: for path in (CANDIDATES / name, CANDIDATES / f"{name}.meta.json", RAW / name): if path.exists(): path.unlink() if path.parent == CANDIDATES: removed += 1 renamed = 0 for path in sorted(CANDIDATES.glob("*.mp3")): match = PATTERN.match(path.name) if not match: continue event_id, variant = match.group(1), match.group(2) event_name = names.get(event_id) if not event_name: print(f"WARN no contract name for {event_id}") continue target = path.with_name(f"{event_id}-{event_name}.{variant}.mp3") if target == path: continue if target.exists(): print(f"SKIP collision {target.name}") continue path.rename(target) sidecar = path.with_name(path.name + ".meta.json") if sidecar.exists(): sidecar.rename(target.with_name(target.name + ".meta.json")) raw_source = RAW / path.name if raw_source.exists(): raw_source.rename(RAW / target.name) renamed += 1 for sidecar in sorted(CANDIDATES.glob("*.meta.json")): text = sidecar.read_text() try: record = json.loads(text) except json.JSONDecodeError: continue record["path"] = str(CANDIDATES / sidecar.name.removesuffix(".meta.json")) sidecar.write_text(json.dumps(record, ensure_ascii=False, indent=1)) print(f"removed_failed={removed} renamed={renamed} total={len(list(CANDIDATES.glob('*.mp3')))}") return 0 if __name__ == "__main__": raise SystemExit(main())