Generate files.json manifest for data files.
(example_name: str, output_dir: Path)
| 1656 | |
| 1657 | |
| 1658 | def generate_manifest(example_name: str, output_dir: Path) -> None: |
| 1659 | """Generate files.json manifest for data files.""" |
| 1660 | example_dir = PROJECT_ROOT / "examples" / example_name |
| 1661 | data_extensions = frozenset( |
| 1662 | (".json", ".csv", ".txt", ".cfg", ".bin", ".dat", ".mp3", ".wav") |
| 1663 | ) |
| 1664 | data_files: list[dict[str, str | int]] = [] |
| 1665 | _SKIP = frozenset(("fastled_js", ".build")) |
| 1666 | |
| 1667 | # Use os.scandir with directory pruning — avoids walking fastled_js/ and .build/ |
| 1668 | # On Windows, DirEntry.stat() reuses FindFirstFile data (no extra syscall) |
| 1669 | example_str = str(example_dir) |
| 1670 | example_len = len(example_str) |
| 1671 | if not example_str.endswith(os.sep): |
| 1672 | example_len += 1 |
| 1673 | stack = [example_str] |
| 1674 | while stack: |
| 1675 | current = stack.pop() |
| 1676 | try: |
| 1677 | with os.scandir(current) as it: |
| 1678 | for entry in it: |
| 1679 | try: |
| 1680 | if entry.is_dir(follow_symlinks=False): |
| 1681 | if entry.name not in _SKIP: |
| 1682 | stack.append(entry.path) |
| 1683 | elif entry.is_file(follow_symlinks=False): |
| 1684 | _, ext = os.path.splitext(entry.name) |
| 1685 | if ext.lower() in data_extensions: |
| 1686 | rel_path = entry.path[example_len:] |
| 1687 | size = entry.stat(follow_symlinks=False).st_size |
| 1688 | data_files.append({"path": rel_path, "size": size}) |
| 1689 | except OSError: |
| 1690 | pass |
| 1691 | except OSError: |
| 1692 | pass |
| 1693 | |
| 1694 | files_json = output_dir / "files.json" |
| 1695 | with open(files_json, "w", encoding="utf-8") as f: |
| 1696 | json.dump(data_files, f, indent=2) |
| 1697 | |
| 1698 | |
| 1699 | def generate_asset_manifest(example_name: str, output_dir: Path) -> None: |