This commit is contained in:
2025-09-25 18:17:12 +02:00
parent 8de07d073d
commit 0ee635bd2a
8 changed files with 361 additions and 146 deletions

120
BOOT_SETUP.md Normal file
View File

@@ -0,0 +1,120 @@
# Video Player Boot Setup
This document describes how the video player has been configured to start automatically on boot without IR remote control.
## Changes Made
### 1. Updated Service Files
Both service files have been updated with the following changes:
- **User**: Changed from `pi` to `tulivision`
- **Working Directory**: Updated to `/home/tulivision/rpi-tulivision`
- **ExecStart**: Updated to use virtual environment Python and added `--no-ir` flag
- **Environment Variables**: Updated paths for tulivision user
- **Removed**: GPIO group requirement (no longer needed with `--no-ir`)
- **Added**: Better process termination handling
### 2. Service Files
#### video-player.service
- **Description**: Raspberry Pi Video Player (No IR Remote Control)
- **Command**: `video_player.py --no-ir`
- **Behavior**: Starts with default channel (channel 1)
#### video-player-random.service
- **Description**: Raspberry Pi Video Player with Random Video Startup (No IR Remote Control)
- **Command**: `video_player.py --no-ir --random`
- **Behavior**: Starts with a random video
### 3. Management Script
A management script `manage_video_player.sh` has been created with the following commands:
```bash
./manage_video_player.sh start # Start the video player service
./manage_video_player.sh stop # Stop the video player service
./manage_video_player.sh restart # Restart the video player service
./manage_video_player.sh status # Show service status
./manage_video_player.sh enable # Enable video player for boot (default channel)
./manage_video_player.sh enable-random # Enable random video player for boot
./manage_video_player.sh disable # Disable video player services from boot
./manage_video_player.sh logs # Show service logs (follow mode)
```
## Installation Status
- ✅ Service files installed to `/etc/systemd/system/`
-`video-player.service` enabled for boot
-`video-player-random.service` available but disabled
- ✅ Management script available in project directory
## Current Configuration
- **Active Service**: `video-player.service` (enabled for boot)
- **Startup Mode**: Default channel (channel 1)
- **IR Remote**: Disabled (no GPIO access required)
- **User**: tulivision
- **Working Directory**: /home/tulivision/rpi-tulivision
- **Python Environment**: Virtual environment in venv/
## Testing Results
- ✅ Service starts successfully
- ✅ VLC initializes without errors
- ✅ Video playback works
- ✅ Service stops cleanly
- ✅ No GPIO access required
- ✅ Automatic restart on failure
## Boot Behavior
When the system boots:
1. The `video-player.service` will start automatically
2. It will initialize VLC without IR remote control
3. It will scan the video directory and create channels automatically
4. It will start playing the default channel (channel 1)
5. If the service fails, it will restart automatically after 10 seconds
## Switching Between Services
To switch to random video startup:
```bash
./manage_video_player.sh enable-random
```
To switch back to default channel startup:
```bash
./manage_video_player.sh enable
```
## Troubleshooting
### Check Service Status
```bash
./manage_video_player.sh status
```
### View Service Logs
```bash
./manage_video_player.sh logs
```
### Restart Service
```bash
./manage_video_player.sh restart
```
### Disable Auto-Start
```bash
./manage_video_player.sh disable
```
## Notes
- The service runs without IR remote control, so no GPIO access is required
- Videos are automatically discovered from the configured directory
- Channels are assigned automatically based on alphabetical order
- The service will restart automatically if it crashes
- All logging is sent to systemd journal

View File

@@ -41,6 +41,7 @@ class VideoPlayerConfig:
channel_timeout: float = 3.0 channel_timeout: float = 3.0
multi_digit_timeout: float = 1.0 multi_digit_timeout: float = 1.0
channel_display_timeout: float = 2.0 channel_display_timeout: float = 2.0
channel_refresh_interval: float = 30.0 # seconds
channel_assignment_method: str = "alphabetical" # alphabetical, manual, custom channel_assignment_method: str = "alphabetical" # alphabetical, manual, custom
# IR Remote settings # IR Remote settings
@@ -435,6 +436,7 @@ def create_config_templates():
"channel_timeout": 3.0, "channel_timeout": 3.0,
"multi_digit_timeout": 1.0, "multi_digit_timeout": 1.0,
"channel_display_timeout": 2.0, "channel_display_timeout": 2.0,
"channel_refresh_interval": 30.0,
"channel_assignment_method": "alphabetical", "channel_assignment_method": "alphabetical",
"ir_pin": 18, "ir_pin": 18,
"ir_protocols": ["NEC", "RC5"], "ir_protocols": ["NEC", "RC5"],

63
manage_video_player.sh Executable file
View File

@@ -0,0 +1,63 @@
#!/bin/bash
# Video Player Service Management Script
SERVICE_NAME="video-player"
RANDOM_SERVICE_NAME="video-player-random"
case "$1" in
start)
echo "Starting video player service..."
sudo systemctl start $SERVICE_NAME
sudo systemctl status $SERVICE_NAME
;;
stop)
echo "Stopping video player service..."
sudo systemctl stop $SERVICE_NAME
sudo systemctl stop $RANDOM_SERVICE_NAME
;;
restart)
echo "Restarting video player service..."
sudo systemctl restart $SERVICE_NAME
sudo systemctl status $SERVICE_NAME
;;
status)
echo "Video player service status:"
sudo systemctl status $SERVICE_NAME
;;
enable)
echo "Enabling video player service for boot..."
sudo systemctl enable $SERVICE_NAME
sudo systemctl disable $RANDOM_SERVICE_NAME
echo "Video player service enabled for boot"
;;
enable-random)
echo "Enabling random video player service for boot..."
sudo systemctl enable $RANDOM_SERVICE_NAME
sudo systemctl disable $SERVICE_NAME
echo "Random video player service enabled for boot"
;;
disable)
echo "Disabling video player services..."
sudo systemctl disable $SERVICE_NAME
sudo systemctl disable $RANDOM_SERVICE_NAME
echo "Video player services disabled"
;;
logs)
echo "Video player service logs:"
sudo journalctl -u $SERVICE_NAME -f
;;
*)
echo "Usage: $0 {start|stop|restart|status|enable|enable-random|disable|logs}"
echo ""
echo "Commands:"
echo " start - Start the video player service"
echo " stop - Stop the video player service"
echo " restart - Restart the video player service"
echo " status - Show service status"
echo " enable - Enable video player for boot (default channel)"
echo " enable-random - Enable random video player for boot"
echo " disable - Disable video player services from boot"
echo " logs - Show service logs (follow mode)"
exit 1
;;
esac

View File

@@ -1,29 +1,32 @@
{ {
"1": { "_note": "This file is no longer used. Channels are now automatically assigned based on video files found in the video directory. Video files are assigned channel numbers 1, 2, 3... in alphabetical order.",
"number": 1, "_example": {
"name": "Sample Video 1", "1": {
"path": "/home/pi/Videos/sample1.mp4", "number": 1,
"description": "First sample video", "name": "Sample Video 1",
"category": "general", "path": "/home/pi/Videos/sample1.mp4",
"enabled": true, "description": "First sample video",
"priority": 0 "category": "general",
}, "enabled": true,
"2": { "priority": 0
"number": 2, },
"name": "Sample Video 2", "2": {
"path": "/home/pi/Videos/sample2.mp4", "number": 2,
"description": "Second sample video", "name": "Sample Video 2",
"category": "general", "path": "/home/pi/Videos/sample2.mp4",
"enabled": true, "description": "Second sample video",
"priority": 0 "category": "general",
}, "enabled": true,
"3": { "priority": 0
"number": 3, },
"name": "Sample Video 3", "3": {
"path": "/home/pi/Videos/sample3.mp4", "number": 3,
"description": "Third sample video", "name": "Sample Video 3",
"category": "general", "path": "/home/pi/Videos/sample3.mp4",
"enabled": true, "description": "Third sample video",
"priority": 0 "category": "general",
"enabled": true,
"priority": 0
}
} }
} }

View File

@@ -13,6 +13,7 @@
"channel_timeout": 3.0, "channel_timeout": 3.0,
"multi_digit_timeout": 1.0, "multi_digit_timeout": 1.0,
"channel_display_timeout": 2.0, "channel_display_timeout": 2.0,
"channel_refresh_interval": 30.0,
"channel_assignment_method": "alphabetical", "channel_assignment_method": "alphabetical",
"ir_pin": 18, "ir_pin": 18,
"ir_protocols": ["NEC", "RC5"], "ir_protocols": ["NEC", "RC5"],

View File

@@ -1,27 +1,31 @@
[Unit] [Unit]
Description=Raspberry Pi Video Player with Random Video Startup Description=Raspberry Pi Video Player with Random Video Startup (No IR Remote Control)
Documentation=https://github.com/your-repo/ulivision-tv Documentation=https://github.com/your-repo/ulivision-tv
After=network.target sound.target graphical-session.target After=network.target sound.target graphical-session.target
Wants=graphical-session.target Wants=graphical-session.target
[Service] [Service]
Type=simple Type=simple
User=pi User=tulivision
Group=pi Group=tulivision
WorkingDirectory=/home/pi/ulivision-tv WorkingDirectory=/home/tulivision/rpi-tulivision
ExecStart=/usr/bin/python3 /home/pi/ulivision-tv/video_player.py --random ExecStart=/home/tulivision/rpi-tulivision/venv/bin/python3 /home/tulivision/rpi-tulivision/video_player.py --no-ir --random
ExecReload=/bin/kill -HUP $MAINPID ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/bin/kill -TERM $MAINPID
Restart=always Restart=always
RestartSec=10 RestartSec=10
TimeoutStopSec=30
KillMode=mixed
KillSignal=SIGTERM
StandardOutput=journal StandardOutput=journal
StandardError=journal StandardError=journal
SyslogIdentifier=video-player-random SyslogIdentifier=video-player-random
# Environment variables # Environment variables
Environment=DISPLAY=:0 Environment=DISPLAY=:0
Environment=XAUTHORITY=/home/pi/.Xauthority Environment=XAUTHORITY=/home/tulivision/.Xauthority
Environment=PYTHONPATH=/home/pi/ulivision-tv Environment=PYTHONPATH=/home/tulivision/rpi-tulivision
Environment=HOME=/home/pi Environment=HOME=/home/tulivision
# Security settings # Security settings
NoNewPrivileges=false NoNewPrivileges=false
@@ -33,8 +37,5 @@ ProtectHome=false
LimitNOFILE=65536 LimitNOFILE=65536
MemoryMax=512M MemoryMax=512M
# GPIO access
SupplementaryGroups=gpio
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View File

@@ -1,27 +1,31 @@
[Unit] [Unit]
Description=Raspberry Pi Video Player with IR Remote Control Description=Raspberry Pi Video Player (No IR Remote Control)
Documentation=https://github.com/your-repo/ulivision-tv Documentation=https://github.com/your-repo/ulivision-tv
After=network.target sound.target graphical-session.target After=network.target sound.target graphical-session.target
Wants=graphical-session.target Wants=graphical-session.target
[Service] [Service]
Type=simple Type=simple
User=pi User=tulivision
Group=pi Group=tulivision
WorkingDirectory=/home/pi/ulivision-tv WorkingDirectory=/home/tulivision/rpi-tulivision
ExecStart=/usr/bin/python3 /home/pi/ulivision-tv/video_player.py ExecStart=/home/tulivision/rpi-tulivision/venv/bin/python3 /home/tulivision/rpi-tulivision/video_player.py --no-ir
ExecReload=/bin/kill -HUP $MAINPID ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/bin/kill -TERM $MAINPID
Restart=always Restart=always
RestartSec=10 RestartSec=10
TimeoutStopSec=30
KillMode=mixed
KillSignal=SIGTERM
StandardOutput=journal StandardOutput=journal
StandardError=journal StandardError=journal
SyslogIdentifier=video-player SyslogIdentifier=video-player
# Environment variables # Environment variables
Environment=DISPLAY=:0 Environment=DISPLAY=:0
Environment=XAUTHORITY=/home/pi/.Xauthority Environment=XAUTHORITY=/home/tulivision/.Xauthority
Environment=PYTHONPATH=/home/pi/ulivision-tv Environment=PYTHONPATH=/home/tulivision/rpi-tulivision
Environment=HOME=/home/pi Environment=HOME=/home/tulivision
# Security settings # Security settings
NoNewPrivileges=false NoNewPrivileges=false
@@ -33,8 +37,5 @@ ProtectHome=false
LimitNOFILE=65536 LimitNOFILE=65536
MemoryMax=512M MemoryMax=512M
# GPIO access
SupplementaryGroups=gpio
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View File

@@ -27,7 +27,7 @@ load_dotenv()
class VideoPlayer: class VideoPlayer:
"""Main video player class with VLC integration and IR remote control""" """Main video player class with VLC integration and IR remote control"""
def __init__(self, config_path: str = "config.json"): def __init__(self, config_path: str = "config.json", enable_ir: bool = True):
"""Initialize the video player with configuration""" """Initialize the video player with configuration"""
self.config_path = config_path self.config_path = config_path
self.config = self.load_config() self.config = self.load_config()
@@ -40,6 +40,7 @@ class VideoPlayer:
self.current_channel = None self.current_channel = None
self.ir_queue = queue.Queue() self.ir_queue = queue.Queue()
self.running = False self.running = False
self.enable_ir = enable_ir
# IR Remote settings # IR Remote settings
self.ir_pin = self.config.get('ir_pin', 18) self.ir_pin = self.config.get('ir_pin', 18)
@@ -50,11 +51,15 @@ class VideoPlayer:
self.default_channel = self.config.get('default_channel', 1) self.default_channel = self.config.get('default_channel', 1)
self.channel_timeout = self.config.get('channel_timeout', 3.0) self.channel_timeout = self.config.get('channel_timeout', 3.0)
self.multi_digit_timeout = self.config.get('multi_digit_timeout', 1.0) self.multi_digit_timeout = self.config.get('multi_digit_timeout', 1.0)
self.channel_refresh_interval = self.config.get('channel_refresh_interval', 30.0) # seconds
# Multi-digit channel input # Multi-digit channel input
self.channel_input = "" self.channel_input = ""
self.last_digit_time = 0 self.last_digit_time = 0
# Channel refresh tracking
self.last_channel_refresh = time.time()
self.logger.info("Video Player initialized") self.logger.info("Video Player initialized")
def load_config(self) -> Dict: def load_config(self) -> Dict:
@@ -78,34 +83,11 @@ class VideoPlayer:
"default_channel": 1, "default_channel": 1,
"channel_timeout": 3.0, "channel_timeout": 3.0,
"multi_digit_timeout": 1.0, "multi_digit_timeout": 1.0,
"channel_refresh_interval": 30.0,
"vlc_options": [ "vlc_options": [
"--fullscreen", "--fullscreen",
"--no-video-title-show", "--no-video-title-show",
"--no-audio-display", "--quiet"
"--always-on-top",
"--video-on-top",
"--no-embedded-video",
"--no-video-deco",
"--no-qt-fs-controller",
"--no-qt-system-tray",
"--no-qt-notification",
"--no-qt-privacy-ask",
"--no-qt-updates-notif",
"--no-qt-error-dialogs",
"--no-qt-fs-controller",
"--no-qt-video-autosize",
"--no-qt-name-in-title",
"--no-qt-minimal-view",
"--no-qt-bgcone",
"--no-qt-pause-minimized",
"--no-qt-continue",
"--no-qt-recentplay",
"--no-qt-start-minimized",
"--no-qt-system-tray",
"--no-qt-notification",
"--no-qt-privacy-ask",
"--no-qt-updates-notif",
"--no-qt-error-dialogs"
], ],
"ir_codes": { "ir_codes": {
"0": "channel_0", "0": "channel_0",
@@ -154,7 +136,11 @@ class VideoPlayer:
"""Initialize VLC media player""" """Initialize VLC media player"""
try: try:
vlc_options = self.config.get('vlc_options', []) vlc_options = self.config.get('vlc_options', [])
self.vlc_instance = vlc.Instance(vlc_options)
# Use the configured VLC options
all_vlc_options = vlc_options
self.vlc_instance = vlc.Instance(all_vlc_options)
self.vlc_player = self.vlc_instance.media_player_new() self.vlc_player = self.vlc_instance.media_player_new()
# Set fullscreen and always on top # Set fullscreen and always on top
@@ -201,7 +187,7 @@ class VideoPlayer:
return video_files return video_files
def create_channels(self): def create_channels(self):
"""Create channel mapping from video files""" """Create channel mapping from video files automatically"""
video_files = self.scan_video_folder() video_files = self.scan_video_folder()
self.channels = {} self.channels = {}
@@ -213,52 +199,40 @@ class VideoPlayer:
'file': video_file 'file': video_file
} }
self.logger.info(f"Created {len(self.channels)} channels") self.logger.info(f"Created {len(self.channels)} channels automatically from directory")
# Save channel mapping to file
self.save_channel_mapping()
def save_channel_mapping(self): def load_channels_from_directory(self):
"""Save channel mapping to JSON file""" """Load channels automatically from video directory"""
try: try:
channel_data = {} self.create_channels()
for channel_num, channel_info in self.channels.items(): if self.channels:
channel_data[str(channel_num)] = { self.logger.info(f"Loaded {len(self.channels)} channels from directory")
'name': channel_info['name'],
'path': channel_info['path']
}
with open('channels.json', 'w') as f:
json.dump(channel_data, f, indent=2)
self.logger.info("Channel mapping saved to channels.json")
except Exception as e:
self.logger.error(f"Error saving channel mapping: {e}")
def load_channel_mapping(self):
"""Load channel mapping from JSON file"""
try:
if os.path.exists('channels.json'):
with open('channels.json', 'r') as f:
channel_data = json.load(f)
self.channels = {}
for channel_num, channel_info in channel_data.items():
video_path = Path(channel_info['path'])
if video_path.exists():
self.channels[int(channel_num)] = {
'number': int(channel_num),
'name': channel_info['name'],
'path': channel_info['path'],
'file': video_path
}
self.logger.info(f"Loaded {len(self.channels)} channels from mapping file")
return True return True
else:
self.logger.warning("No video files found in directory")
return False
except Exception as e: except Exception as e:
self.logger.error(f"Error loading channel mapping: {e}") self.logger.error(f"Error loading channels from directory: {e}")
return False
def refresh_channels(self):
"""Refresh channel list from directory (useful when videos are added/removed)"""
old_channel_count = len(self.channels)
self.load_channels_from_directory()
new_channel_count = len(self.channels)
return False if new_channel_count != old_channel_count:
self.logger.info(f"Channel count changed: {old_channel_count} -> {new_channel_count}")
# If current channel no longer exists, switch to first available
if self.current_channel and self.current_channel not in self.channels:
if self.channels:
first_channel = min(self.channels.keys())
self.logger.info(f"Current channel {self.current_channel} no longer exists, switching to channel {first_channel}")
self.play_channel(first_channel)
else:
self.logger.warning("No channels available after refresh")
self.current_channel = None
def play_channel(self, channel_number: int) -> bool: def play_channel(self, channel_number: int) -> bool:
"""Play video for specified channel number""" """Play video for specified channel number"""
@@ -327,6 +301,10 @@ class VideoPlayer:
def setup_gpio(self): def setup_gpio(self):
"""Setup GPIO for IR receiver""" """Setup GPIO for IR receiver"""
if not self.enable_ir:
self.logger.info("IR remote control disabled, skipping GPIO setup")
return True
try: try:
GPIO.setmode(GPIO.BCM) GPIO.setmode(GPIO.BCM)
GPIO.setup(self.ir_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(self.ir_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
@@ -515,17 +493,21 @@ class VideoPlayer:
self.logger.error("Failed to setup GPIO") self.logger.error("Failed to setup GPIO")
return False return False
# Load or create channels # Load channels automatically from directory
if not self.load_channel_mapping(): if not self.load_channels_from_directory():
self.create_channels() self.logger.error("Failed to load channels from directory")
return False
if not self.channels: if not self.channels:
self.logger.error("No video files found") self.logger.error("No video files found")
return False return False
# Start IR processing thread # Start IR processing thread only if IR is enabled
ir_thread = threading.Thread(target=self.process_ir_commands, daemon=True) if self.enable_ir:
ir_thread.start() ir_thread = threading.Thread(target=self.process_ir_commands, daemon=True)
ir_thread.start()
else:
self.logger.info("IR remote control disabled, skipping IR processing thread")
# Play based on startup mode # Play based on startup mode
success = False success = False
@@ -566,6 +548,12 @@ class VideoPlayer:
self.logger.info("Video stopped, ensuring video is playing") self.logger.info("Video stopped, ensuring video is playing")
self.ensure_video_playing() self.ensure_video_playing()
# Periodically refresh channels to detect new/removed videos
current_time = time.time()
if current_time - self.last_channel_refresh > self.channel_refresh_interval:
self.refresh_channels()
self.last_channel_refresh = current_time
# Keep VLC on top # Keep VLC on top
self.keep_vlc_on_top() self.keep_vlc_on_top()
@@ -610,7 +598,8 @@ class VideoPlayer:
if self.vlc_player: if self.vlc_player:
self.vlc_player.stop() self.vlc_player.stop()
GPIO.cleanup() if self.enable_ir:
GPIO.cleanup()
self.logger.info("Cleanup complete") self.logger.info("Cleanup complete")
def parse_arguments(): def parse_arguments():
@@ -620,12 +609,18 @@ def parse_arguments():
formatter_class=argparse.RawDescriptionHelpFormatter, formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=""" epilog="""
Examples: Examples:
python3 video_player.py # Start with default channel python3 video_player.py # Start with default channel (auto-assigned)
python3 video_player.py --random # Start with random video python3 video_player.py --random # Start with random video
python3 video_player.py --channel 5 # Start with channel 5 python3 video_player.py --channel 5 # Start with channel 5 (auto-assigned)
python3 video_player.py --index 2 # Start with video at index 2 (0-based) python3 video_player.py --index 2 # Start with video at index 2 (0-based)
python3 video_player.py --list-channels # List available channels python3 video_player.py --no-ir # Start without IR remote control
python3 video_player.py --list-channels # List available channels (auto-assigned)
python3 video_player.py --list-videos # List available videos with indices python3 video_player.py --list-videos # List available videos with indices
python3 video_player.py --refresh-channels # Refresh channel list from directory
Note: Channels are automatically assigned numbers (1, 2, 3...) based on alphabetical order
of video files found in the video directory. No channel mapping file is needed.
Use --no-ir to start without IR remote control if GPIO access is not available.
""" """
) )
@@ -660,6 +655,11 @@ Examples:
action='store_true', action='store_true',
help='List available videos with indices and exit' help='List available videos with indices and exit'
) )
parser.add_argument(
'--refresh-channels',
action='store_true',
help='Refresh channel list from directory and exit'
)
# Configuration arguments # Configuration arguments
parser.add_argument( parser.add_argument(
@@ -668,6 +668,11 @@ Examples:
default='config.json', default='config.json',
help='Path to configuration file (default: config.json)' help='Path to configuration file (default: config.json)'
) )
parser.add_argument(
'--no-ir',
action='store_true',
help='Start without IR remote control (skip GPIO setup)'
)
return parser.parse_args() return parser.parse_args()
@@ -704,37 +709,48 @@ def main():
"""Main entry point""" """Main entry point"""
args = parse_arguments() args = parse_arguments()
# Check if user is in gpio group (needed for GPIO access) # Check if user is in gpio group (needed for GPIO access) only if IR is enabled
import grp if not args.no_ir:
try: import grp
gpio_group = grp.getgrnam('gpio') try:
current_groups = os.getgroups() gpio_group = grp.getgrnam('gpio')
if gpio_group.gr_gid not in current_groups: current_groups = os.getgroups()
print("This script requires GPIO access. Please add your user to the gpio group:") if gpio_group.gr_gid not in current_groups:
print("sudo usermod -a -G gpio $USER") print("This script requires GPIO access. Please add your user to the gpio group:")
print("Then log out and log back in, or run: newgrp gpio") print("sudo usermod -a -G gpio $USER")
print("Then log out and log back in, or run: newgrp gpio")
print("Alternatively, use --no-ir to start without IR remote control")
sys.exit(1)
except KeyError:
print("GPIO group not found. Please ensure your system has GPIO support.")
print("Alternatively, use --no-ir to start without IR remote control")
sys.exit(1) sys.exit(1)
except KeyError:
print("GPIO group not found. Please ensure your system has GPIO support.")
sys.exit(1)
# Check if another instance is running # Check if another instance is running
current_pid = os.getpid()
for proc in psutil.process_iter(['pid', 'name', 'cmdline']): for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
try: try:
if 'video_player.py' in ' '.join(proc.info['cmdline'] or []) and proc.info['pid'] != os.getpid(): if proc.info['cmdline']:
print("Another instance of video_player.py is already running") cmdline = proc.info['cmdline']
sys.exit(1) # Check if this is actually running video_player.py (not just containing it in args)
if (len(cmdline) > 1 and
cmdline[1].endswith('video_player.py') and
proc.info['pid'] != current_pid):
print("Another instance of video_player.py is already running")
sys.exit(1)
except (psutil.NoSuchProcess, psutil.AccessDenied): except (psutil.NoSuchProcess, psutil.AccessDenied):
continue continue
# Create video player # Create video player
player = VideoPlayer(args.config) enable_ir = not args.no_ir
player = VideoPlayer(args.config, enable_ir=enable_ir)
# Handle list commands # Handle list commands
if args.list_channels: if args.list_channels:
# Load channels first # Load channels from directory
if not player.load_channel_mapping(): if not player.load_channels_from_directory():
player.create_channels() print("No video files found in directory")
return
list_channels(player) list_channels(player)
return return
@@ -742,6 +758,14 @@ def main():
list_videos(player) list_videos(player)
return return
if args.refresh_channels:
if player.load_channels_from_directory():
print(f"Refreshed channels: {len(player.channels)} channels found")
list_channels(player)
else:
print("No video files found in directory")
return
# Determine startup mode and parameters # Determine startup mode and parameters
startup_mode = "default" startup_mode = "default"
channel_number = None channel_number = None