#!/usr/bin/env python3 import csv from pathlib import Path import sys import json import shlex import subprocess def get_chapters(json_path): """Loads the JSON file and returns all chapters.""" with open(json_path, 'r', encoding='utf-8') as f: data = json.load(f) # The chapters are under video -> chapters video = data.get('video', {}) chapters = video.get('chapters', []) if not chapters: print("No chapters found.") return [] print(f"Found {len(chapters)} chapter(s) in video '{video.get('title')}':\n") for i, chapter in enumerate(chapters, 1): start_sec = chapter.get('startMilliseconds', 0) / 1000 length_sec = chapter.get('lengthMilliseconds', 0) / 1000 #print(f"{i}. {chapter.get('description', 'No Title')}") #print(f" - Type: {chapter.get('type')}") #print(f" - Start: {start_sec}s ({chapter.get('startMilliseconds')}ms)") #print(f" - Length: {length_sec}s ({chapter.get('lengthMilliseconds')}ms)") #print(f" - Game: {chapter.get('gameDisplayName')} (ID: {chapter.get('gameId')})") #print() return chapters def get_video_id_from_chat(json_path: str | Path) -> str | None: """Gets the id inside video:{ } - from the chat json you download""" try: with open(json_path, 'r', encoding='utf-8') as f: data = json.load(f) return data.get("video", {}).get("id") except Exception as e: print(f"Could not read video.id from {json_path}: {e}") return None def write_csv(data: list, file_name: str): # Open file with newline='' to prevent extra blank rows across platforms with open(file_name, "w", newline="", encoding="utf-8") as file: writer = csv.writer(file) # Write all rows at once writer.writerows(data) def run_command(cmd_str: str, progress_prefix: str = "Progress", look_for: list = ["frame=", "time=", "fps=", "Rendering frame"]) -> bool: """Runs a system command, streams its output live, and reports errors on failure.""" args = shlex.split(cmd_str) # Redirect stderr to stdout to catch all logging/progress in one stream process = subprocess.Popen( args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1 ) print(f"Executing: {cmd_str[:90]}...") # Maintain a small buffer history to display context if a crash occurs output_history = [] # Stream the output live to the terminal while True: line = process.stdout.readline() if not line and process.poll() is not None: break if line: clean_line = line.strip() output_history.append(clean_line) # Keep history for error reporting # Keep history slim by only keeping the last 20 lines if len(output_history) > 20: output_history.pop(0) # Only print updates that show progress metrics to keep terminal clean if any(metric in clean_line for metric in look_for): sys.stdout.write(f"\r[{progress_prefix}] {clean_line}") sys.stdout.flush() elif "Error" in clean_line or "failed" in clean_line: print(f"\n[Alert] {clean_line}") print("\n") # New line after process finishes # Evaluate success status success = (process.returncode == 0) if not success: print(f"❌ Command failed with exit code: {process.returncode}") print("--- Technical Error Details (Last 5 lines of output) ---") # Print the last 5 captured lines to show the exact point of failure for error_line in output_history[-5:]: print(f" > {error_line}") print("---------------------------------------------------------") return success