#!/usr/bin/env python3 """ Video Player Setup and Configuration Script Interactive setup for configuring the video player system """ import os import sys import json import time import subprocess from pathlib import Path from typing import Dict, List, Optional import logging class VideoPlayerSetup: """Interactive setup for Video Player system""" def __init__(self): self.config_dir = Path("/etc/video_player") self.install_dir = Path("/opt/video_player") self.video_folder = Path("/home/pi/Videos") self.logger = self.setup_logging() def setup_logging(self): """Setup logging for setup script""" logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s' ) return logging.getLogger(__name__) def print_header(self, title: str): """Print formatted header""" print("\n" + "="*60) print(f" {title}") print("="*60) def print_section(self, title: str): """Print formatted section header""" print(f"\n--- {title} ---") def get_user_input(self, prompt: str, default: str = None, required: bool = True) -> str: """Get user input with optional default value""" if default: full_prompt = f"{prompt} [{default}]: " else: full_prompt = f"{prompt}: " while True: response = input(full_prompt).strip() if response: return response elif default: return default elif not required: return "" else: print("This field is required. Please enter a value.") def get_yes_no(self, prompt: str, default: bool = True) -> bool: """Get yes/no input from user""" default_text = "Y/n" if default else "y/N" response = input(f"{prompt} [{default_text}]: ").strip().lower() if not response: return default return response in ['y', 'yes', '1', 'true'] def get_number(self, prompt: str, min_val: int = None, max_val: int = None, default: int = None) -> int: """Get number input from user with validation""" while True: try: if default is not None: response = input(f"{prompt} [{default}]: ").strip() if not response: return default else: response = input(f"{prompt}: ").strip() number = int(response) if min_val is not None and number < min_val: print(f"Value must be at least {min_val}") continue if max_val is not None and number > max_val: print(f"Value must be at most {max_val}") continue return number except ValueError: print("Please enter a valid number.") def check_system_requirements(self) -> bool: """Check if system meets requirements""" self.print_section("System Requirements Check") # Check if running on Raspberry Pi try: with open('/proc/cpuinfo', 'r') as f: cpuinfo = f.read() if 'Raspberry Pi' not in cpuinfo: print("⚠️ Warning: This system may not be a Raspberry Pi") if not self.get_yes_no("Continue anyway?", False): return False except: print("⚠️ Warning: Could not read CPU info") # Check if running as root if os.geteuid() != 0: print("❌ Error: This script must be run as root (use sudo)") return False # Check required directories required_dirs = [self.config_dir, self.install_dir] for directory in required_dirs: if not directory.exists(): print(f"❌ Error: Required directory not found: {directory}") return False # Check required files required_files = [ self.install_dir / "video_player.py", self.install_dir / "ir_remote.py", self.install_dir / "config_manager.py" ] for file_path in required_files: if not file_path.exists(): print(f"❌ Error: Required file not found: {file_path}") return False print("✅ System requirements check passed") return True def configure_video_settings(self) -> Dict: """Configure video playback settings""" self.print_section("Video Playback Configuration") config = {} # Video folder print(f"Current video folder: {self.video_folder}") if not self.get_yes_no("Use default video folder?", True): config['video_folder'] = self.get_user_input("Enter video folder path") else: config['video_folder'] = str(self.video_folder) # Default channel config['default_channel'] = self.get_number( "Default channel number to start on", min_val=1, default=1 ) # Auto play config['auto_play'] = self.get_yes_no("Auto-play video on startup?", True) # Display settings config['fullscreen'] = self.get_yes_no("Start in fullscreen mode?", True) if not config['fullscreen']: config['window_width'] = self.get_number( "Window width", min_val=640, default=1920 ) config['window_height'] = self.get_number( "Window height", min_val=480, default=1080 ) config['hide_cursor'] = self.get_yes_no("Hide mouse cursor?", True) return config def configure_audio_settings(self) -> Dict: """Configure audio settings""" self.print_section("Audio Configuration") config = {} # Audio device config['audio_device'] = self.get_user_input( "Audio device (default for system default)", default="default" ) # Volume config['volume'] = self.get_number( "Default volume (0-100)", min_val=0, max_val=100, default=50 ) # Mute on start config['mute_on_start'] = self.get_yes_no("Start muted?", False) return config def configure_channel_settings(self) -> Dict: """Configure channel system settings""" self.print_section("Channel System Configuration") config = {} # Channel timeout config['channel_timeout'] = self.get_number( "Channel display timeout (seconds)", min_val=1, max_val=10, default=3 ) # Multi-digit timeout config['multi_digit_timeout'] = self.get_number( "Multi-digit channel input timeout (seconds)", min_val=1, max_val=5, default=1 ) # Channel assignment method print("Channel assignment methods:") print("1. alphabetical - Sort videos alphabetically") print("2. manual - Manual channel assignment") print("3. custom - Custom order from file") method_choice = self.get_number( "Choose assignment method (1-3)", min_val=1, max_val=3, default=1 ) methods = {1: "alphabetical", 2: "manual", 3: "custom"} config['channel_assignment_method'] = methods[method_choice] return config def configure_ir_settings(self) -> Dict: """Configure IR remote settings""" self.print_section("IR Remote Configuration") config = {} # GPIO pin config['ir_pin'] = self.get_number( "GPIO pin for IR receiver (1-40)", min_val=1, max_val=40, default=18 ) # IR protocols print("Supported IR protocols:") print("1. NEC (most common)") print("2. RC5") print("3. Both NEC and RC5") protocol_choice = self.get_number( "Choose IR protocol support (1-3)", min_val=1, max_val=3, default=1 ) protocols = { 1: ["NEC"], 2: ["RC5"], 3: ["NEC", "RC5"] } config['ir_protocols'] = protocols[protocol_choice] # IR repeat delay config['ir_repeat_delay'] = self.get_number( "IR repeat delay (seconds)", min_val=1, max_val=10, default=1 ) / 10.0 # Convert to decimal return config def configure_vlc_settings(self) -> Dict: """Configure VLC player settings""" self.print_section("VLC Player Configuration") config = {} # VLC options vlc_options = [ "--fullscreen", "--no-video-title-show", "--no-audio-display", "--no-osd", "--quiet" ] print("Default VLC options:") for i, option in enumerate(vlc_options, 1): print(f"{i}. {option}") if self.get_yes_no("Use default VLC options?", True): config['vlc_options'] = vlc_options else: print("Enter custom VLC options (one per line, empty line to finish):") custom_options = [] while True: option = input("VLC option: ").strip() if not option: break custom_options.append(option) config['vlc_options'] = custom_options if custom_options else vlc_options return config def configure_logging_settings(self) -> Dict: """Configure logging settings""" self.print_section("Logging Configuration") config = {} # Log level print("Log levels:") print("1. DEBUG - Detailed information") print("2. INFO - General information") print("3. WARNING - Warning messages") print("4. ERROR - Error messages only") level_choice = self.get_number( "Choose log level (1-4)", min_val=1, max_val=4, default=2 ) levels = {1: "DEBUG", 2: "INFO", 3: "WARNING", 4: "ERROR"} config['log_level'] = levels[level_choice] # Log file config['log_file'] = self.get_user_input( "Log file path", default="/var/log/video_player.log" ) # Log rotation config['log_max_size'] = self.get_number( "Maximum log file size (MB)", min_val=1, max_val=100, default=10 ) * 1024 * 1024 # Convert to bytes config['log_backup_count'] = self.get_number( "Number of log backup files", min_val=1, max_val=10, default=5 ) return config def configure_system_settings(self) -> Dict: """Configure system settings""" self.print_section("System Configuration") config = {} # Check interval config['check_interval'] = self.get_number( "System check interval (seconds)", min_val=1, max_val=10, default=1 ) # Restart on crash config['restart_on_crash'] = self.get_yes_no("Restart on crash?", True) if config['restart_on_crash']: config['max_restart_attempts'] = self.get_number( "Maximum restart attempts", min_val=1, max_val=10, default=3 ) return config def scan_video_files(self) -> List[Dict]: """Scan for video files in the configured folder""" self.print_section("Video File Scanning") video_folder = Path(self.config.get('video_folder', self.video_folder)) video_extensions = {'.mp4', '.avi', '.mkv', '.mov', '.wmv', '.flv', '.webm', '.m4v'} if not video_folder.exists(): print(f"❌ Video folder does not exist: {video_folder}") return [] video_files = [] for file_path in video_folder.rglob('*'): if file_path.is_file() and file_path.suffix.lower() in video_extensions: video_files.append({ 'path': str(file_path), 'name': file_path.stem, 'size': file_path.stat().st_size }) video_files.sort(key=lambda x: x['name']) print(f"Found {len(video_files)} video files:") for i, video in enumerate(video_files, 1): size_mb = video['size'] / (1024 * 1024) print(f"{i:2d}. {video['name']} ({size_mb:.1f} MB)") return video_files def create_channel_mapping(self, video_files: List[Dict]) -> Dict: """Create channel mapping from video files""" self.print_section("Channel Mapping") channels = {} assignment_method = self.config.get('channel_assignment_method', 'alphabetical') if assignment_method == 'alphabetical': # Auto-assign channels alphabetically for i, video in enumerate(video_files, 1): channels[i] = { 'number': i, 'name': video['name'], 'path': video['path'], 'description': f"Auto-assigned channel {i}", 'category': 'general', 'enabled': True, 'priority': 0 } elif assignment_method == 'manual': # Manual channel assignment print("Manual channel assignment:") for video in video_files: print(f"\nVideo: {video['name']}") channel_num = self.get_number( f"Assign to channel number (0 to skip)", min_val=0, max_val=999 ) if channel_num > 0: channels[channel_num] = { 'number': channel_num, 'name': video['name'], 'path': video['path'], 'description': f"Manually assigned channel {channel_num}", 'category': 'general', 'enabled': True, 'priority': 0 } else: # custom # Load from existing file or create new channels_file = self.config_dir / "channels.json" if channels_file.exists(): with open(channels_file, 'r') as f: existing_channels = json.load(f) for channel_num, channel_info in existing_channels.items(): channels[int(channel_num)] = channel_info print("Loaded existing channel mapping") else: print("No existing channel mapping found, using alphabetical assignment") for i, video in enumerate(video_files, 1): channels[i] = { 'number': i, 'name': video['name'], 'path': video['path'], 'description': f"Auto-assigned channel {i}", 'category': 'general', 'enabled': True, 'priority': 0 } print(f"\nCreated {len(channels)} channels") return channels def save_configuration(self): """Save all configuration to files""" self.print_section("Saving Configuration") try: # Save main configuration main_config_file = self.config_dir / "config.json" with open(main_config_file, 'w') as f: json.dump(self.config, f, indent=2) print(f"✅ Main configuration saved to {main_config_file}") # Save channel configuration if hasattr(self, 'channels'): channels_file = self.config_dir / "channels.json" channels_data = {str(k): v for k, v in self.channels.items()} with open(channels_file, 'w') as f: json.dump(channels_data, f, indent=2) print(f"✅ Channel configuration saved to {channels_file}") # Set proper permissions os.chmod(main_config_file, 0o644) if hasattr(self, 'channels'): os.chmod(channels_file, 0o644) print("✅ Configuration saved successfully") except Exception as e: print(f"❌ Error saving configuration: {e}") return False return True def test_configuration(self) -> bool: """Test the configuration""" self.print_section("Configuration Test") try: # Test video folder access video_folder = Path(self.config.get('video_folder', self.video_folder)) if not video_folder.exists(): print(f"❌ Video folder does not exist: {video_folder}") return False print(f"✅ Video folder accessible: {video_folder}") # Test GPIO pin ir_pin = self.config.get('ir_pin', 18) if not (1 <= ir_pin <= 40): print(f"❌ Invalid GPIO pin: {ir_pin}") return False print(f"✅ GPIO pin valid: {ir_pin}") # Test VLC try: result = subprocess.run(['vlc', '--version'], capture_output=True, text=True, timeout=5) if result.returncode == 0: print("✅ VLC media player available") else: print("⚠️ VLC media player may not be properly installed") except: print("⚠️ Could not test VLC media player") # Test Python modules try: import vlc print("✅ python-vlc module available") except ImportError: print("❌ python-vlc module not available") return False try: import RPi.GPIO as GPIO print("✅ RPi.GPIO module available") except ImportError: print("❌ RPi.GPIO module not available") return False print("✅ Configuration test passed") return True except Exception as e: print(f"❌ Configuration test failed: {e}") return False def run_setup(self): """Run the complete setup process""" self.print_header("Video Player Setup") print("Welcome to the Video Player setup wizard!") print("This will help you configure the video player system.") if not self.get_yes_no("Continue with setup?", True): print("Setup cancelled") return False # Check system requirements if not self.check_system_requirements(): print("❌ System requirements not met") return False # Collect configuration self.config = {} # Video settings video_config = self.configure_video_settings() self.config.update(video_config) # Audio settings audio_config = self.configure_audio_settings() self.config.update(audio_config) # Channel settings channel_config = self.configure_channel_settings() self.config.update(channel_config) # IR settings ir_config = self.configure_ir_settings() self.config.update(ir_config) # VLC settings vlc_config = self.configure_vlc_settings() self.config.update(vlc_config) # Logging settings logging_config = self.configure_logging_settings() self.config.update(logging_config) # System settings system_config = self.configure_system_settings() self.config.update(system_config) # Scan video files video_files = self.scan_video_files() if video_files: # Create channel mapping self.channels = self.create_channel_mapping(video_files) else: print("⚠️ No video files found. You can add them later.") self.channels = {} # Test configuration if not self.test_configuration(): print("❌ Configuration test failed") if not self.get_yes_no("Continue anyway?", False): return False # Save configuration if not self.save_configuration(): print("❌ Failed to save configuration") return False # Final summary self.print_section("Setup Complete") print("✅ Video Player setup completed successfully!") print(f"📁 Video folder: {self.config.get('video_folder')}") print(f"📺 Default channel: {self.config.get('default_channel')}") print(f"🔧 IR pin: {self.config.get('ir_pin')}") print(f"📊 Channels configured: {len(self.channels)}") print(f"📝 Log level: {self.config.get('log_level')}") print("\nNext steps:") print("1. Add video files to your video folder") print("2. Configure IR remote codes if needed") print("3. Start the service: video-player-start") print("4. Check status: video-player-status") return True def main(): """Main entry point""" if len(sys.argv) > 1 and sys.argv[1] == '--help': print("Video Player Setup Script") print("Usage: python3 setup.py") print("This script will guide you through configuring the video player system.") return setup = VideoPlayerSetup() success = setup.run_setup() if success: print("\n🎉 Setup completed successfully!") sys.exit(0) else: print("\n❌ Setup failed!") sys.exit(1) if __name__ == "__main__": main()