675 lines
23 KiB
Python
Executable File
675 lines
23 KiB
Python
Executable File
#!/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()
|