Fix player freezing and rapid cycling issues

- Switch from VLC to PIL for image display to avoid VLC display errors
- Add transition protection to prevent rapid media cycling
- Remove conflicting next_media_loop calls
- Improve error handling and logging for better debugging
- Fix VLC configuration for better Raspberry Pi compatibility
This commit is contained in:
2025-09-04 15:52:13 +03:00
parent 2a564f5e84
commit d2a996feb9
5 changed files with 584 additions and 11 deletions

View File

@@ -16,6 +16,7 @@ class SimpleTkPlayer:
self.current_index = 0
self.paused = False
self.pause_timer = None
self.is_transitioning = False # Flag to prevent rapid cycling
self.label = tk.Label(root, bg='black')
self.label.pack(fill=tk.BOTH, expand=True)
self.create_controls()
@@ -162,7 +163,6 @@ class SimpleTkPlayer:
def after_intro(self):
self.show_current_media()
self.root.after(100, self.next_media_loop)
def show_video(self, file_path, on_end=None, duration=None):
try:
@@ -176,10 +176,20 @@ class SimpleTkPlayer:
self.video_canvas.pack(fill=tk.BOTH, expand=True)
self.root.attributes('-fullscreen', True)
self.root.update_idletasks()
self.vlc_instance = vlc.Instance('--vout=x11')
# Configure VLC for Raspberry Pi with better compatibility options
vlc_args = [
'--no-xlib',
'--vout=gl',
'--aout=alsa',
'--intf=dummy',
'--quiet',
'--no-video-title-show',
'--no-stats',
'--disable-screensaver'
]
self.vlc_instance = vlc.Instance(vlc_args)
self.vlc_player = self.vlc_instance.media_player_new()
self.vlc_player.set_mrl(file_path)
self.vlc_player.set_fullscreen(True)
self.vlc_player.set_xwindow(self.video_canvas.winfo_id())
self.vlc_player.play()
# Watchdog timer: fallback if video doesn't end
@@ -241,12 +251,73 @@ class SimpleTkPlayer:
if ext.endswith(('.mp4', '.avi', '.mov', '.mkv')):
self.show_video(file_path, on_end=self.next_media, duration=duration)
elif ext.endswith(('.jpg', '.jpeg', '.png', '.bmp', '.gif')):
self.show_image_via_vlc(file_path, duration if duration is not None else 10, on_end=self.next_media)
# Use PIL for images instead of VLC to avoid display issues
self.show_image_via_pil(file_path, duration if duration is not None else 10, on_end=self.next_media)
try:
self.show_image_via_vlc(file_path, duration if duration is not None else 10, on_end=self.next_media)
except Exception as e:
print(f"[PLAYER] VLC image display failed: {e}. Trying PIL fallback.")
self.show_image_via_pil(file_path, duration if duration is not None else 10, on_end=self.next_media)
else:
print(f"[PLAYER] Unsupported file type: {media['file_name']}")
self.label.config(text=f"Unsupported: {media['file_name']}", fg='yellow')
self.root.after(2000, self.next_media)
def show_image_via_pil(self, file_path, duration, on_end=None):
"""Fallback image display using PIL and Tkinter"""
try:
print(f"[PLAYER] Displaying image via PIL: {file_path}")
from PIL import Image, ImageTk
# Stop any VLC player that might be running
if hasattr(self, 'vlc_player') and self.vlc_player:
self.vlc_player.stop()
# Hide video canvas if exists and show label
if hasattr(self, 'video_canvas'):
self.video_canvas.pack_forget()
self.label.pack(fill=tk.BOTH, expand=True)
# Load and resize image
img = Image.open(file_path)
screen_width = self.root.winfo_screenwidth()
screen_height = self.root.winfo_screenheight()
# Calculate scaling to fit screen while maintaining aspect ratio
img_ratio = img.width / img.height
screen_ratio = screen_width / screen_height
if img_ratio > screen_ratio:
# Image is wider than screen ratio
new_width = screen_width
new_height = int(screen_width / img_ratio)
else:
# Image is taller than screen ratio
new_height = screen_height
new_width = int(screen_height * img_ratio)
img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
self.photo = ImageTk.PhotoImage(img)
self.label.config(image=self.photo, text="", bg='black')
print(f"[PLAYER] Image displayed successfully, will show for {duration} seconds")
# Schedule next media after duration
def finish_image():
print(f"[PLAYER] Image duration completed for {os.path.basename(file_path)}")
self.label.config(image="", text="")
if hasattr(self, 'photo'):
del self.photo # Free memory
if on_end:
on_end()
self.image_timer = self.root.after(int(duration * 1000), finish_image)
except Exception as e:
print(f"[PLAYER] PIL image display failed: {e}. Skipping.")
self.label.config(text=f"Image Error: {os.path.basename(file_path)}", fg='red', font=('Arial', 24))
self.root.after(2000, lambda: on_end() if on_end else None)
def reload_playlist_and_continue(self):
print("[PLAYER] Attempting to reload playlist...")
new_playlist = load_latest_playlist()
@@ -259,9 +330,36 @@ class SimpleTkPlayer:
print("[PLAYER] Still no playlist. Will retry.")
self.root.after(10000, self.reload_playlist_and_continue)
def validate_image_file(self, file_path):
"""Validate if image file is not corrupted"""
try:
import imghdr
# Check if file has valid image header
img_type = imghdr.what(file_path)
if img_type is None:
return False
# Additional check for file size (empty files)
file_size = os.path.getsize(file_path)
if file_size < 100: # Too small to be a valid image
return False
return True
except Exception as e:
print(f"[PLAYER] Error validating image {file_path}: {e}")
return False
def show_image_via_vlc(self, file_path, duration, on_end=None):
try:
print(f"[PLAYER] Attempting to show image: {file_path}")
# Validate file before attempting to display
if not self.validate_image_file(file_path):
print(f"[PLAYER] Invalid or corrupted image file: {file_path}. Skipping.")
if on_end:
on_end()
return
if hasattr(self, 'vlc_player') and self.vlc_player:
self.vlc_player.stop()
if not hasattr(self, 'video_canvas'):
@@ -271,10 +369,20 @@ class SimpleTkPlayer:
self.video_canvas.pack(fill=tk.BOTH, expand=True)
self.root.attributes('-fullscreen', True)
self.root.update_idletasks()
self.vlc_instance = vlc.Instance('--vout=x11')
# Configure VLC for Raspberry Pi with better compatibility options
vlc_args = [
'--no-xlib',
'--vout=gl',
'--aout=dummy',
'--intf=dummy',
'--quiet',
'--no-video-title-show',
'--no-stats',
'--disable-screensaver'
]
self.vlc_instance = vlc.Instance(vlc_args)
self.vlc_player = self.vlc_instance.media_player_new()
self.vlc_player.set_mrl(file_path)
self.vlc_player.set_fullscreen(True)
self.vlc_player.set_xwindow(self.video_canvas.winfo_id())
self.vlc_player.play()
# Watchdog timer: fallback if image doesn't advance
@@ -301,14 +409,29 @@ class SimpleTkPlayer:
on_end()
def next_media(self):
if self.is_transitioning:
print("[PLAYER] Already transitioning, ignoring next_media call")
return
self.is_transitioning = True
self.current_index = (self.current_index + 1) % len(self.playlist)
print(f"[PLAYER] Moving to next media: index {self.current_index}")
# Clear any existing timers
if hasattr(self, 'video_watchdog'):
self.root.after_cancel(self.video_watchdog)
if hasattr(self, 'image_watchdog'):
self.root.after_cancel(self.image_watchdog)
if hasattr(self, 'image_timer'):
self.root.after_cancel(self.image_timer)
self.show_current_media()
# Reset transition flag after a brief delay
self.root.after(500, lambda: setattr(self, 'is_transitioning', False))
def next_media_loop(self):
if not self.playlist or self.paused:
self.root.after(1000, self.next_media_loop)
return
self.show_current_media()
# This function is no longer needed as media transitions are handled by the callback system
pass
def exit_app(self):
# Signal all threads and flags to stop