From ba6785528d3f02dc7c027ec11dcfa8c4be965c8c Mon Sep 17 00:00:00 2001 From: RobbStarkAustria Date: Sun, 29 Mar 2026 15:04:09 +0200 Subject: [PATCH] remove bug for playing videos --- src/display_manager.py | 66 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/src/display_manager.py b/src/display_manager.py index f7e9f87..bcf2bfd 100644 --- a/src/display_manager.py +++ b/src/display_manager.py @@ -21,7 +21,7 @@ from datetime import datetime, timezone from pathlib import Path from logging.handlers import RotatingFileHandler from typing import Optional, Dict, List, IO -from urllib.parse import urlparse +from urllib.parse import urlparse, urlsplit, urlunsplit, quote import requests import psutil import json as _json @@ -450,9 +450,25 @@ class DisplayProcess: # python-vlc MediaPlayer: is_playing() returns 1 while playing if hasattr(self.player, 'is_playing'): try: - return bool(self.player.is_playing()) - except Exception: + if bool(self.player.is_playing()): + return True + + # A plain is_playing()==0 can happen briefly during opening/ + # buffering on HTTP streams. Treat those states as still alive. + state = None + if hasattr(self.player, 'get_state'): + state = self.player.get_state() + if state is not None: + import vlc as _vlc + return state in ( + _vlc.State.Opening, + _vlc.State.Buffering, + _vlc.State.Playing, + _vlc.State.Paused, + ) return False + except Exception: + pass # MediaListPlayer may not have is_playing - try to inspect media player state try: state = None @@ -1144,6 +1160,7 @@ class DisplayManager: # Normalize file-server host alias (e.g., http://server:8000/...) -> configured FILE_SERVER video_url = self._resolve_file_url(video_url) + video_url = self._sanitize_media_url(video_url) logging.info(f"Starting video: {video_url}") # Prefer using python-vlc (libvlc) for finer control try: @@ -1355,6 +1372,34 @@ class DisplayManager: except Exception as e: logging.debug(f"Error resolving file url '{url}': {e}") return url + + def _sanitize_media_url(self, url: str) -> str: + """Percent-encode media URLs so VLC/ffmpeg handle spaces/unicode reliably. + + Some event payloads include raw spaces or non-ASCII characters in URL paths. + Requests usually tolerates that, but VLC/ffmpeg can fail to open those URLs. + """ + try: + if not url: + return url + + parts = urlsplit(url) + if parts.scheme not in ('http', 'https'): + return url + + safe_path = quote(parts.path or '/', safe="/%:@!$&'()*+,;=-._~") + safe_query = quote(parts.query or '', safe="=&%:@!$'()*+,;/?-._~") + sanitized = urlunsplit((parts.scheme, parts.netloc, safe_path, safe_query, parts.fragment)) + + if sanitized != url: + logging.info("Sanitized media URL for VLC compatibility") + logging.debug(f"Media URL before: {url}") + logging.debug(f"Media URL after: {sanitized}") + + return sanitized + except Exception as e: + logging.debug(f"Could not sanitize media URL '{url}': {e}") + return url def start_webpage(self, event: Dict, autoscroll_enabled: bool = False) -> Optional[DisplayProcess]: """Start webpage display in kiosk mode""" @@ -1654,12 +1699,26 @@ class DisplayManager: # Same event - check if process is still running if not self.current_process.is_running(): exit_code = None + player_state = None if getattr(self.current_process, 'process', None): try: exit_code = self.current_process.process.returncode except Exception: exit_code = None + elif getattr(self.current_process, 'player', None): + try: + player = self.current_process.player + if hasattr(player, 'get_state'): + player_state = player.get_state() + elif hasattr(player, 'get_media_player'): + mp = player.get_media_player() + if mp and hasattr(mp, 'get_state'): + player_state = mp.get_state() + except Exception as e: + logging.debug(f"Could not read VLC player state: {e}") logging.warning(f"Display process exited (exit code: {exit_code})") + if player_state is not None: + logging.warning(f"VLC player state at exit detection: {player_state}") # Try to surface last lines of the related log file, if any if getattr(self.current_process, 'log_path', None): try: @@ -2122,6 +2181,7 @@ class DisplayManager: video = self.current_event_data.get('video', {}) if isinstance(self.current_event_data, dict) else {} source_url = video.get('url') if isinstance(video, dict) else None source_url = self._resolve_file_url(source_url) if source_url else None + source_url = self._sanitize_media_url(source_url) if source_url else None if not source_url: return False