updated player

This commit is contained in:
2025-11-22 09:48:48 +02:00
parent 493f307599
commit 3f9674517d
26 changed files with 1208 additions and 13 deletions

View File

@@ -1,5 +1,5 @@
{
"server_ip": "https://digi-signage.moto-adv.com",
"server_ip": "digi-signage.moto-adv.com",
"port": "443",
"screen_name": "tv-terasa",
"quickconnect_key": "8887779",

BIN
config/resources/intro1.mp4 Normal file

Binary file not shown.

View File

@@ -395,33 +395,58 @@ class SignagePlayer(Widget):
file_name = media_item.get('file_name', '')
duration = media_item.get('duration', 10)
Logger.info(f"SignagePlayer: ===== Playing item {self.current_index + 1}/{len(self.playlist)} =====")
Logger.info(f"SignagePlayer: File: {file_name}")
Logger.info(f"SignagePlayer: Duration: {duration}s")
# Construct full path to media file
media_path = os.path.join(self.media_dir, file_name)
Logger.info(f"SignagePlayer: Full path: {media_path}")
if not os.path.exists(media_path):
Logger.error(f"SignagePlayer: Media file not found: {media_path}")
Logger.error(f"SignagePlayer: Media file not found: {media_path}")
Logger.error(f"SignagePlayer: Skipping to next media...")
self.consecutive_errors += 1
self.next_media()
return
Logger.info(f"SignagePlayer: ✓ File exists (size: {os.path.getsize(media_path):,} bytes)")
# Remove status label if showing
self.ids.status_label.opacity = 0
# Remove previous media widget
if self.current_widget:
# Properly stop video if it's playing to prevent resource leaks
if isinstance(self.current_widget, Video):
try:
Logger.info(f"SignagePlayer: Stopping previous video widget...")
self.current_widget.state = 'stop'
self.current_widget.unload()
except Exception as e:
Logger.warning(f"SignagePlayer: Error stopping video: {e}")
self.ids.content_area.remove_widget(self.current_widget)
self.current_widget = None
Logger.info(f"SignagePlayer: Previous widget removed")
# Determine media type and create appropriate widget
file_extension = os.path.splitext(file_name)[1].lower()
Logger.info(f"SignagePlayer: Extension: {file_extension}")
if file_extension in ['.mp4', '.avi', '.mkv', '.mov', '.webm']:
# Video file
Logger.info(f"SignagePlayer: Media type: VIDEO")
self.play_video(media_path, duration)
elif file_extension in ['.jpg', '.jpeg', '.png', '.bmp', '.gif']:
# Image file
Logger.info(f"SignagePlayer: Media type: IMAGE")
self.play_image(media_path, duration)
else:
Logger.warning(f"SignagePlayer: Unsupported media type: {file_extension}")
Logger.warning(f"SignagePlayer: Unsupported media type: {file_extension}")
Logger.warning(f"SignagePlayer: Supported: .mp4/.avi/.mkv/.mov/.webm/.jpg/.jpeg/.png/.bmp/.gif")
Logger.warning(f"SignagePlayer: Skipping to next media...")
self.consecutive_errors += 1
self.next_media()
return
@@ -438,6 +463,7 @@ class SignagePlayer(Widget):
# Reset error counter on successful playback
self.consecutive_errors = 0
Logger.info(f"SignagePlayer: ✓ Media started successfully (consecutive_errors reset to 0)")
except Exception as e:
Logger.error(f"SignagePlayer: Error playing media: {e}")
@@ -459,13 +485,16 @@ class SignagePlayer(Widget):
try:
# Verify file exists
if not os.path.exists(video_path):
Logger.error(f"Video file not found: {video_path}")
Logger.error(f"SignagePlayer: ❌ Video file not found: {video_path}")
self.consecutive_errors += 1
self.next_media()
return
Logger.info(f"SignagePlayer: Loading video {os.path.basename(video_path)} for {duration}s")
Logger.info(f"SignagePlayer: Video provider: {os.environ.get('KIVY_VIDEO', 'default')}")
# Create Video widget with optimized settings
Logger.info(f"SignagePlayer: Creating Video widget...")
self.current_widget = Video(
source=video_path,
state='play', # Start playing immediately
@@ -483,9 +512,12 @@ class SignagePlayer(Widget):
self.current_widget.bind(on_eos=self._on_video_eos)
# Add to content area
Logger.info(f"SignagePlayer: Adding video widget to content area...")
self.ids.content_area.add_widget(self.current_widget)
# Schedule next media after duration
# Schedule next media after duration (unschedule first to prevent overlaps)
Logger.info(f"SignagePlayer: Scheduled next media in {duration}s")
Clock.unschedule(self.next_media)
Clock.schedule_once(self.next_media, duration)
except Exception as e:
@@ -512,6 +544,7 @@ class SignagePlayer(Widget):
def play_image(self, image_path, duration):
"""Play an image file"""
try:
Logger.info(f"SignagePlayer: Creating AsyncImage widget...")
self.current_widget = AsyncImage(
source=image_path,
allow_stretch=True,
@@ -519,9 +552,13 @@ class SignagePlayer(Widget):
size_hint=(1, 1),
pos_hint={'center_x': 0.5, 'center_y': 0.5}
)
Logger.info(f"SignagePlayer: Adding image widget to content area...")
self.ids.content_area.add_widget(self.current_widget)
# Schedule next media after duration
# Schedule next media after duration (unschedule first to prevent overlaps)
Logger.info(f"SignagePlayer: Scheduled next media in {duration}s")
Clock.unschedule(self.next_media)
Clock.schedule_once(self.next_media, duration)
Logger.info(f"SignagePlayer: ✓ Image displayed successfully")
except Exception as e:
Logger.error(f"SignagePlayer: Error playing image {image_path}: {e}")
self.consecutive_errors += 1
@@ -531,8 +568,10 @@ class SignagePlayer(Widget):
def next_media(self, dt=None):
"""Move to next media item"""
if self.is_paused:
Logger.debug(f"SignagePlayer: Skipping next_media - player is paused")
return
Logger.info(f"SignagePlayer: Transitioning to next media (was index {self.current_index})")
self.current_index += 1
# Unschedule any pending media transitions

View File

@@ -1,10 +1,10 @@
{
"hostname": "rpi-tvholba1",
"auth_code": "aDHIMS2yx_HhfR0dWKy9VHaM_h0CKemfcsqv4Zgp0IY",
"player_id": 2,
"player_name": "Tv-Anunturi",
"group_id": null,
"hostname": "tv-terasa",
"auth_code": "iiSyZDLWGyqNIxeRt54XYREgvAio11RwwU1_oJev6WI",
"player_id": 1,
"player_name": "TV-acasa 1",
"playlist_id": 1,
"orientation": "Landscape",
"authenticated": true,
"server_url": "http://172.18.0.1:5000"
"server_url": "https://digi-signage.moto-adv.com:443"
}

View File

@@ -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!

View File

@@ -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!

View File

@@ -0,0 +1,35 @@
#!/usr/bin/env python3
"""Analyze what's happening with the playlist download."""
import json
# Check the saved playlist
playlist_file = 'playlists/server_playlist_v8.json'
print("=" * 80)
print("SAVED PLAYLIST ANALYSIS")
print("=" * 80)
with open(playlist_file, 'r') as f:
data = json.load(f)
print(f"\nVersion: {data.get('version', 'N/A')}")
print(f"Items in playlist: {len(data.get('playlist', []))}")
print("\nPlaylist items:")
for idx, item in enumerate(data.get('playlist', []), 1):
print(f"\n{idx}. File: {item.get('file_name', 'N/A')}")
print(f" URL: {item.get('url', 'N/A')}")
print(f" Duration: {item.get('duration', 'N/A')}s")
print("\n" + "=" * 80)
print("\n⚠️ ISSUE: Server has 5 files, but only 3 are saved!")
print("\nPossible reasons:")
print("1. Server sent only 3 files")
print("2. 2 files failed to download and were skipped")
print("3. Download function has a bug")
print("\nThe download_media_files() function in get_playlists_v2.py:")
print("- Downloads from the 'url' field in the playlist")
print("- If download fails, it SKIPS the file (continues)")
print("- Only successfully downloaded files are added to updated_playlist")
print("\nThis means 2 files likely had invalid URLs or download errors!")
print("=" * 80)

View File

@@ -0,0 +1,123 @@
#!/usr/bin/env python3
"""
Check video files for codec compatibility with ffpyplayer
"""
import os
import subprocess
import json
def check_video_codec(video_path):
"""Check video codec using ffprobe"""
try:
cmd = [
'ffprobe',
'-v', 'quiet',
'-print_format', 'json',
'-show_format',
'-show_streams',
video_path
]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
return None, "ffprobe failed"
data = json.loads(result.stdout)
video_streams = [s for s in data.get('streams', []) if s.get('codec_type') == 'video']
audio_streams = [s for s in data.get('streams', []) if s.get('codec_type') == 'audio']
if not video_streams:
return None, "No video stream found"
video_stream = video_streams[0]
info = {
'codec': video_stream.get('codec_name', 'unknown'),
'codec_long': video_stream.get('codec_long_name', 'unknown'),
'width': video_stream.get('width', 0),
'height': video_stream.get('height', 0),
'fps': eval(video_stream.get('r_frame_rate', '0/1')),
'duration': float(data.get('format', {}).get('duration', 0)),
'bitrate': int(data.get('format', {}).get('bit_rate', 0)),
'audio_codec': audio_streams[0].get('codec_name', 'none') if audio_streams else 'none',
'size': int(data.get('format', {}).get('size', 0))
}
return info, None
except FileNotFoundError:
return None, "ffprobe not installed (run: sudo apt-get install ffmpeg)"
except Exception as e:
return None, str(e)
def main():
base_dir = os.path.dirname(os.path.abspath(__file__))
media_dir = os.path.join(base_dir, 'media')
print("=" * 80)
print("VIDEO CODEC COMPATIBILITY CHECKER")
print("=" * 80)
# Supported codecs by ffpyplayer
supported_codecs = ['h264', 'h265', 'hevc', 'vp8', 'vp9', 'mpeg4']
# Find video files
video_extensions = ['.mp4', '.avi', '.mkv', '.mov', '.webm']
video_files = []
if os.path.exists(media_dir):
for filename in os.listdir(media_dir):
ext = os.path.splitext(filename)[1].lower()
if ext in video_extensions:
video_files.append(filename)
if not video_files:
print("\n✓ No video files found in media directory")
return
print(f"\nFound {len(video_files)} video file(s):\n")
for filename in video_files:
video_path = os.path.join(media_dir, filename)
print(f"📹 {filename}")
print(f" Path: {video_path}")
info, error = check_video_codec(video_path)
if error:
print(f" ❌ ERROR: {error}")
continue
# Display video info
print(f" Video Codec: {info['codec']} ({info['codec_long']})")
print(f" Resolution: {info['width']}x{info['height']}")
print(f" Frame Rate: {info['fps']:.2f} fps")
print(f" Duration: {info['duration']:.1f}s")
print(f" Bitrate: {info['bitrate'] / 1000:.0f} kbps")
print(f" Audio Codec: {info['audio_codec']}")
print(f" File Size: {info['size'] / (1024*1024):.2f} MB")
# Check compatibility
if info['codec'] in supported_codecs:
print(f" ✅ COMPATIBLE - Codec '{info['codec']}' is supported by ffpyplayer")
else:
print(f" ⚠️ WARNING - Codec '{info['codec']}' may not be supported")
print(f" Supported codecs: {', '.join(supported_codecs)}")
print(f" Consider re-encoding to H.264:")
print(f" ffmpeg -i \"{filename}\" -c:v libx264 -preset fast -crf 23 \"{os.path.splitext(filename)[0]}_h264.mp4\"")
# Performance warnings
if info['width'] > 1920 or info['height'] > 1080:
print(f" ⚠️ High resolution ({info['width']}x{info['height']}) may cause performance issues")
print(f" Consider downscaling to 1920x1080 or lower")
if info['bitrate'] > 5000000: # 5 Mbps
print(f" ⚠️ High bitrate ({info['bitrate'] / 1000000:.1f} Mbps) may cause playback issues")
print(f" Consider reducing bitrate to 2-4 Mbps")
print()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,167 @@
#!/usr/bin/env python3
"""
Diagnostic script to check why media files might be skipped
"""
import os
import json
# Paths
base_dir = os.path.dirname(os.path.abspath(__file__))
media_dir = os.path.join(base_dir, 'media')
playlists_dir = os.path.join(base_dir, 'playlists')
# Supported extensions
VIDEO_EXTENSIONS = ['.mp4', '.avi', '.mkv', '.mov', '.webm']
IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.bmp', '.gif']
SUPPORTED_EXTENSIONS = VIDEO_EXTENSIONS + IMAGE_EXTENSIONS
def check_playlist():
"""Check playlist for issues"""
print("=" * 80)
print("PLAYLIST DIAGNOSTIC TOOL")
print("=" * 80)
# Find latest playlist file
playlist_files = [f for f in os.listdir(playlists_dir)
if f.startswith('server_playlist_v') and f.endswith('.json')]
if not playlist_files:
print("\n❌ ERROR: No playlist files found!")
return
# Sort by version and get latest
versions = [(int(f.split('_v')[-1].split('.json')[0]), f) for f in playlist_files]
versions.sort(reverse=True)
latest_file = versions[0][1]
playlist_path = os.path.join(playlists_dir, latest_file)
print(f"\n📋 Latest Playlist: {latest_file}")
print(f" Path: {playlist_path}")
# Load playlist
try:
with open(playlist_path, 'r') as f:
data = json.load(f)
playlist = data.get('playlist', [])
version = data.get('version', 0)
print(f" Version: {version}")
print(f" Total items: {len(playlist)}")
except Exception as e:
print(f"\n❌ ERROR loading playlist: {e}")
return
# Check media directory
print(f"\n📁 Media Directory: {media_dir}")
if not os.path.exists(media_dir):
print(" ❌ ERROR: Media directory doesn't exist!")
return
media_files = os.listdir(media_dir)
print(f" Files found: {len(media_files)}")
for f in media_files:
print(f" - {f}")
# Check each playlist item
print("\n" + "=" * 80)
print("CHECKING PLAYLIST ITEMS")
print("=" * 80)
valid_count = 0
missing_count = 0
unsupported_count = 0
for idx, item in enumerate(playlist, 1):
file_name = item.get('file_name', '')
duration = item.get('duration', 0)
media_path = os.path.join(media_dir, file_name)
file_ext = os.path.splitext(file_name)[1].lower()
print(f"\n[{idx}/{len(playlist)}] {file_name}")
print(f" Duration: {duration}s")
# Check if file exists
if not os.path.exists(media_path):
print(f" ❌ STATUS: FILE NOT FOUND")
print(f" Expected path: {media_path}")
missing_count += 1
continue
# Check file size
file_size = os.path.getsize(media_path)
print(f" ✓ File exists ({file_size:,} bytes)")
# Check if supported type
if file_ext not in SUPPORTED_EXTENSIONS:
print(f" ❌ STATUS: UNSUPPORTED FILE TYPE '{file_ext}'")
print(f" Supported extensions: {', '.join(SUPPORTED_EXTENSIONS)}")
unsupported_count += 1
continue
# Check media type
if file_ext in VIDEO_EXTENSIONS:
media_type = "VIDEO"
elif file_ext in IMAGE_EXTENSIONS:
media_type = "IMAGE"
else:
media_type = "UNKNOWN"
print(f" ✓ Type: {media_type}")
print(f" ✓ Extension: {file_ext}")
print(f" ✓ STATUS: SHOULD PLAY OK")
valid_count += 1
# Summary
print("\n" + "=" * 80)
print("SUMMARY")
print("=" * 80)
print(f"Total items: {len(playlist)}")
print(f"✓ Valid: {valid_count}")
print(f"❌ Missing files: {missing_count}")
print(f"❌ Unsupported: {unsupported_count}")
if valid_count == len(playlist):
print("\n✅ All playlist items should play correctly!")
else:
print(f"\n⚠️ WARNING: {len(playlist) - valid_count} items may be skipped!")
# Additional checks
print("\n" + "=" * 80)
print("ADDITIONAL CHECKS")
print("=" * 80)
# Check for files in media dir not in playlist
playlist_files_set = {item.get('file_name', '') for item in playlist}
orphaned_files = [f for f in media_files if f not in playlist_files_set]
if orphaned_files:
print(f"\n⚠️ Files in media directory NOT in playlist:")
for f in orphaned_files:
print(f" - {f}")
else:
print("\n✓ All media files are in the playlist")
# Check for case sensitivity issues
print("\n🔍 Checking for case sensitivity issues...")
media_files_lower = {f.lower(): f for f in media_files}
case_issues = []
for item in playlist:
file_name = item.get('file_name', '')
if file_name.lower() in media_files_lower:
actual_name = media_files_lower[file_name.lower()]
if actual_name != file_name:
case_issues.append((file_name, actual_name))
if case_issues:
print("⚠️ Case sensitivity mismatches found:")
for playlist_name, actual_name in case_issues:
print(f" Playlist: {playlist_name}")
print(f" Actual: {actual_name}")
else:
print("✓ No case sensitivity issues found")
if __name__ == '__main__':
check_playlist()

View File

@@ -0,0 +1,54 @@
#!/usr/bin/env python3
"""Force playlist update to download all files."""
import json
import sys
import os
# Add src directory to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
from get_playlists_v2 import update_playlist_if_needed
# Load config
config_file = 'config/app_config.json'
with open(config_file, 'r') as f:
config = json.load(f)
print("=" * 80)
print("FORCING PLAYLIST UPDATE")
print("=" * 80)
playlist_dir = 'playlists'
media_dir = 'media'
print(f"\nConfiguration:")
print(f" Playlist dir: {playlist_dir}")
print(f" Media dir: {media_dir}")
print("\n" + "=" * 80)
print("Updating playlist...")
print("=" * 80 + "\n")
result = update_playlist_if_needed(config, playlist_dir, media_dir)
if result:
print("\n" + "=" * 80)
print("SUCCESS!")
print("=" * 80)
print(f"✓ Playlist updated to: {result}")
# Check media directory
import os
media_files = sorted([f for f in os.listdir(media_dir) if not f.startswith('.')])
print(f"\n✓ Media files downloaded ({len(media_files)}):")
for f in media_files:
size = os.path.getsize(os.path.join(media_dir, f))
print(f" - {f} ({size:,} bytes)")
else:
print("\n" + "=" * 80)
print("FAILED or already up to date")
print("=" * 80)
print("\n" + "=" * 80)

View File

@@ -0,0 +1,10 @@
{
"hostname": "tv-terasa",
"auth_code": "iiSyZDLWGyqNIxeRt54XYREgvAio11RwwU1_oJev6WI",
"player_id": 1,
"player_name": "TV-acasa 1",
"playlist_id": 1,
"orientation": "Landscape",
"authenticated": true,
"server_url": "http://digi-signage.moto-adv.com"
}

View File

@@ -0,0 +1,54 @@
{
"count": 5,
"player_id": 1,
"player_name": "TV-acasa 1",
"playlist": [
{
"description": null,
"duration": 15,
"file_name": "music.jpg",
"id": 1,
"position": 1,
"type": "image",
"url": "http://digi-signage.moto-adv.com/static/uploads/music.jpg"
},
{
"description": null,
"duration": 23,
"file_name": "130414-746934884.mp4",
"id": 2,
"position": 3,
"type": "video",
"url": "http://digi-signage.moto-adv.com/static/uploads/130414-746934884.mp4"
},
{
"description": null,
"duration": 15,
"file_name": "IMG_0386.jpeg",
"id": 4,
"position": 4,
"type": "image",
"url": "http://digi-signage.moto-adv.com/static/uploads/IMG_0386.jpeg"
},
{
"description": null,
"duration": 15,
"file_name": "AGC_20250704_204105932.jpg",
"id": 5,
"position": 5,
"type": "image",
"url": "http://digi-signage.moto-adv.com/static/uploads/AGC_20250704_204105932.jpg"
},
{
"description": null,
"duration": 15,
"file_name": "50194.jpg",
"id": 3,
"position": 6,
"type": "image",
"url": "http://digi-signage.moto-adv.com/static/uploads/50194.jpg"
}
],
"playlist_id": 1,
"playlist_version": 9
}

View File

@@ -0,0 +1,138 @@
#!/usr/bin/env python3
"""Test server connection and playlist fetch."""
import json
import sys
import os
# Add src directory to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
from player_auth import PlayerAuth
# Load config
config_file = 'config/app_config.json'
with open(config_file, 'r') as f:
config = json.load(f)
print("=" * 80)
print("SERVER CONNECTION TEST")
print("=" * 80)
server_ip = config.get("server_ip", "")
screen_name = config.get("screen_name", "")
quickconnect_key = config.get("quickconnect_key", "")
port = config.get("port", "")
print(f"\nConfiguration:")
print(f" Server: {server_ip}")
print(f" Port: {port}")
print(f" Screen Name: {screen_name}")
print(f" QuickConnect: {quickconnect_key}")
# Build server URL
if server_ip.startswith('http://') or server_ip.startswith('https://'):
server_url = server_ip
# If it has https but port 443 is specified, ensure port is included if non-standard
if not ':' in server_ip.replace('https://', '').replace('http://', ''):
if port and port != '443' and port != '80':
server_url = f"{server_ip}:{port}"
else:
# Use https for port 443, http for others
protocol = "https" if port == "443" else "http"
server_url = f"{protocol}://{server_ip}:{port}"
print(f"\nServer URL: {server_url}")
# Test authentication
print("\n" + "=" * 80)
print("1. TESTING AUTHENTICATION")
print("=" * 80)
auth = PlayerAuth('src/player_auth.json')
# Check if already authenticated
if auth.is_authenticated():
print("✓ Found existing authentication")
valid, message = auth.verify_auth()
if valid:
print(f"✓ Auth is valid: {message}")
else:
print(f"✗ Auth expired: {message}")
print("\nRe-authenticating...")
success, error = auth.authenticate(
server_url=server_url,
hostname=screen_name,
quickconnect_code=quickconnect_key
)
if success:
print(f"✓ Re-authentication successful!")
else:
print(f"✗ Re-authentication failed: {error}")
sys.exit(1)
else:
print("No existing authentication found. Authenticating...")
success, error = auth.authenticate(
server_url=server_url,
hostname=screen_name,
quickconnect_code=quickconnect_key
)
if success:
print(f"✓ Authentication successful!")
else:
print(f"✗ Authentication failed: {error}")
sys.exit(1)
# Test playlist fetch
print("\n" + "=" * 80)
print("2. TESTING PLAYLIST FETCH")
print("=" * 80)
playlist_data = auth.get_playlist()
if playlist_data:
print(f"✓ Playlist fetched successfully!")
print(f"\nPlaylist Version: {playlist_data.get('playlist_version', 'N/A')}")
print(f"Number of items: {len(playlist_data.get('playlist', []))}")
print("\n" + "-" * 80)
print("PLAYLIST ITEMS:")
print("-" * 80)
for idx, item in enumerate(playlist_data.get('playlist', []), 1):
print(f"\n{idx}. File: {item.get('file_name', 'N/A')}")
print(f" URL: {item.get('url', 'N/A')}")
print(f" Duration: {item.get('duration', 'N/A')}s")
# Check if URL is relative or absolute
url = item.get('url', '')
if url.startswith('http://') or url.startswith('https://'):
print(f" Type: Absolute URL")
else:
print(f" Type: Relative path (will fail to download!)")
# Save full response
with open('server_response_debug.json', 'w') as f:
json.dump(playlist_data, f, indent=2)
print(f"\n✓ Full response saved to: server_response_debug.json")
print("\n" + "=" * 80)
print("SUMMARY")
print("=" * 80)
print(f"Server has: {len(playlist_data.get('playlist', []))} files")
print(f"Local has: 3 files (from playlists/server_playlist_v8.json)")
if len(playlist_data.get('playlist', [])) > 3:
print(f"\n⚠️ PROBLEM: Server has {len(playlist_data.get('playlist', []))} files but only 3 were saved!")
print("\nMissing files are likely:")
local_files = ['music.jpg', '130414-746934884.mp4', 'IMG_0386.jpeg']
server_files = [item.get('file_name', '') for item in playlist_data.get('playlist', [])]
missing = [f for f in server_files if f not in local_files]
for f in missing:
print(f" - {f}")
else:
print("✗ Failed to fetch playlist")
sys.exit(1)
print("\n" + "=" * 80)

View File

@@ -0,0 +1,55 @@
#!/usr/bin/env python3
"""Direct API test to check server playlist."""
import requests
import json
# Try with the saved auth
auth_file = 'src/player_auth.json'
with open(auth_file, 'r') as f:
auth_data = json.load(f)
server_url = auth_data['server_url']
auth_code = auth_data['auth_code']
print("=" * 80)
print("DIRECT API TEST")
print("=" * 80)
print(f"Server: {server_url}")
print(f"Auth code: {auth_code[:20]}...")
print()
# Try to get playlist
try:
url = f"{server_url}/api/player/playlist"
headers = {
'Authorization': f'Bearer {auth_code}'
}
print(f"Fetching: {url}")
response = requests.get(url, headers=headers, timeout=10)
print(f"Status: {response.status_code}")
if response.status_code == 200:
data = response.json()
print(f"\nPlaylist version: {data.get('playlist_version', 'N/A')}")
print(f"Number of items: {len(data.get('playlist', []))}")
print("\nPlaylist items:")
for idx, item in enumerate(data.get('playlist', []), 1):
print(f"\n {idx}. {item.get('file_name', 'N/A')}")
print(f" URL: {item.get('url', 'N/A')}")
print(f" Duration: {item.get('duration', 'N/A')}s")
# Save full response
with open('server_playlist_full.json', 'w') as f:
json.dump(data, f, indent=2)
print(f"\nFull response saved to: server_playlist_full.json")
else:
print(f"Error: {response.text}")
except Exception as e:
print(f"Error: {e}")
print("\n" + "=" * 80)

View File

@@ -0,0 +1,82 @@
#!/usr/bin/env python3
"""
Test script to verify the enhanced logging without running the full GUI
"""
import os
import json
def simulate_playback_check():
"""Simulate the playback logic to see what would happen"""
base_dir = os.path.dirname(os.path.abspath(__file__))
media_dir = os.path.join(base_dir, 'media')
playlists_dir = os.path.join(base_dir, 'playlists')
# Supported extensions
VIDEO_EXTENSIONS = ['.mp4', '.avi', '.mkv', '.mov', '.webm']
IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.bmp', '.gif']
# Load playlist
playlist_file = os.path.join(playlists_dir, 'server_playlist_v8.json')
with open(playlist_file, 'r') as f:
data = json.load(f)
playlist = data.get('playlist', [])
print("=" * 80)
print("SIMULATING PLAYBACK SEQUENCE")
print("=" * 80)
for idx, media_item in enumerate(playlist):
file_name = media_item.get('file_name', '')
duration = media_item.get('duration', 10)
print(f"\n[STEP {idx + 1}] ===== Playing item {idx + 1}/{len(playlist)} =====")
print(f" File: {file_name}")
print(f" Duration: {duration}s")
# Construct path
media_path = os.path.join(media_dir, file_name)
print(f" Full path: {media_path}")
# Check existence
if not os.path.exists(media_path):
print(f" \u274c Media file not found: {media_path}")
print(f" ACTION: Skipping to next media...")
continue
file_size = os.path.getsize(media_path)
print(f" \u2713 File exists (size: {file_size:,} bytes)")
# Check extension
file_extension = os.path.splitext(file_name)[1].lower()
print(f" Extension: {file_extension}")
if file_extension in VIDEO_EXTENSIONS:
print(f" Media type: VIDEO")
print(f" ACTION: play_video('{media_path}', {duration})")
print(f" - Creating Video widget...")
print(f" - Adding to content area...")
print(f" - Scheduling next media in {duration}s")
print(f" \u2713 Media started successfully")
elif file_extension in IMAGE_EXTENSIONS:
print(f" Media type: IMAGE")
print(f" ACTION: play_image('{media_path}', {duration})")
print(f" - Creating AsyncImage widget...")
print(f" - Adding to content area...")
print(f" - Scheduling next media in {duration}s")
print(f" \u2713 Image displayed successfully")
else:
print(f" \u274c Unsupported media type: {file_extension}")
print(f" Supported: .mp4/.avi/.mkv/.mov/.webm/.jpg/.jpeg/.png/.bmp/.gif")
print(f" ACTION: Skipping to next media...")
continue
print(f"\n [After {duration}s] Transitioning to next media (was index {idx})")
print("\n" + "=" * 80)
print("END OF PLAYLIST - Would restart from beginning")
print("=" * 80)
if __name__ == '__main__':
simulate_playback_check()

View File

@@ -0,0 +1,45 @@
#!/usr/bin/env python3
"""Test script to check what playlist the server is actually returning."""
import json
import sys
import os
# Add src directory to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
from get_playlists_v2 import fetch_server_playlist
# Load config
config_file = 'config/app_config.json'
with open(config_file, 'r') as f:
config = json.load(f)
print("=" * 80)
print("TESTING SERVER PLAYLIST FETCH")
print("=" * 80)
# Fetch playlist from server
print("\n1. Fetching playlist from server...")
server_data = fetch_server_playlist(config)
print(f"\n2. Server Response:")
print(f" Version: {server_data.get('version', 'N/A')}")
print(f" Playlist items: {len(server_data.get('playlist', []))}")
print(f"\n3. Detailed Playlist Items:")
for idx, item in enumerate(server_data.get('playlist', []), 1):
print(f"\n Item {idx}:")
print(f" file_name: {item.get('file_name', 'N/A')}")
print(f" url: {item.get('url', 'N/A')}")
print(f" duration: {item.get('duration', 'N/A')}")
print("\n" + "=" * 80)
print(f"TOTAL: Server has {len(server_data.get('playlist', []))} files")
print("=" * 80)
# Save to file for inspection
output_file = 'server_response_debug.json'
with open(output_file, 'w') as f:
json.dump(server_data, f, indent=2)
print(f"\nFull server response saved to: {output_file}")