updated player
This commit is contained in:
@@ -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
BIN
config/resources/intro1.mp4
Normal file
Binary file not shown.
51
src/main.py
51
src/main.py
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
211
working_files/DEBUGGING_MEDIA_SKIPS.md
Normal file
211
working_files/DEBUGGING_MEDIA_SKIPS.md
Normal 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!
|
||||
182
working_files/INVESTIGATION_RESULTS.md
Normal file
182
working_files/INVESTIGATION_RESULTS.md
Normal 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!
|
||||
35
working_files/analyze_playlist.py
Normal file
35
working_files/analyze_playlist.py
Normal 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)
|
||||
123
working_files/check_video_codecs.py
Normal file
123
working_files/check_video_codecs.py
Normal 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()
|
||||
167
working_files/diagnose_playlist.py
Normal file
167
working_files/diagnose_playlist.py
Normal 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()
|
||||
54
working_files/force_update.py
Normal file
54
working_files/force_update.py
Normal 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)
|
||||
10
working_files/player_auth.json
Normal file
10
working_files/player_auth.json
Normal 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"
|
||||
}
|
||||
54
working_files/server_response_debug.json
Normal file
54
working_files/server_response_debug.json
Normal 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
|
||||
}
|
||||
138
working_files/test_connection.py
Normal file
138
working_files/test_connection.py
Normal 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)
|
||||
55
working_files/test_direct_api.py
Normal file
55
working_files/test_direct_api.py
Normal 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)
|
||||
82
working_files/test_playback_logging.py
Normal file
82
working_files/test_playback_logging.py
Normal 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()
|
||||
45
working_files/test_server_playlist.py
Normal file
45
working_files/test_server_playlist.py
Normal 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}")
|
||||
Reference in New Issue
Block a user