#!/usr/bin/env python3 """ Capture output from upstream/main branch. This script captures the output of upstream/main Mainline using NullDisplay and saves it to a JSON file for comparison with sideline branch. """ import argparse import json import sys from pathlib import Path # Add upstream/main to path sys.path.insert(0, "/tmp/upstream_mainline") def capture_upstream_output( output_file: str, frames: int = 60, width: int = 80, height: int = 24, ): """Capture upstream/main output. Args: output_file: Path to save captured output frames: Number of frames to capture width: Terminal width height: Terminal height """ print(f"Capturing upstream/main output...") try: # Import upstream modules from engine import config, themes from engine.display import NullDisplay from engine.fetch import fetch_all, load_cache from engine.scroll import stream from engine.ntfy import NtfyPoller from engine.mic import MicMonitor except ImportError as e: print(f"Error importing upstream modules: {e}") print("Make sure upstream/main is in the Python path") return False # Create a custom NullDisplay that captures frames class CapturingNullDisplay: def __init__(self, width, height, max_frames): self.width = width self.height = height self.max_frames = max_frames self.frame_count = 0 self.frames = [] def init(self, width: int, height: int) -> None: self.width = width self.height = height def show(self, buffer: list[str], border: bool = False) -> None: if self.frame_count < self.max_frames: self.frames.append(list(buffer)) self.frame_count += 1 if self.frame_count >= self.max_frames: raise StopIteration("Frame limit reached") def clear(self) -> None: pass def cleanup(self) -> None: pass def get_frames(self): return self.frames display = CapturingNullDisplay(width, height, frames) # Load items (use cached headlines) items = load_cache() if not items: print("No cached items found, fetching...") result = fetch_all() if isinstance(result, tuple): items, linked, failed = result else: items = result if not items: print("Error: No items available") return False print(f"Loaded {len(items)} items") # Create ntfy poller and mic monitor (upstream uses these) ntfy_poller = NtfyPoller(config.NTFY_TOPIC, reconnect_delay=5, display_secs=30) mic_monitor = MicMonitor() # Run stream for specified number of frames print(f"Capturing {frames} frames...") try: # Run the stream stream( items=items, ntfy_poller=ntfy_poller, mic_monitor=mic_monitor, display=display, ) except StopIteration: print("Frame limit reached") except Exception as e: print(f"Error during capture: {e}") # Continue to save what we have # Get captured frames captured_frames = display.get_frames() print(f"Retrieved {len(captured_frames)} frames from display") # Save to JSON output_path = Path(output_file) output_path.parent.mkdir(parents=True, exist_ok=True) recording_data = { "version": 1, "preset": "upstream_demo", "display": "null", "width": width, "height": height, "frame_count": len(captured_frames), "frames": [ { "frame_number": i, "buffer": frame, "width": width, "height": height, } for i, frame in enumerate(captured_frames) ], } with open(output_path, "w") as f: json.dump(recording_data, f, indent=2) print(f"Saved recording to {output_path}") return True def main(): parser = argparse.ArgumentParser(description="Capture upstream/main output") parser.add_argument( "--output", default="output/upstream_demo.json", help="Output file path (default: output/upstream_demo.json)", ) parser.add_argument( "--frames", type=int, default=60, help="Number of frames to capture (default: 60)", ) parser.add_argument( "--width", type=int, default=80, help="Terminal width (default: 80)", ) parser.add_argument( "--height", type=int, default=24, help="Terminal height (default: 24)", ) args = parser.parse_args() success = capture_upstream_output( output_file=args.output, frames=args.frames, width=args.width, height=args.height, ) return 0 if success else 1 if __name__ == "__main__": exit(main())