"""Read back what ros2 bag already knows about a recording — duration, message counts, per-topic counts — instead of just listing directory names. Also a plain on-disk size sum (metadata.yaml doesn't carry a byte size in the ros2 bag version installed here).""" from __future__ import annotations import os from pathlib import Path from typing import Optional import yaml def dir_size_bytes(path: Path) -> int: total = 0 try: for entry in path.iterdir(): if entry.is_file(): total += entry.stat().st_size except OSError: pass return total def read_bag_metadata(bag_dir: Path) -> Optional[dict]: """Returns None if metadata.yaml is missing (e.g. bag was killed mid-write before rosbag2 flushed it) or unparseable — a real, if incomplete, state the operator should be able to see rather than a crash.""" meta_path = bag_dir / "metadata.yaml" if not meta_path.exists(): return None try: with open(meta_path, encoding="utf-8") as f: raw = yaml.safe_load(f) info = raw["rosbag2_bagfile_information"] except Exception: return None duration_s = info.get("duration", {}).get("nanoseconds", 0) / 1e9 message_count = info.get("message_count", 0) topics = [] for entry in info.get("topics_with_message_count", []): tm = entry.get("topic_metadata", {}) topics.append({ "name": tm.get("name", "?"), "type": tm.get("type", "?"), "message_count": entry.get("message_count", 0), }) return { "duration_s": round(duration_s, 1), "message_count": message_count, "topics": topics, "size_bytes": dir_size_bytes(bag_dir), }