inital commit
This commit is contained in:
674
setup.py
Executable file
674
setup.py
Executable file
@@ -0,0 +1,674 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user