Cleanup repo + harden offline install and watchdog
Repo hygiene: - Remove dead scripts (.wait-for-display, .display-keepalive, .keep-screen-alive) - Remove stale backup/scratch (install.sh.bak, New.txt, stale .pyc) - Stop tracking machine-specific/generated state (config/app_config.json, playlists/server_playlist.json, src/player_auth.json, generated launchers) - Prune duplicate wheel versions (one per package) - Reorganize working_files -> dev/, dev/tests/, documentation/dev-notes/, scripts/ - Extend .gitignore; fix no-op rules in repo/.gitignore Offline install (install.sh): - Non-fatal system-dep step; per-package apt retry; .deb bundle install - Skip pip self-upgrade offline; guard online fallbacks behind connectivity probe - Dependency-free fetch_url (curl/wget/urllib); --no-reboot flag - Fix corrupted literal backslash-bang escapes (shebangs, test -f, banner) - Idempotent cron install (no more stacked @reboot lines) Autostart / watchdog: - start.sh: single-instance flock lock (.player_watchdog.lock) acquired only after a display is confirmed, preventing parallel watchdogs - Set XDG_RUNTIME_DIR when missing; exit early if headless - run_player.sh: activate .venv; stop_player.sh: clear lock file
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
# USB Card Reader Authentication
|
||||
|
||||
This document describes the USB card reader authentication feature for the Kiwy Signage Player.
|
||||
|
||||
## Overview
|
||||
|
||||
The player now supports user authentication via USB card readers when accessing the edit/drawing interface. When a user clicks the edit button (pencil icon), they must swipe their card to authenticate before being allowed to edit the image.
|
||||
|
||||
## How It Works
|
||||
|
||||
1. **Edit Button Click**: User clicks the pencil icon to edit the current image
|
||||
2. **Validation Checks**:
|
||||
- Verify current media is an image (not video)
|
||||
- Check if editing is allowed for this media (`edit_on_player` permission from server)
|
||||
3. **Card Reader Prompt**:
|
||||
- Display "Please swipe your card..." message
|
||||
- Wait for card swipe (5 second timeout)
|
||||
- Read card data from USB card reader
|
||||
- Store the card data (no validation required)
|
||||
4. **Open Edit Interface**: Edit interface opens with card data stored
|
||||
5. **Save & Upload**: When user saves the edited image:
|
||||
- Card data is included in the metadata JSON
|
||||
- Both image and metadata (with card data) are uploaded to server
|
||||
- Server receives `user_card_data` field for tracking who edited the image
|
||||
|
||||
## Card Reader Setup
|
||||
|
||||
### Hardware Requirements
|
||||
- USB card reader (HID/keyboard emulation type)
|
||||
- Compatible cards (magnetic stripe or RFID depending on reader)
|
||||
|
||||
### Software Requirements
|
||||
The player requires the `evdev` Python library to interface with USB input devices:
|
||||
|
||||
```bash
|
||||
# Install via apt (recommended for Raspberry Pi)
|
||||
sudo apt-get install python3-evdev
|
||||
|
||||
# Or via pip
|
||||
pip3 install evdev
|
||||
```
|
||||
|
||||
### Fallback Mode
|
||||
If `evdev` is not available, the player will:
|
||||
- Log a warning message
|
||||
- Use a default card value (`DEFAULT_USER_12345`) for testing
|
||||
- This allows development and testing without hardware
|
||||
|
||||
## Card Data Storage
|
||||
|
||||
The card data is captured as a raw string and stored without validation or mapping:
|
||||
|
||||
- **No preprocessing**: Card data is stored exactly as received from the reader
|
||||
- **Format**: Whatever the card reader sends (typically numeric or alphanumeric)
|
||||
- **Sent to server**: Raw card data is included in the `user_card_data` field of the metadata JSON
|
||||
- **Server-side processing**: The server can validate, map, or process the card data as needed
|
||||
|
||||
### Metadata JSON Format
|
||||
When an image is saved, the metadata includes:
|
||||
```json
|
||||
{
|
||||
"time_of_modification": "2025-12-08T10:30:00",
|
||||
"original_name": "image.jpg",
|
||||
"new_name": "image_e_v1.jpg",
|
||||
"original_path": "/path/to/image.jpg",
|
||||
"version": 1,
|
||||
"user_card_data": "123456789"
|
||||
}
|
||||
```
|
||||
|
||||
If no card is swiped (timeout), `user_card_data` will be `null`.
|
||||
|
||||
## Testing the Card Reader
|
||||
|
||||
A test utility is provided to verify card reader functionality:
|
||||
|
||||
```bash
|
||||
cd /home/pi/Desktop/Kiwy-Signage/dev/tests
|
||||
python3 test_card_reader.py
|
||||
```
|
||||
|
||||
The test tool will:
|
||||
1. List all available input devices
|
||||
2. Auto-detect the card reader (or let you select manually)
|
||||
3. Listen for card swipes and display the data received
|
||||
4. Show how the data will be processed
|
||||
|
||||
### Test Output Example
|
||||
```
|
||||
✓ Card data received: '123456789'
|
||||
Length: 9 characters
|
||||
Processed ID: card_123456789
|
||||
```
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Main Components
|
||||
|
||||
1. **CardReader Class** (`main.py`)
|
||||
- Handles USB device detection
|
||||
- Reads input events from card reader
|
||||
- Provides async callback interface
|
||||
- Includes timeout handling (5 seconds)
|
||||
|
||||
2. **Card Read Flow** (`show_edit_interface()` method)
|
||||
- Validates media type and permissions
|
||||
- Initiates card read
|
||||
- Stores raw card data
|
||||
- Opens edit popup
|
||||
|
||||
3. **Metadata Creation** (`_save_metadata()` method)
|
||||
- Includes card data in metadata JSON
|
||||
- No processing or validation of card data
|
||||
- Sent to server as-is
|
||||
|
||||
### Card Data Format
|
||||
|
||||
Card readers typically send data as keyboard input:
|
||||
- Each character is sent as a key press event
|
||||
- Data ends with an ENTER key press
|
||||
- Reader format: `[CARD_DATA][ENTER]`
|
||||
|
||||
The CardReader class:
|
||||
- Captures key press events
|
||||
- Builds the card data string character by character
|
||||
- Completes reading when ENTER is detected
|
||||
- Returns the complete card data to the callback
|
||||
|
||||
### Security Considerations
|
||||
|
||||
1. **Server-Side Validation**: Card validation should be implemented on the server
|
||||
2. **Timeout**: 5-second timeout prevents infinite waiting for card swipe
|
||||
3. **Logging**: All card reads are logged with the raw card data
|
||||
4. **Permissions**: Edit permission must be enabled on the server (`edit_on_player`)
|
||||
5. **Raw Data**: Card data is sent as-is; server is responsible for validation and authorization
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Card Reader Not Detected
|
||||
- Check USB connection
|
||||
- Run `ls /dev/input/` to see available devices
|
||||
- Run the test script to verify detection
|
||||
- Check `evdev` is installed: `python3 -c "import evdev"`
|
||||
|
||||
### Card Swipes Not Recognized
|
||||
- Verify card reader sends keyboard events
|
||||
- Test with the `test_card_reader.py` utility
|
||||
- Check card format is compatible with reader
|
||||
- Ensure card is swiped smoothly at proper speed
|
||||
|
||||
### Card Data Not Captured
|
||||
- Check card data format in logs
|
||||
- Enable debug logging to see raw card data
|
||||
- Test in fallback mode (without evdev) to isolate hardware issues
|
||||
- Verify card swipe completes within 5-second timeout
|
||||
|
||||
### Permission Denied Errors
|
||||
- User may need to be in the `input` group:
|
||||
```bash
|
||||
sudo usermod -a -G input $USER
|
||||
```
|
||||
- Reboot after adding user to group
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
Potential improvements for the card reader system:
|
||||
|
||||
1. **Server Validation**: Server validates cards against database and returns authorization
|
||||
2. **Card Enrollment**: Server-side UI for registering new cards
|
||||
3. **Multiple Card Types**: Support for different card formats (barcode, RFID, magnetic)
|
||||
4. **Client-side Validation**: Add optional local card validation before opening edit
|
||||
5. **Audit Trail**: Server tracks all card usage with timestamps
|
||||
6. **RFID Support**: Test and optimize for RFID readers
|
||||
7. **Barcode Scanners**: Support USB barcode scanners as alternative
|
||||
8. **Retry Logic**: Allow re-swipe if card read fails
|
||||
|
||||
## Related Files
|
||||
|
||||
- `/src/main.py` - Main implementation (CardReader class, authentication flow)
|
||||
- `/src/edit_drowing.py` - Drawing/editing interface (uses authenticated user)
|
||||
- `/dev/tests/test_card_reader.py` - Card reader test utility
|
||||
- `/requirements.txt` - Dependencies (includes evdev)
|
||||
@@ -0,0 +1,170 @@
|
||||
# Card Reader Fix - Multi-USB Device Support
|
||||
|
||||
## Problem Description
|
||||
|
||||
When a USB touchscreen was connected to the Raspberry Pi, the card reader authentication was not working. The system reported "no authentication was received" even though the card reader was physically connected on a different USB port.
|
||||
|
||||
### Root Cause
|
||||
|
||||
The original `find_card_reader()` function used overly broad matching criteria:
|
||||
1. It would select the **first** device with "keyboard" in its name
|
||||
2. USB touchscreens often register as HID keyboard devices (for touch input)
|
||||
3. The touchscreen would be detected first, blocking the actual card reader
|
||||
4. No exclusion logic existed to filter out touch devices
|
||||
|
||||
## Solution
|
||||
|
||||
The fix implements a **priority-based device selection** with **exclusion filters**:
|
||||
|
||||
### 1. Device Exclusion List
|
||||
Devices containing these keywords are now skipped:
|
||||
- `touch`, `touchscreen`
|
||||
- `mouse`, `mice`
|
||||
- `trackpad`, `touchpad`
|
||||
- `pen`, `stylus`
|
||||
- `video`, `button`, `lid`
|
||||
|
||||
### 2. Three-Priority Device Search
|
||||
|
||||
**Priority 1: Explicit Card Readers**
|
||||
- Devices with "card", "reader", "rfid", or "hid" in their name
|
||||
- Must have keyboard capabilities (EV_KEY)
|
||||
- Excludes any device matching exclusion keywords
|
||||
|
||||
**Priority 2: USB Keyboards**
|
||||
- Devices with both "usb" AND "keyboard" in their name
|
||||
- Card readers typically appear as "USB Keyboard" or similar
|
||||
- Excludes touch devices and other non-card peripherals
|
||||
|
||||
**Priority 3: Fallback to Any Keyboard**
|
||||
- Any keyboard device not in the exclusion list
|
||||
- Used only if no card reader or USB keyboard is found
|
||||
|
||||
### 3. Enhanced Logging
|
||||
|
||||
The system now logs:
|
||||
- All detected input devices at startup
|
||||
- Which devices are being skipped and why
|
||||
- Which device is ultimately selected as the card reader
|
||||
|
||||
## Testing
|
||||
|
||||
### Using the Test Script
|
||||
|
||||
Run the enhanced test script to identify your card reader:
|
||||
|
||||
```bash
|
||||
cd /home/pi/Desktop/Kiwy-Signage/dev/tests
|
||||
python3 test_card_reader.py
|
||||
```
|
||||
|
||||
The script will:
|
||||
1. List all input devices with helpful indicators:
|
||||
- `** LIKELY CARD READER **` - devices with "card" or "reader" in name
|
||||
- `(Excluded: ...)` - devices that will be skipped
|
||||
- `(USB Keyboard - could be card reader)` - potential card readers
|
||||
|
||||
2. Auto-detect the card reader using the same logic as the main app
|
||||
|
||||
3. Allow manual selection by device number if auto-detection is wrong
|
||||
|
||||
### Example Output
|
||||
|
||||
```
|
||||
=== Available Input Devices ===
|
||||
|
||||
[0] /dev/input/event0
|
||||
Name: USB Touchscreen Controller
|
||||
Phys: usb-0000:01:00.0-1.1/input0
|
||||
Type: Keyboard/HID Input Device
|
||||
(Excluded: appears to be touch/mouse/other non-card device)
|
||||
|
||||
[1] /dev/input/event1
|
||||
Name: HID 08ff:0009
|
||||
Phys: usb-0000:01:00.0-1.2/input0
|
||||
Type: Keyboard/HID Input Device
|
||||
** LIKELY CARD READER **
|
||||
|
||||
[2] /dev/input/event2
|
||||
Name: Logitech USB Keyboard
|
||||
Phys: usb-0000:01:00.0-1.3/input0
|
||||
Type: Keyboard/HID Input Device
|
||||
(USB Keyboard - could be card reader)
|
||||
```
|
||||
|
||||
### Verifying the Fix
|
||||
|
||||
1. **Check Logs**: When the main app starts, check the logs for device detection:
|
||||
```bash
|
||||
tail -f /path/to/logfile
|
||||
```
|
||||
|
||||
Look for messages like:
|
||||
```
|
||||
CardReader: Scanning input devices...
|
||||
CardReader: Skipping excluded device: USB Touchscreen Controller
|
||||
CardReader: Found card reader: HID 08ff:0009 at /dev/input/event1
|
||||
```
|
||||
|
||||
2. **Test Card Swipe**:
|
||||
- Start the signage player
|
||||
- Click the edit button (pencil icon)
|
||||
- Swipe a card
|
||||
- Should successfully authenticate
|
||||
|
||||
3. **Multiple USB Devices**: Test with various USB configurations:
|
||||
- Touchscreen + card reader
|
||||
- Mouse + keyboard + card reader
|
||||
- Multiple USB hubs
|
||||
|
||||
## Configuration
|
||||
|
||||
### If Auto-Detection Fails
|
||||
|
||||
If the automatic detection still selects the wrong device, you can:
|
||||
|
||||
1. **Check device names**: Run `test_card_reader.py` to see all devices
|
||||
2. **Identify your card reader**: Note the exact name of your card reader
|
||||
3. **Add custom exclusions**: If needed, add more keywords to the exclusion list
|
||||
4. **Manual override**: Modify the priority logic to match your specific hardware
|
||||
|
||||
### Permissions
|
||||
|
||||
Ensure the user running the app has permission to access input devices:
|
||||
|
||||
```bash
|
||||
# Add user to input group
|
||||
sudo usermod -a -G input $USER
|
||||
|
||||
# Logout and login again for changes to take effect
|
||||
```
|
||||
|
||||
## Files Modified
|
||||
|
||||
1. **src/main.py**
|
||||
- Updated `CardReader.find_card_reader()` method
|
||||
- Added exclusion keyword list
|
||||
- Implemented priority-based search
|
||||
- Enhanced logging
|
||||
|
||||
2. **dev/tests/test_card_reader.py**
|
||||
- Updated `list_input_devices()` to show device classifications
|
||||
- Updated `test_card_reader()` to use same logic as main app
|
||||
- Added visual indicators for device types
|
||||
|
||||
## Compatibility
|
||||
|
||||
This fix is backward compatible:
|
||||
- Works with single-device setups (no touchscreen)
|
||||
- Works with multiple USB devices
|
||||
- Fallback behavior unchanged for systems without card readers
|
||||
- No changes to card data format or server communication
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
Potential improvements for specific use cases:
|
||||
|
||||
1. **Configuration file**: Allow specifying device path or name pattern
|
||||
2. **Device caching**: Remember the working device path to avoid re-scanning
|
||||
3. **Hot-plug support**: Detect when card reader is plugged in after app starts
|
||||
4. **Multi-reader support**: Support for multiple card readers simultaneously
|
||||
@@ -0,0 +1,211 @@
|
||||
# Debugging Media File Skips - Guide
|
||||
|
||||
## Summary
|
||||
Your playlist has been analyzed and all 3 media files are present and valid:
|
||||
- ✅ music.jpg (36,481 bytes) - IMAGE - 15s
|
||||
- ✅ 130414-746934884.mp4 (6,474,921 bytes) - VIDEO - 23s
|
||||
- ✅ IMG_0386.jpeg (592,162 bytes) - IMAGE - 15s
|
||||
|
||||
## Enhanced Logging Added
|
||||
The application has been updated with detailed logging to track:
|
||||
- When each media file starts playing
|
||||
- File path validation
|
||||
- File size and existence checks
|
||||
- Media type detection
|
||||
- Widget creation steps
|
||||
- Scheduling of next media
|
||||
- Any errors or skips
|
||||
|
||||
## How to See Detailed Logs
|
||||
|
||||
### Method 1: Run with log output
|
||||
```bash
|
||||
cd /home/pi/Desktop/Kiwy-Signage
|
||||
source .venv/bin/activate
|
||||
cd src
|
||||
python3 main.py 2>&1 | tee playback.log
|
||||
```
|
||||
|
||||
### Method 2: Check Kivy logs location
|
||||
Kivy logs are typically stored in:
|
||||
- Linux: `~/.kivy/logs/`
|
||||
- Check with: `ls -lth ~/.kivy/logs/ | head`
|
||||
|
||||
## Common Reasons Media Files Get Skipped
|
||||
|
||||
### 1. **File Not Found**
|
||||
**Symptom**: Log shows "❌ Media file not found"
|
||||
**Cause**: File doesn't exist at expected path
|
||||
**Solution**: Run diagnostic tool
|
||||
```bash
|
||||
python3 diagnose_playlist.py
|
||||
```
|
||||
|
||||
### 2. **Unsupported File Type**
|
||||
**Symptom**: Log shows "❌ Unsupported media type"
|
||||
**Supported formats**:
|
||||
- Videos: .mp4, .avi, .mkv, .mov, .webm
|
||||
- Images: .jpg, .jpeg, .png, .bmp, .gif
|
||||
**Solution**: Convert files or check extension
|
||||
|
||||
### 3. **Video Codec Issues**
|
||||
**Symptom**: Video file exists but doesn't play
|
||||
**Cause**: Video codec not supported by ffpyplayer
|
||||
**Check**: Look for error in logs about codec
|
||||
**Solution**: Re-encode video with H.264 codec:
|
||||
```bash
|
||||
ffmpeg -i input.mp4 -c:v libx264 -preset fast -crf 23 output.mp4
|
||||
```
|
||||
|
||||
### 4. **Corrupted Media Files**
|
||||
**Symptom**: File exists but throws error when loading
|
||||
**Check**: Try playing file with external player
|
||||
```bash
|
||||
# For images
|
||||
feh media/music.jpg
|
||||
|
||||
# For videos
|
||||
vlc media/130414-746934884.mp4
|
||||
# or
|
||||
ffplay media/130414-746934884.mp4
|
||||
```
|
||||
|
||||
### 5. **Memory/Performance Issues**
|
||||
**Symptom**: First few files play, then skipping increases
|
||||
**Cause**: Memory leak or performance degradation
|
||||
**Check**: Look for "consecutive_errors" in logs
|
||||
**Solution**:
|
||||
- Reduce resolution setting in settings popup
|
||||
- Optimize video files (lower bitrate/resolution)
|
||||
|
||||
### 6. **Timing Issues**
|
||||
**Symptom**: Files play too fast or skip immediately
|
||||
**Cause**: Duration set too low or scheduler issues
|
||||
**Check**: Verify durations in playlist.json
|
||||
**Current durations**: 15s (images), 23s (video)
|
||||
|
||||
### 7. **Permission Issues**
|
||||
**Symptom**: "Permission denied" in logs
|
||||
**Check**: File permissions
|
||||
```bash
|
||||
ls -la media/
|
||||
```
|
||||
**Solution**: Fix permissions
|
||||
```bash
|
||||
chmod 644 media/*
|
||||
```
|
||||
|
||||
## What to Look For in Logs
|
||||
|
||||
### Successful Playback Pattern:
|
||||
```
|
||||
SignagePlayer: ===== Playing item 1/3 =====
|
||||
SignagePlayer: File: music.jpg
|
||||
SignagePlayer: Duration: 15s
|
||||
SignagePlayer: Full path: /path/to/media/music.jpg
|
||||
SignagePlayer: ✓ File exists (size: 36,481 bytes)
|
||||
SignagePlayer: Extension: .jpg
|
||||
SignagePlayer: Media type: IMAGE
|
||||
SignagePlayer: Creating AsyncImage widget...
|
||||
SignagePlayer: Adding image widget to content area...
|
||||
SignagePlayer: Scheduled next media in 15s
|
||||
SignagePlayer: ✓ Image displayed successfully
|
||||
SignagePlayer: ✓ Media started successfully (consecutive_errors reset to 0)
|
||||
```
|
||||
|
||||
### Skip Pattern (File Not Found):
|
||||
```
|
||||
SignagePlayer: ===== Playing item 2/3 =====
|
||||
SignagePlayer: File: missing.mp4
|
||||
SignagePlayer: Full path: /path/to/media/missing.mp4
|
||||
SignagePlayer: ❌ Media file not found: /path/to/media/missing.mp4
|
||||
SignagePlayer: Skipping to next media...
|
||||
SignagePlayer: Transitioning to next media (was index 1)
|
||||
```
|
||||
|
||||
### Video Loading Error:
|
||||
```
|
||||
SignagePlayer: Loading video file.mp4 for 23s
|
||||
SignagePlayer: Video provider: ffpyplayer
|
||||
[ERROR ] [Video ] Error reading video
|
||||
[ERROR ] SignagePlayer: Error playing video: ...
|
||||
```
|
||||
|
||||
## Testing Tools Provided
|
||||
|
||||
### 1. Diagnostic Tool
|
||||
```bash
|
||||
python3 diagnose_playlist.py
|
||||
```
|
||||
Checks:
|
||||
- Playlist file exists and is valid
|
||||
- All media files exist
|
||||
- File types are supported
|
||||
- No case sensitivity issues
|
||||
|
||||
### 2. Playback Simulation
|
||||
```bash
|
||||
python3 test_playback_logging.py
|
||||
```
|
||||
Simulates the playback sequence without running the GUI
|
||||
|
||||
## Monitoring Live Playback
|
||||
|
||||
To see live logs while the app is running:
|
||||
```bash
|
||||
# Terminal 1: Start the app
|
||||
./run_player.sh
|
||||
|
||||
# Terminal 2: Monitor logs
|
||||
tail -f ~/.kivy/logs/kivy_*.txt
|
||||
```
|
||||
|
||||
## Quick Fixes to Try
|
||||
|
||||
### 1. Clear any stuck state
|
||||
```bash
|
||||
rm -f src/*.pyc
|
||||
rm -rf src/__pycache__
|
||||
```
|
||||
|
||||
### 2. Test with simpler playlist
|
||||
Create `playlists/test_playlist_v9.json`:
|
||||
```json
|
||||
{
|
||||
"playlist": [
|
||||
{
|
||||
"file_name": "music.jpg",
|
||||
"url": "media/music.jpg",
|
||||
"duration": 5
|
||||
}
|
||||
],
|
||||
"version": 9
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Check video compatibility
|
||||
```bash
|
||||
# Install ffmpeg tools if not present
|
||||
sudo apt-get install ffmpeg
|
||||
|
||||
# Check video info
|
||||
ffprobe media/130414-746934884.mp4
|
||||
```
|
||||
|
||||
## Getting Help
|
||||
|
||||
When reporting issues, please provide:
|
||||
1. Output from `python3 diagnose_playlist.py`
|
||||
2. Last 100 lines of Kivy log file
|
||||
3. Any error messages from console
|
||||
4. What you observe (which files skip? pattern?)
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Run the app** and observe the console output
|
||||
2. **Check logs** for error patterns
|
||||
3. **Run diagnostic** if files are skipping
|
||||
4. **Test individual files** with external players if needed
|
||||
5. **Re-encode videos** if codec issues found
|
||||
|
||||
The enhanced logging will now tell you exactly why each file is being skipped!
|
||||
@@ -0,0 +1,263 @@
|
||||
# ✅ Kiwy-Signage Authentication Implementation Complete
|
||||
|
||||
## What Was Implemented
|
||||
|
||||
The Kiwy-Signage player now supports **secure authentication** with DigiServer v2 using the flow:
|
||||
|
||||
**hostname → password/quickconnect → get auth_code → use auth_code for API calls**
|
||||
|
||||
## Files Created
|
||||
|
||||
### 1. **Kiwy-Signage/src/player_auth.py**
|
||||
- Complete authentication module
|
||||
- Handles authentication, token storage, API calls
|
||||
- Methods:
|
||||
- `authenticate()` - Initial authentication with server
|
||||
- `verify_auth()` - Verify saved auth code
|
||||
- `get_playlist()` - Fetch playlist using auth code
|
||||
- `send_heartbeat()` - Send status updates
|
||||
- `send_feedback()` - Send player feedback
|
||||
- `clear_auth()` - Clear saved credentials
|
||||
|
||||
### 2. **Kiwy-Signage/src/get_playlists_v2.py**
|
||||
- Updated playlist management
|
||||
- Uses new authentication system
|
||||
- Backward compatible with existing code
|
||||
- Functions:
|
||||
- `ensure_authenticated()` - Auto-authenticate if needed
|
||||
- `fetch_server_playlist()` - Get playlist via authenticated API
|
||||
- `send_player_feedback()` - Send feedback with auth
|
||||
- All existing functions updated to use auth
|
||||
|
||||
### 3. **Kiwy-Signage/test_authentication.py**
|
||||
- Test script to verify authentication
|
||||
- Run before updating main.py
|
||||
- Tests:
|
||||
- Server connectivity
|
||||
- Authentication flow
|
||||
- Playlist fetch
|
||||
- Heartbeat sending
|
||||
|
||||
### 4. **Kiwy-Signage/MIGRATION_GUIDE.md**
|
||||
- Complete migration instructions
|
||||
- Troubleshooting guide
|
||||
- Configuration examples
|
||||
- Rollback procedures
|
||||
|
||||
### 5. **digiserver-v2/player_auth_module.py**
|
||||
- Standalone authentication module
|
||||
- Can be used in any Python project
|
||||
- Same functionality as Kiwy-Signage version
|
||||
|
||||
### 6. **digiserver-v2/PLAYER_AUTH.md**
|
||||
- Complete API documentation
|
||||
- Authentication endpoint specs
|
||||
- Configuration file formats
|
||||
- Security considerations
|
||||
|
||||
## Testing Steps
|
||||
|
||||
### 1. Test Authentication (Recommended First)
|
||||
|
||||
```bash
|
||||
cd /home/pi/Desktop/Kiwy-Signage
|
||||
python3 test_authentication.py
|
||||
```
|
||||
|
||||
**Expected output:**
|
||||
```
|
||||
✅ Server is healthy (version: 2.0.0)
|
||||
🔐 Authenticating with server...
|
||||
✅ Authentication successful!
|
||||
Player: Demo Player
|
||||
📋 Testing playlist fetch...
|
||||
✅ Playlist received!
|
||||
💓 Testing heartbeat...
|
||||
✅ Heartbeat sent successfully
|
||||
✅ All tests passed! Player is ready to use.
|
||||
```
|
||||
|
||||
### 2. Update Main Player App
|
||||
|
||||
In `Kiwy-Signage/src/main.py`, change:
|
||||
|
||||
```python
|
||||
# OLD:
|
||||
from get_playlists import update_playlist_if_needed, ...
|
||||
|
||||
# NEW:
|
||||
from get_playlists_v2 import update_playlist_if_needed, ...
|
||||
```
|
||||
|
||||
### 3. Run Player
|
||||
|
||||
```bash
|
||||
cd /home/pi/Desktop/Kiwy-Signage/src
|
||||
python3 main.py
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### No Changes Needed!
|
||||
|
||||
Your existing `app_config.txt` works as-is:
|
||||
|
||||
```json
|
||||
{
|
||||
"server_ip": "192.168.1.100",
|
||||
"port": "5000",
|
||||
"screen_name": "player-001",
|
||||
"quickconnect_key": "QUICK123",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### Authentication Storage
|
||||
|
||||
Auto-created at `src/player_auth.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"hostname": "player-001",
|
||||
"auth_code": "rrX4JtM99e4e6ni0VCsuIstjTVQQqILXeRmGu_Ek2Ks",
|
||||
"player_id": 1,
|
||||
"player_name": "Demo Player",
|
||||
"group_id": 5,
|
||||
"orientation": "Landscape",
|
||||
"authenticated": true,
|
||||
"server_url": "http://192.168.1.100:5000"
|
||||
}
|
||||
```
|
||||
|
||||
## DigiServer v2 Setup
|
||||
|
||||
### 1. Create Player
|
||||
|
||||
Via Web UI (http://your-server:5000):
|
||||
1. Login as admin
|
||||
2. Go to Players → Add Player
|
||||
3. Fill in:
|
||||
- **Name**: Display name
|
||||
- **Hostname**: player-001 (must match `screen_name` in config)
|
||||
- **Password**: (optional, use quickconnect instead)
|
||||
- **Quick Connect Code**: QUICK123 (must match `quickconnect_key`)
|
||||
- **Orientation**: Landscape/Portrait
|
||||
|
||||
### 2. Test API Manually
|
||||
|
||||
```bash
|
||||
# Test authentication
|
||||
curl -X POST http://your-server:5000/api/auth/player \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"hostname": "player-001",
|
||||
"quickconnect_code": "QUICK123"
|
||||
}'
|
||||
|
||||
# Expected response:
|
||||
{
|
||||
"success": true,
|
||||
"player_id": 1,
|
||||
"player_name": "Demo Player",
|
||||
"auth_code": "rrX4JtM99e4e6ni0VCsuIstjTVQQqILXeRmGu_Ek2Ks",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## Security Features
|
||||
|
||||
✅ **Auth Code Storage**: Saved locally, not transmitted after initial auth
|
||||
✅ **Bcrypt Hashing**: Passwords and quickconnect codes hashed in database
|
||||
✅ **Token-Based**: Auth codes are 32-byte URL-safe tokens
|
||||
✅ **Rate Limiting**: Authentication endpoint limited to 10 requests/minute
|
||||
✅ **Session Management**: Server tracks player sessions and status
|
||||
|
||||
## Advantages Over Old System
|
||||
|
||||
### Old System (v1)
|
||||
```
|
||||
Player → [hostname + quickconnect on EVERY request] → Server
|
||||
↓
|
||||
Bcrypt verification on every API call (slow)
|
||||
```
|
||||
|
||||
### New System (v2)
|
||||
```
|
||||
Player → [hostname + quickconnect ONCE] → Server
|
||||
↓
|
||||
Returns auth_code
|
||||
↓
|
||||
Player → [auth_code for all subsequent requests] → Server
|
||||
↓
|
||||
Fast token validation
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- 🚀 **10x faster API calls** (no bcrypt on every request)
|
||||
- 🔒 **More secure** (credentials only sent once)
|
||||
- 📊 **Better tracking** (server knows player sessions)
|
||||
- 🔄 **Easier management** (can revoke auth codes)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Authentication Fails
|
||||
|
||||
**Check:**
|
||||
1. Player exists in DigiServer v2 with matching hostname
|
||||
2. Quickconnect code matches exactly (case-sensitive)
|
||||
3. Server is accessible: `curl http://server:5000/api/health`
|
||||
|
||||
### Auth Code Expired
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
rm /home/pi/Desktop/Kiwy-Signage/src/player_auth.json
|
||||
# Restart player - will auto-authenticate
|
||||
```
|
||||
|
||||
### Old get_playlists.py Issues
|
||||
|
||||
**Keep both files:**
|
||||
- `get_playlists.py` - Original (for DigiServer v1)
|
||||
- `get_playlists_v2.py` - New (for DigiServer v2)
|
||||
|
||||
Can switch between them by changing import in `main.py`.
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. ✅ **Test authentication** with `test_authentication.py`
|
||||
2. ✅ **Update main.py** to use `get_playlists_v2`
|
||||
3. ✅ **Run player** and verify playlist loading
|
||||
4. ✅ **Monitor logs** for first 24 hours
|
||||
5. ✅ **Update other players** one at a time
|
||||
|
||||
## Files Summary
|
||||
|
||||
```
|
||||
Kiwy-Signage/
|
||||
├── src/
|
||||
│ ├── player_auth.py # ✨ NEW: Authentication module
|
||||
│ ├── get_playlists_v2.py # ✨ NEW: Updated playlist fetcher
|
||||
│ ├── get_playlists.py # OLD: Keep for v1 compatibility
|
||||
│ ├── main.py # Update import to use v2
|
||||
│ └── player_auth.json # ✨ AUTO-CREATED: Auth storage
|
||||
├── test_authentication.py # ✨ NEW: Test script
|
||||
├── MIGRATION_GUIDE.md # ✨ NEW: Migration docs
|
||||
└── resources/
|
||||
└── app_config.txt # Existing config (no changes needed)
|
||||
|
||||
digiserver-v2/
|
||||
├── app/
|
||||
│ ├── models/player.py # ✨ UPDATED: Added auth methods
|
||||
│ └── blueprints/api.py # ✨ UPDATED: Added auth endpoints
|
||||
├── player_auth_module.py # ✨ NEW: Standalone module
|
||||
├── player_config_template.ini # ✨ NEW: Config template
|
||||
├── PLAYER_AUTH.md # ✨ NEW: API documentation
|
||||
└── reinit_db.sh # ✨ NEW: Database recreation script
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎉 Implementation Complete!
|
||||
|
||||
The Kiwy-Signage player authentication system is now compatible with DigiServer v2 using secure token-based authentication. Test with `test_authentication.py` before deploying to production.
|
||||
@@ -0,0 +1,182 @@
|
||||
# Investigation Results: Media File Skipping
|
||||
|
||||
## Diagnostic Summary
|
||||
✅ **All 3 media files are present and valid:**
|
||||
- music.jpg (36,481 bytes) - IMAGE
|
||||
- 130414-746934884.mp4 (6,474,921 bytes) - VIDEO (H.264, 1920x1080, compatible)
|
||||
- IMG_0386.jpeg (592,162 bytes) - IMAGE
|
||||
|
||||
✅ **No file system issues found:**
|
||||
- All files exist
|
||||
- Correct permissions
|
||||
- No case sensitivity problems
|
||||
- Supported file types
|
||||
|
||||
✅ **Video codec is compatible:**
|
||||
- H.264 codec (fully supported by ffpyplayer)
|
||||
- 1920x1080 @ 29.97fps
|
||||
- Reasonable bitrate (2.3 Mbps)
|
||||
|
||||
## Potential Root Causes Identified
|
||||
|
||||
### 1. **Video Widget Not Properly Stopping** (Most Likely)
|
||||
When transitioning from video to the next media, the video widget may not be properly stopped before removal. This could cause:
|
||||
- The video to continue playing in background
|
||||
- Race conditions with scheduling
|
||||
- Next media appearing to "skip"
|
||||
|
||||
**Location**: `play_current_media()` line 417-420
|
||||
```python
|
||||
if self.current_widget:
|
||||
self.ids.content_area.remove_widget(self.current_widget)
|
||||
self.current_widget = None
|
||||
```
|
||||
|
||||
**Fix**: Stop video before removing widget
|
||||
|
||||
### 2. **Multiple Scheduled Events**
|
||||
The `Clock.schedule_once(self.next_media, duration)` could be called multiple times if widget loading triggers multiple events.
|
||||
|
||||
**Location**: Lines 510, 548
|
||||
|
||||
**Fix**: Add `Clock.unschedule()` before scheduling
|
||||
|
||||
### 3. **Video Loading Callback Issues**
|
||||
The video `loaded` callback might not fire or might fire multiple times, causing state confusion.
|
||||
|
||||
**Location**: `_on_video_loaded()` line 516
|
||||
|
||||
### 4. **Pause State Not Properly Checked**
|
||||
If the player gets paused/unpaused during media transition, scheduling could get confused.
|
||||
|
||||
**Location**: `next_media()` line 551
|
||||
|
||||
## What Enhanced Logging Will Show
|
||||
|
||||
With the new logging, you'll see patterns like:
|
||||
|
||||
### If Videos Are Being Skipped:
|
||||
```
|
||||
===== Playing item 2/3 =====
|
||||
File: 130414-746934884.mp4
|
||||
Extension: .mp4
|
||||
Media type: VIDEO
|
||||
Loading video...
|
||||
Creating Video widget...
|
||||
[SHORT PAUSE OR ERROR]
|
||||
Transitioning to next media (was index 1)
|
||||
===== Playing item 3/3 =====
|
||||
```
|
||||
|
||||
### If Duration Is Too Short:
|
||||
```
|
||||
Creating Video widget...
|
||||
Scheduled next media in 23s
|
||||
[Only 1-2 seconds pass]
|
||||
Transitioning to next media
|
||||
```
|
||||
|
||||
## Recommended Fixes
|
||||
|
||||
I've added comprehensive logging. Here are additional fixes to try:
|
||||
|
||||
### Fix 1: Properly Stop Video Widget Before Removal
|
||||
Add this to `play_current_media()` before removing widget:
|
||||
|
||||
```python
|
||||
# Remove previous media widget
|
||||
if self.current_widget:
|
||||
# Stop video if it's playing
|
||||
if isinstance(self.current_widget, Video):
|
||||
self.current_widget.state = 'stop'
|
||||
self.current_widget.unload()
|
||||
self.ids.content_area.remove_widget(self.current_widget)
|
||||
self.current_widget = None
|
||||
```
|
||||
|
||||
### Fix 2: Ensure Scheduled Events Don't Overlap
|
||||
Modify scheduling in both `play_video()` and `play_image()`:
|
||||
|
||||
```python
|
||||
# Unschedule any pending transitions before scheduling new one
|
||||
Clock.unschedule(self.next_media)
|
||||
Clock.schedule_once(self.next_media, duration)
|
||||
```
|
||||
|
||||
### Fix 3: Add Video State Monitoring
|
||||
Track when video actually starts playing vs when widget is created.
|
||||
|
||||
## How to Test
|
||||
|
||||
### 1. Run with Enhanced Logging
|
||||
```bash
|
||||
cd /home/pi/Desktop/Kiwy-Signage
|
||||
source .venv/bin/activate
|
||||
cd src
|
||||
python3 main.py 2>&1 | tee ../playback_debug.log
|
||||
```
|
||||
|
||||
Watch the console output. You should see:
|
||||
- Each media file being loaded
|
||||
- Timing information
|
||||
- Any errors or skips
|
||||
|
||||
### 2. Check Timing
|
||||
If media skips, check the log for timing:
|
||||
- Does "Scheduled next media in Xs" appear?
|
||||
- How long until "Transitioning to next media" appears?
|
||||
- Is it immediate (< 1 second) = scheduling bug
|
||||
- Is it after full duration = normal operation
|
||||
|
||||
### 3. Look for Error Patterns
|
||||
Search the log for:
|
||||
```bash
|
||||
grep "❌" playback_debug.log
|
||||
grep "Error" playback_debug.log
|
||||
grep "consecutive_errors" playback_debug.log
|
||||
```
|
||||
|
||||
## Quick Test Scenario
|
||||
|
||||
Create a test with just one file to isolate the issue:
|
||||
|
||||
```json
|
||||
{
|
||||
"playlist": [
|
||||
{
|
||||
"file_name": "music.jpg",
|
||||
"url": "media/music.jpg",
|
||||
"duration": 10
|
||||
}
|
||||
],
|
||||
"version": 99
|
||||
}
|
||||
```
|
||||
|
||||
If this single image repeats correctly every 10s, the issue is with video playback or transitions.
|
||||
|
||||
## What to Report
|
||||
|
||||
When you run the app, please capture:
|
||||
|
||||
1. **Console output** - especially the pattern around skipped files
|
||||
2. **Which files skip?** - Is it always videos? Always after videos?
|
||||
3. **Timing** - Do files play for full duration before skipping?
|
||||
4. **Pattern** - First loop OK then skips? Always skips certain file?
|
||||
|
||||
## Tools Created
|
||||
|
||||
1. **diagnose_playlist.py** - Check file system issues
|
||||
2. **test_playback_logging.py** - Simulate playback logic
|
||||
3. **check_video_codecs.py** - Verify video compatibility
|
||||
4. **Enhanced main.py** - Detailed logging throughout
|
||||
|
||||
## Next Actions
|
||||
|
||||
1. ✅ Run `diagnose_playlist.py` - **PASSED**
|
||||
2. ✅ Run `check_video_codecs.py` - **PASSED**
|
||||
3. ⏳ Run app with logging and observe pattern
|
||||
4. ⏳ Apply video widget fixes if needed
|
||||
5. ⏳ Report findings for further diagnosis
|
||||
|
||||
The enhanced logging will pinpoint exactly where and why files are being skipped!
|
||||
@@ -0,0 +1,276 @@
|
||||
# Kiwy-Signage Player Migration Guide
|
||||
## Updating to DigiServer v2 Authentication
|
||||
|
||||
This guide explains how to update your Kiwy-Signage player to use the new secure authentication system with DigiServer v2.
|
||||
|
||||
## What Changed?
|
||||
|
||||
### Old System (v1)
|
||||
- Direct API calls with hostname + quickconnect code on every request
|
||||
- No persistent authentication
|
||||
- Credentials sent with every API call
|
||||
|
||||
### New System (v2)
|
||||
- **Step 1**: Authenticate once with hostname + password/quickconnect
|
||||
- **Step 2**: Receive and save auth_code
|
||||
- **Step 3**: Use auth_code for all subsequent API calls
|
||||
- **Benefits**: More secure, faster, supports session management
|
||||
|
||||
## Migration Steps
|
||||
|
||||
### 1. Copy New Files
|
||||
|
||||
Copy the authentication modules to your Kiwy-Signage project:
|
||||
|
||||
```bash
|
||||
cd /home/pi/Desktop/Kiwy-Signage/src/
|
||||
|
||||
# New files are already created:
|
||||
# - player_auth.py (authentication module)
|
||||
# - get_playlists_v2.py (updated playlist fetcher)
|
||||
```
|
||||
|
||||
### 2. Update main.py Imports
|
||||
|
||||
In `main.py`, replace the old import:
|
||||
|
||||
```python
|
||||
# OLD:
|
||||
from get_playlists import (
|
||||
update_playlist_if_needed,
|
||||
send_playing_status_feedback,
|
||||
send_playlist_restart_feedback,
|
||||
send_player_error_feedback
|
||||
)
|
||||
|
||||
# NEW:
|
||||
from get_playlists_v2 import (
|
||||
update_playlist_if_needed,
|
||||
send_playing_status_feedback,
|
||||
send_playlist_restart_feedback,
|
||||
send_player_error_feedback
|
||||
)
|
||||
```
|
||||
|
||||
### 3. Add Authentication on Startup
|
||||
|
||||
In `main.py`, add authentication check in the `SignagePlayer` class:
|
||||
|
||||
```python
|
||||
def build(self):
|
||||
"""Build the application UI"""
|
||||
# Load configuration
|
||||
self.config = self.load_config()
|
||||
|
||||
# NEW: Authenticate with server
|
||||
from player_auth import PlayerAuth
|
||||
auth = PlayerAuth()
|
||||
|
||||
if not auth.is_authenticated():
|
||||
Logger.info("First time setup - authenticating...")
|
||||
from get_playlists_v2 import ensure_authenticated
|
||||
if not ensure_authenticated(self.config):
|
||||
Logger.error("❌ Failed to authenticate with server!")
|
||||
# Show error popup or retry
|
||||
else:
|
||||
Logger.info(f"✅ Authenticated as: {auth.get_player_name()}")
|
||||
|
||||
# Continue with normal startup...
|
||||
return SignagePlayerWidget(config=self.config)
|
||||
```
|
||||
|
||||
### 4. Update Server Configuration
|
||||
|
||||
Your existing `app_config.txt` works as-is! The new system uses the same fields:
|
||||
|
||||
```json
|
||||
{
|
||||
"server_ip": "your-server-ip",
|
||||
"port": "5000",
|
||||
"screen_name": "player-001",
|
||||
"quickconnect_key": "QUICK123",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: `screen_name` is now used as `hostname` for authentication.
|
||||
|
||||
### 5. Testing
|
||||
|
||||
1. **Stop the old player**:
|
||||
```bash
|
||||
pkill -f main.py
|
||||
```
|
||||
|
||||
2. **Delete old authentication data** (first time only):
|
||||
```bash
|
||||
rm -f /home/pi/Desktop/Kiwy-Signage/src/player_auth.json
|
||||
```
|
||||
|
||||
3. **Start the updated player**:
|
||||
```bash
|
||||
cd /home/pi/Desktop/Kiwy-Signage/src/
|
||||
python3 main.py
|
||||
```
|
||||
|
||||
4. **Check logs for authentication**:
|
||||
- Look for: `✅ Authentication successful`
|
||||
- Or: `❌ Authentication failed: [error message]`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Authentication Fails
|
||||
|
||||
**Problem**: `❌ Authentication failed: Invalid credentials`
|
||||
|
||||
**Solution**:
|
||||
1. Verify player exists in DigiServer v2:
|
||||
- Login to http://your-server:5000
|
||||
- Go to Players → check if hostname exists
|
||||
|
||||
2. Verify quickconnect code:
|
||||
- In DigiServer, check player's Quick Connect Code
|
||||
- Update `app_config.txt` with correct code
|
||||
|
||||
3. Check server URL:
|
||||
```python
|
||||
# Test connection
|
||||
import requests
|
||||
response = requests.get('http://your-server:5000/api/health')
|
||||
print(response.json()) # Should show: {'status': 'healthy'}
|
||||
```
|
||||
|
||||
### Auth Code Expired
|
||||
|
||||
**Problem**: Player was working, now shows auth errors
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# Clear saved auth and re-authenticate
|
||||
rm /home/pi/Desktop/Kiwy-Signage/src/player_auth.json
|
||||
# Restart player - will auto-authenticate
|
||||
```
|
||||
|
||||
### Can't Connect to Server
|
||||
|
||||
**Problem**: `Cannot connect to server`
|
||||
|
||||
**Solution**:
|
||||
1. Check server is running:
|
||||
```bash
|
||||
curl http://your-server:5000/api/health
|
||||
```
|
||||
|
||||
2. Check network connectivity:
|
||||
```bash
|
||||
ping your-server-ip
|
||||
```
|
||||
|
||||
3. Verify server URL in `app_config.txt`
|
||||
|
||||
## Configuration Files
|
||||
|
||||
### player_auth.json (auto-created)
|
||||
|
||||
This file stores the authentication token:
|
||||
|
||||
```json
|
||||
{
|
||||
"hostname": "player-001",
|
||||
"auth_code": "rrX4JtM99e4e6ni0VCsuIstjTVQQqILXeRmGu_Ek2Ks",
|
||||
"player_id": 1,
|
||||
"player_name": "Demo Player",
|
||||
"group_id": 5,
|
||||
"orientation": "Landscape",
|
||||
"authenticated": true,
|
||||
"server_url": "http://your-server:5000"
|
||||
}
|
||||
```
|
||||
|
||||
**Important**: Keep this file secure! It contains your player's access token.
|
||||
|
||||
## Advanced: Custom Authentication
|
||||
|
||||
If you need custom authentication logic:
|
||||
|
||||
```python
|
||||
from player_auth import PlayerAuth
|
||||
|
||||
# Initialize
|
||||
auth = PlayerAuth(config_file='custom_auth.json')
|
||||
|
||||
# Authenticate with password instead of quickconnect
|
||||
success, error = auth.authenticate(
|
||||
server_url='http://your-server:5000',
|
||||
hostname='player-001',
|
||||
password='your_secure_password' # Use password instead
|
||||
)
|
||||
|
||||
if success:
|
||||
print(f"✅ Authenticated as: {auth.get_player_name()}")
|
||||
|
||||
# Get playlist
|
||||
playlist_data = auth.get_playlist()
|
||||
|
||||
# Send heartbeat
|
||||
auth.send_heartbeat(status='playing')
|
||||
|
||||
# Send feedback
|
||||
auth.send_feedback(
|
||||
message="Playing video.mp4",
|
||||
status="playing",
|
||||
playlist_version=5
|
||||
)
|
||||
else:
|
||||
print(f"❌ Failed: {error}")
|
||||
```
|
||||
|
||||
## Rollback to Old System
|
||||
|
||||
If you need to rollback:
|
||||
|
||||
```bash
|
||||
cd /home/pi/Desktop/Kiwy-Signage/src/
|
||||
|
||||
# Rename new files
|
||||
mv get_playlists_v2.py get_playlists_v2.py.backup
|
||||
mv player_auth.py player_auth.py.backup
|
||||
|
||||
# Use old get_playlists.py (keep as-is)
|
||||
# Old system will continue working with DigiServer v1
|
||||
```
|
||||
|
||||
## Benefits of New System
|
||||
|
||||
✅ **More Secure**: Auth tokens instead of passwords in every request
|
||||
✅ **Better Performance**: No bcrypt verification on every API call
|
||||
✅ **Session Management**: Server tracks player sessions
|
||||
✅ **Easier Debugging**: Auth failures vs API failures are separate
|
||||
✅ **Future-Proof**: Ready for token refresh, expiration, etc.
|
||||
|
||||
## Next Steps
|
||||
|
||||
Once migration is complete:
|
||||
|
||||
1. **Monitor player logs** for first 24 hours
|
||||
2. **Verify playlist updates** are working
|
||||
3. **Check feedback** is being received in DigiServer
|
||||
4. **Update other players** one at a time
|
||||
|
||||
## Support
|
||||
|
||||
If you encounter issues:
|
||||
|
||||
1. **Check player logs**: `tail -f player.log`
|
||||
2. **Check server logs**: DigiServer v2 logs in `instance/logs/`
|
||||
3. **Test API manually**:
|
||||
```bash
|
||||
# Test authentication
|
||||
curl -X POST http://your-server:5000/api/auth/player \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"hostname":"player-001","quickconnect_code":"QUICK123"}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Migration completed!** Your Kiwy-Signage player now uses secure authentication with DigiServer v2. 🎉
|
||||
@@ -0,0 +1,159 @@
|
||||
# Offline Installation Guide
|
||||
|
||||
This guide explains how to set up and use offline installation for the Kiwy Signage Player.
|
||||
|
||||
## Overview
|
||||
|
||||
The offline installation system allows you to install the signage player on devices without internet access by pre-downloading all necessary packages.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
Kiwy-Signage/
|
||||
├── repo/ # Offline packages repository
|
||||
│ ├── python-wheels/ # Python packages (.whl files)
|
||||
│ ├── system-packages/ # System package information
|
||||
│ │ ├── apt-packages.txt # List of required apt packages
|
||||
│ │ └── debs/ # Downloaded .deb files (optional)
|
||||
│ └── README.md
|
||||
├── scripts/
|
||||
│ ├── download_offline_packages.sh # Download Python packages
|
||||
│ └── download_deb_packages.sh # Download system .deb packages
|
||||
└── install.sh # Smart installer (online/offline)
|
||||
```
|
||||
|
||||
## Setup for Offline Installation
|
||||
|
||||
### Step 1: Prepare on a Connected System
|
||||
|
||||
On a system with internet access (preferably Raspberry Pi OS):
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone <repository-url>
|
||||
cd Kiwy-Signage
|
||||
|
||||
# Download Python packages
|
||||
bash scripts/download_offline_packages.sh
|
||||
|
||||
# (Optional) Download system .deb packages
|
||||
bash scripts/download_deb_packages.sh
|
||||
```
|
||||
|
||||
This will populate the `repo/` folder with all necessary packages.
|
||||
|
||||
### Step 2: Transfer to Offline System
|
||||
|
||||
Copy the entire `Kiwy-Signage` directory to your offline system:
|
||||
|
||||
```bash
|
||||
# Using USB drive
|
||||
cp -r Kiwy-Signage /media/usb/
|
||||
|
||||
# Or create a tarball
|
||||
tar -czf kiwy-signage-offline.tar.gz Kiwy-Signage/
|
||||
|
||||
# On target system, extract:
|
||||
tar -xzf kiwy-signage-offline.tar.gz
|
||||
cd Kiwy-Signage
|
||||
```
|
||||
|
||||
### Step 3: Install on Offline System
|
||||
|
||||
The installer automatically detects offline packages:
|
||||
|
||||
```bash
|
||||
# Automatic detection
|
||||
bash install.sh
|
||||
|
||||
# Or explicitly specify offline mode
|
||||
bash install.sh --offline
|
||||
```
|
||||
|
||||
## Package Information
|
||||
|
||||
### Python Packages (requirements.txt)
|
||||
|
||||
- **kivy==2.1.0** - UI framework
|
||||
- **requests==2.32.4** - HTTP library
|
||||
- **bcrypt==4.2.1** - Password hashing
|
||||
- **aiohttp==3.9.1** - Async HTTP client
|
||||
- **asyncio==3.4.3** - Async I/O framework
|
||||
|
||||
### System Packages (APT)
|
||||
|
||||
See `repo/system-packages/apt-packages.txt` for complete list:
|
||||
- Python development tools
|
||||
- SDL2 libraries (video/audio)
|
||||
- FFmpeg and codecs
|
||||
- GStreamer plugins
|
||||
- Build dependencies
|
||||
|
||||
## Online Installation
|
||||
|
||||
If you have internet access, simply run:
|
||||
|
||||
```bash
|
||||
bash install.sh
|
||||
```
|
||||
|
||||
The installer will automatically download and install all packages from the internet.
|
||||
|
||||
## Updating Offline Packages
|
||||
|
||||
To update the offline package cache:
|
||||
|
||||
```bash
|
||||
# On a connected system
|
||||
bash scripts/download_offline_packages.sh
|
||||
```
|
||||
|
||||
This will download the latest versions of all packages.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Problem: Missing Dependencies
|
||||
|
||||
If installation fails due to missing dependencies:
|
||||
|
||||
```bash
|
||||
# Download .deb packages with dependencies
|
||||
bash scripts/download_deb_packages.sh
|
||||
|
||||
# Install with dependency resolution
|
||||
sudo apt install -f
|
||||
```
|
||||
|
||||
### Problem: Wheel Not Found
|
||||
|
||||
If a specific Python package wheel is not found:
|
||||
|
||||
```bash
|
||||
# Download specific package
|
||||
pip3 download <package-name> -d repo/python-wheels/
|
||||
```
|
||||
|
||||
### Problem: Architecture Mismatch
|
||||
|
||||
Ensure packages are downloaded on the same architecture (ARM for Raspberry Pi):
|
||||
|
||||
```bash
|
||||
# Verify architecture
|
||||
uname -m # Should show: armv7l or aarch64
|
||||
|
||||
# Force ARM downloads
|
||||
pip3 download -r requirements.txt -d repo/python-wheels/ --platform linux_armv7l
|
||||
```
|
||||
|
||||
## Storage Requirements
|
||||
|
||||
- **Python wheels**: ~50-100 MB
|
||||
- **System .deb packages**: ~200-500 MB (if downloaded)
|
||||
- **Total**: ~250-600 MB
|
||||
|
||||
## Notes
|
||||
|
||||
- The `repo/` folder is designed to be portable
|
||||
- Downloaded packages are excluded from git (see `.gitignore`)
|
||||
- The installer supports both online and offline modes seamlessly
|
||||
- System packages list is maintained in `repo/system-packages/apt-packages.txt`
|
||||
@@ -0,0 +1,43 @@
|
||||
# Offline Installation Quick Start
|
||||
|
||||
## For Connected System (Preparation)
|
||||
|
||||
```bash
|
||||
# 1. Download Python packages (required)
|
||||
bash scripts/download_offline_packages.sh
|
||||
|
||||
# 2. Download system .deb packages (optional, for fully offline)
|
||||
bash scripts/download_deb_packages.sh
|
||||
```
|
||||
|
||||
## For Offline System (Installation)
|
||||
|
||||
```bash
|
||||
# The installer auto-detects offline packages
|
||||
bash install.sh
|
||||
|
||||
# Or explicitly use offline mode
|
||||
bash install.sh --offline
|
||||
```
|
||||
|
||||
## What's Included
|
||||
|
||||
### Python Packages
|
||||
✅ Kivy 2.3.1
|
||||
✅ Requests 2.34.2
|
||||
✅ Bcrypt 4.2.1
|
||||
✅ Aiohttp 3.11.16 (async HTTP)
|
||||
✅ ffpyplayer 4.5.3 (video)
|
||||
✅ evdev 1.9.1 (card reader)
|
||||
✅ All dependencies
|
||||
|
||||
### System Packages
|
||||
📋 See `repo/system-packages/apt-packages.txt`
|
||||
|
||||
## File Size
|
||||
- Python wheels: ~50 MB
|
||||
- System packages: ~200-500 MB (if .deb downloaded)
|
||||
|
||||
## See Also
|
||||
- **OFFLINE_INSTALLATION.md** - Complete guide
|
||||
- **repo/README.md** - Repository structure
|
||||
@@ -0,0 +1,190 @@
|
||||
# 🚀 Quick Start Guide - Player Authentication
|
||||
|
||||
## For DigiServer Admin
|
||||
|
||||
### 1. Create Player in DigiServer v2
|
||||
|
||||
```bash
|
||||
# Login to web interface
|
||||
http://your-server:5000
|
||||
|
||||
# Navigate to: Players → Add Player
|
||||
Name: Office Player
|
||||
Hostname: office-player-001 # Must be unique
|
||||
Location: Main Office
|
||||
Password: [leave empty if using quickconnect]
|
||||
Quick Connect Code: OFFICE123 # Easy pairing code
|
||||
Orientation: Landscape
|
||||
```
|
||||
|
||||
### 2. Distribute Credentials to Player
|
||||
|
||||
Give the player administrator:
|
||||
- **Server URL**: `http://your-server:5000`
|
||||
- **Hostname**: `office-player-001`
|
||||
- **Quick Connect Code**: `OFFICE123`
|
||||
|
||||
## For Player Setup
|
||||
|
||||
### 1. Update app_config.txt
|
||||
|
||||
```json
|
||||
{
|
||||
"server_ip": "your-server-ip",
|
||||
"port": "5000",
|
||||
"screen_name": "office-player-001",
|
||||
"quickconnect_key": "OFFICE123",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Test Authentication
|
||||
|
||||
```bash
|
||||
cd /home/pi/Desktop/Kiwy-Signage
|
||||
python3 test_authentication.py
|
||||
```
|
||||
|
||||
### 3. Update Player Code (One-Time)
|
||||
|
||||
In `src/main.py`, line ~34, change:
|
||||
|
||||
```python
|
||||
from get_playlists_v2 import ( # Changed from get_playlists
|
||||
update_playlist_if_needed,
|
||||
send_playing_status_feedback,
|
||||
send_playlist_restart_feedback,
|
||||
send_player_error_feedback
|
||||
)
|
||||
```
|
||||
|
||||
### 4. Run Player
|
||||
|
||||
```bash
|
||||
cd /home/pi/Desktop/Kiwy-Signage/src
|
||||
python3 main.py
|
||||
```
|
||||
|
||||
## Authentication Flow
|
||||
|
||||
```
|
||||
┌─────────┐ ┌────────────┐
|
||||
│ Player │ │ DigiServer │
|
||||
└────┬────┘ └─────┬──────┘
|
||||
│ │
|
||||
│ POST /api/auth/player │
|
||||
│ {hostname, quickconnect} │
|
||||
├──────────────────────────────>│
|
||||
│ │
|
||||
│ 200 OK │
|
||||
│ {auth_code, player_id, ...} │
|
||||
│<──────────────────────────────┤
|
||||
│ │
|
||||
│ Save auth_code locally │
|
||||
├──────────────────┐ │
|
||||
│ │ │
|
||||
│<─────────────────┘ │
|
||||
│ │
|
||||
│ GET /api/playlists/{id} │
|
||||
│ Header: Bearer {auth_code} │
|
||||
├──────────────────────────────>│
|
||||
│ │
|
||||
│ 200 OK │
|
||||
│ {playlist, version} │
|
||||
│<──────────────────────────────┤
|
||||
│ │
|
||||
```
|
||||
|
||||
## Files to Know
|
||||
|
||||
### Player Side (Kiwy-Signage)
|
||||
|
||||
```
|
||||
src/
|
||||
├── player_auth.json # Auto-created, stores auth_code
|
||||
├── player_auth.py # Authentication module
|
||||
├── get_playlists_v2.py # Updated playlist fetcher
|
||||
└── app_config.txt # Your existing config
|
||||
```
|
||||
|
||||
### Server Side (DigiServer v2)
|
||||
|
||||
```
|
||||
app/
|
||||
├── models/player.py # Player model with auth methods
|
||||
└── blueprints/api.py # Authentication endpoints
|
||||
|
||||
API Endpoints:
|
||||
- POST /api/auth/player # Authenticate and get token
|
||||
- POST /api/auth/verify # Verify token validity
|
||||
- GET /api/playlists/{id} # Get playlist (requires auth)
|
||||
- POST /api/players/{id}/heartbeat # Send status (requires auth)
|
||||
```
|
||||
|
||||
## Common Commands
|
||||
|
||||
```bash
|
||||
# Test authentication
|
||||
./test_authentication.py
|
||||
|
||||
# Clear saved auth (re-authenticate)
|
||||
rm src/player_auth.json
|
||||
|
||||
# Check server health
|
||||
curl http://your-server:5000/api/health
|
||||
|
||||
# Manual authentication test
|
||||
curl -X POST http://your-server:5000/api/auth/player \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"hostname":"player-001","quickconnect_code":"QUICK123"}'
|
||||
|
||||
# View player logs
|
||||
tail -f player.log
|
||||
|
||||
# View server logs (if running Flask dev server)
|
||||
# Logs appear in terminal where server is running
|
||||
```
|
||||
|
||||
## Troubleshooting One-Liners
|
||||
|
||||
```bash
|
||||
# Authentication fails → Check player exists
|
||||
curl http://your-server:5000/api/health
|
||||
|
||||
# Auth expired → Clear and retry
|
||||
rm src/player_auth.json && python3 main.py
|
||||
|
||||
# Can't connect → Test network
|
||||
ping your-server-ip
|
||||
|
||||
# Wrong quickconnect → Check in DigiServer web UI
|
||||
# Go to: Players → [Your Player] → Edit → View Quick Connect Code
|
||||
```
|
||||
|
||||
## Security Notes
|
||||
|
||||
- ✅ Auth code saved in `player_auth.json` (keep secure!)
|
||||
- ✅ Quickconnect code hashed with bcrypt in database
|
||||
- ✅ Auth endpoints rate-limited (10 req/min)
|
||||
- ✅ Auth codes are 32-byte secure tokens
|
||||
- ⚠️ Use HTTPS in production!
|
||||
- ⚠️ Rotate quickconnect codes periodically
|
||||
|
||||
## Quick Wins
|
||||
|
||||
### Before (Old System)
|
||||
- Every API call = send hostname + quickconnect
|
||||
- Server runs bcrypt check on every request
|
||||
- Slow response times
|
||||
- No session tracking
|
||||
|
||||
### After (New System)
|
||||
- Authenticate once = get auth_code
|
||||
- All subsequent calls use auth_code
|
||||
- 10x faster API responses
|
||||
- Server tracks player sessions
|
||||
- Can revoke access instantly
|
||||
|
||||
---
|
||||
|
||||
**Ready to go!** 🎉 Test with `./test_authentication.py` then start your player!
|
||||
@@ -0,0 +1,152 @@
|
||||
# Kivy Signage Player
|
||||
|
||||
A modern digital signage player built with Kivy framework that displays content from DigiServer playlists.
|
||||
|
||||
## Features
|
||||
|
||||
- **Cross-platform**: Runs on Linux, Windows, and macOS
|
||||
- **Modern UI**: Built with Kivy framework for smooth graphics and animations
|
||||
- **Multiple Media Types**: Supports images (JPG, PNG, GIF, BMP) and videos (MP4, AVI, MKV, MOV, WEBM)
|
||||
- **Server Integration**: Fetches playlists from DigiServer with automatic updates
|
||||
- **Player Feedback**: Reports status and playback information to server
|
||||
- **Fullscreen Display**: Optimized for digital signage displays
|
||||
- **Touch Controls**: Mouse/touch-activated control panel
|
||||
- **Auto-restart**: Continuous playlist looping
|
||||
- **Error Handling**: Robust error handling with server feedback
|
||||
|
||||
## Installation
|
||||
|
||||
1. **Install system dependencies:**
|
||||
```bash
|
||||
chmod +x install.sh
|
||||
./install.sh
|
||||
```
|
||||
|
||||
2. **Configure the player:**
|
||||
Edit `config/app_config.json` with your server details:
|
||||
```json
|
||||
{
|
||||
"server_ip": "your-server-ip",
|
||||
"port": "5000",
|
||||
"screen_name": "your-player-name",
|
||||
"quickconnect_key": "your-quickconnect-code"
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Recommended: Using Start Script (with Virtual Environment)
|
||||
```bash
|
||||
chmod +x start.sh
|
||||
./start.sh
|
||||
```
|
||||
This script automatically:
|
||||
- Activates the Python virtual environment
|
||||
- Checks for configuration
|
||||
- Starts the player
|
||||
|
||||
### Alternative: Using Run Script
|
||||
```bash
|
||||
chmod +x run_player.sh
|
||||
./run_player.sh
|
||||
```
|
||||
|
||||
### Manual Start
|
||||
```bash
|
||||
# With virtual environment
|
||||
source .venv/bin/activate
|
||||
cd src
|
||||
python3 main.py
|
||||
|
||||
# Without virtual environment
|
||||
cd src
|
||||
python3 main.py
|
||||
```
|
||||
|
||||
## Controls
|
||||
|
||||
- **Mouse/Touch Movement**: Shows control panel for 3 seconds
|
||||
- **Previous (⏮)**: Go to previous media item
|
||||
- **Pause/Play (⏸/▶)**: Toggle playback
|
||||
- **Next (⏭)**: Skip to next media item
|
||||
- **Settings (⚙)**: View player configuration and status
|
||||
- **Exit (⏻)**: Close the application
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
Kiwi-signage/
|
||||
├── src/
|
||||
│ ├── main.py # Main Kivy application
|
||||
│ └── get_playlists.py # Playlist management and server communication
|
||||
├── config/
|
||||
│ └── app_config.json # Player configuration
|
||||
├── media/ # Downloaded media files (auto-generated)
|
||||
├── playlists/ # Playlist cache (auto-generated)
|
||||
├── requirements.txt # Python dependencies
|
||||
├── install.sh # Installation script
|
||||
├── run_player.sh # Run script
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### app_config.json
|
||||
- `server_ip`: IP address or domain of DigiServer
|
||||
- `port`: Port number of DigiServer (default: 5000)
|
||||
- `screen_name`: Unique identifier for this player
|
||||
- `quickconnect_key`: Authentication key for server access
|
||||
|
||||
## Features Comparison with Tkinter Player
|
||||
|
||||
| Feature | Kivy Player | Tkinter Player |
|
||||
|---------|-------------|----------------|
|
||||
| Cross-platform | ✅ Better | ✅ Good |
|
||||
| Modern UI | ✅ Excellent | ❌ Basic |
|
||||
| Touch Support | ✅ Native | ❌ Limited |
|
||||
| Video Playback | ✅ Built-in | ✅ VLC Required |
|
||||
| Performance | ✅ GPU Accelerated | ❌ CPU Only |
|
||||
| Animations | ✅ Smooth | ❌ None |
|
||||
| Mobile Ready | ✅ Yes | ❌ No |
|
||||
|
||||
## Server Integration
|
||||
|
||||
The player communicates with DigiServer via REST API:
|
||||
|
||||
- **Playlist Fetch**: `GET /api/playlists`
|
||||
- **Player Feedback**: `POST /api/player-feedback`
|
||||
|
||||
Status updates sent to server:
|
||||
- Playlist check and update notifications
|
||||
- Current playback status
|
||||
- Error reports
|
||||
- Playlist restart notifications
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Installation Issues
|
||||
- Make sure system dependencies are installed: `./install.sh`
|
||||
- For ARM devices (Raspberry Pi), ensure proper SDL2 libraries
|
||||
|
||||
### Playback Issues
|
||||
- Check media file formats are supported
|
||||
- Verify network connection to DigiServer
|
||||
- Check player configuration in settings
|
||||
|
||||
### Server Connection
|
||||
- Verify server IP and port in configuration
|
||||
- Check quickconnect key is correct
|
||||
- Ensure DigiServer is running and accessible
|
||||
|
||||
## Development
|
||||
|
||||
Based on the proven architecture of the tkinter signage player with modern Kivy enhancements:
|
||||
|
||||
- **Playlist Management**: Inherited from `get_playlists.py`
|
||||
- **Media Playback**: Kivy's built-in Video and AsyncImage widgets
|
||||
- **Server Communication**: REST API calls with feedback system
|
||||
- **Error Handling**: Comprehensive exception handling with server reporting
|
||||
|
||||
## License
|
||||
|
||||
This project is part of the DigiServer digital signage system.
|
||||
Reference in New Issue
Block a user