#!/usr/bin/env python3 """ Start chat downloader standalone for testing without recording video. Usage: python run_chat_only.py --username vinesauce [--output path] [--max-messages N] [--timeout S] [--verbose] This script uses the project's `ConfigManager` and `FileManager` to create appropriate directories and then starts the chat downloader in a background thread. Press Ctrl+C to stop. """ import argparse import time from datetime import datetime import os from colorama import Fore, Style from modules.config import ConfigManager from modules.file_manager import FileManager from modules.utils import get_ffmpeg_executable, get_twitch_downloader_executable, detect_operating_system from modules.downloader import ContentDownloader from modules.stream_monitor import StreamMonitor def main(): parser = argparse.ArgumentParser(description='Run chat downloader standalone for testing') parser.add_argument('--username', '-u', required=True, help='Twitch username/channel name') parser.add_argument('--output', '-o', help='Output JSON path (optional)') parser.add_argument('--max-messages', type=int, default=None, help='Max messages to capture') parser.add_argument('--timeout', type=float, default=None, help='Timeout in seconds') parser.add_argument('--verbose', action='store_true', help='Show verbose/chat previews') parser.add_argument('--foreground', action='store_true', help='Run downloader in foreground (blocking)') parser.add_argument('--use-chat-downloader-primary', action='store_true', help='Use chat_downloader as primary method') parser.add_argument('--use-chat-downloader-fallback', dest='use_chat_downloader_fallback', action='store_true', help='Allow chat_downloader as fallback (default)') parser.add_argument('--no-chat-downloader-fallback', dest='use_chat_downloader_fallback', action='store_false', help='Disable chat_downloader fallback') args = parser.parse_args() cfg = ConfigManager() config = cfg.load_streamer_config(args.username) # Apply overrides from CLI if args.use_chat_downloader_primary: config['useChatDownloaderPrimary'] = True if args.use_chat_downloader_fallback is not None: config['useChatDownloaderFallback'] = bool(args.use_chat_downloader_fallback) # Ensure directories exist (use configured archive root path) fm = FileManager(root_path=config.get('root_path', 'archive'), username=args.username, config=config) fm.initialize_directories() # Build default output path if not provided if args.output: json_path = args.output else: ts = datetime.now().strftime('%Y%m%d_%Hh%Mm%Ss') json_path = str(fm.chat_json_path / f"CHAT_TEST_{args.username}_{ts}.json") # Initialize downloader os_type = detect_operating_system() twitch_downloader_path = get_twitch_downloader_executable(os_type) ffmpeg_path = get_ffmpeg_executable(os_type) downloader = ContentDownloader(twitch_downloader_path=twitch_downloader_path, ffmpeg_path=ffmpeg_path, config=config) print(f"{Fore.CYAN}Starting standalone chat downloader for {args.username}{Style.RESET_ALL}") print(f"Output path: {json_path}") stop_requested = {'stop': False} def shutdown_check(): return stop_requested['stop'] # Prepare stream monitor stream_monitor = StreamMonitor(args.username) # If chat downloads are disabled in config, enter monitoring mode instead if not downloader.download_live_chat: print(f"{Fore.YELLOW}⚠ downloadLiveCHAT is disabled in config - entering monitoring mode for {args.username}{Style.RESET_ALL}") try: while True: try: is_live = stream_monitor.is_user_live() if is_live: print(f"{Fore.GREEN}✓ {args.username} is live! Exiting monitor. Run the archiver to record video.{Style.RESET_ALL}") break else: print(f"{Fore.CYAN}{args.username} is offline - checking again in 30s...{Style.RESET_ALL}") except Exception as e: print(f"{Fore.YELLOW}⚠ Could not check stream status: {e}{Style.RESET_ALL}") time.sleep(30) except KeyboardInterrupt: print('\nKeyboard interrupt received; stopping monitor...') return # If chat download is enabled, but the stream is currently offline, wait until it goes live try: try: if not stream_monitor.is_user_live(): print(f"{Fore.CYAN}{args.username} is currently offline - waiting for live stream to start...{Style.RESET_ALL}") while not stream_monitor.is_user_live(): time.sleep(10) except Exception: # If we cannot determine live status, proceed to start chat downloader anyway pass except KeyboardInterrupt: print('\nKeyboard interrupt received; exiting...') return # Start thread (stream_monitor passed so downloader can stop when stream ends) thread = downloader.start_chat_downloader_thread( args.username, json_path, shutdown_check=shutdown_check, stream_monitor=stream_monitor, verbose=args.verbose ) try: if args.foreground: # Run download directly in foreground print('Running in foreground; this will block until download completes or interrupted') success = downloader.download_live_chat_with_chat_downloader( args.username, json_path, max_messages=args.max_messages, timeout=args.timeout, shutdown_check=shutdown_check, stream_monitor=None, verbose=args.verbose ) print('Done, success=' + str(success)) else: # Wait for thread to finish or until interrupted while thread.is_alive(): time.sleep(0.5) except KeyboardInterrupt: print('\nKeyboard interrupt received; stopping downloader...') stop_requested['stop'] = True thread.join(timeout=5) if __name__ == '__main__': main()