updated to send feedback

This commit is contained in:
2025-09-08 13:19:50 +03:00
parent bd4f101fcc
commit e2eecb9cf9
9 changed files with 2825 additions and 24 deletions

View File

@@ -3,8 +3,157 @@ import json
import requests
import bcrypt
import re
import datetime
from logging_config import Logger
def send_player_feedback(config, message, status="active", playlist_version=None, error_details=None):
"""
Send feedback to the server about player status.
Args:
config (dict): Configuration containing server details
message (str): Main feedback message
status (str): Player status - "active", "playing", "error", "restarting"
playlist_version (int, optional): Current playlist version being played
error_details (str, optional): Error details if status is "error"
Returns:
bool: True if feedback sent successfully, False otherwise
"""
try:
server = config.get("server_ip", "")
host = config.get("screen_name", "")
quick = config.get("quickconnect_key", "")
port = config.get("port", "")
# Construct server URL
ip_pattern = r'^\d+\.\d+\.\d+\.\d+$'
if re.match(ip_pattern, server):
feedback_url = f'http://{server}:{port}/api/player-feedback'
else:
feedback_url = f'http://{server}/api/player-feedback'
# Prepare feedback data
feedback_data = {
'player_name': host,
'quickconnect_code': quick,
'message': message,
'status': status,
'timestamp': datetime.datetime.now().isoformat(),
'playlist_version': playlist_version,
'error_details': error_details
}
Logger.info(f"Sending feedback to {feedback_url}: {feedback_data}")
# Send POST request
response = requests.post(feedback_url, json=feedback_data, timeout=10)
if response.status_code == 200:
Logger.info(f"Feedback sent successfully: {message}")
return True
else:
Logger.warning(f"Feedback failed with status {response.status_code}: {response.text}")
return False
except requests.exceptions.RequestException as e:
Logger.error(f"Failed to send feedback: {e}")
return False
except Exception as e:
Logger.error(f"Unexpected error sending feedback: {e}")
return False
def send_playlist_check_feedback(config, playlist_version=None):
"""
Send feedback when playlist is checked for updates.
Args:
config (dict): Configuration containing server details
playlist_version (int, optional): Current playlist version
Returns:
bool: True if feedback sent successfully, False otherwise
"""
player_name = config.get("screen_name", "unknown")
version_info = f"v{playlist_version}" if playlist_version else "unknown"
message = f"player {player_name}, is active, Playing {version_info}"
return send_player_feedback(
config=config,
message=message,
status="active",
playlist_version=playlist_version
)
def send_playlist_restart_feedback(config, playlist_version=None):
"""
Send feedback when playlist loop ends and restarts.
Args:
config (dict): Configuration containing server details
playlist_version (int, optional): Current playlist version
Returns:
bool: True if feedback sent successfully, False otherwise
"""
player_name = config.get("screen_name", "unknown")
version_info = f"v{playlist_version}" if playlist_version else "unknown"
message = f"player {player_name}, playlist loop completed, restarting {version_info}"
return send_player_feedback(
config=config,
message=message,
status="restarting",
playlist_version=playlist_version
)
def send_player_error_feedback(config, error_message, playlist_version=None):
"""
Send feedback when an error occurs in the player.
Args:
config (dict): Configuration containing server details
error_message (str): Description of the error
playlist_version (int, optional): Current playlist version
Returns:
bool: True if feedback sent successfully, False otherwise
"""
player_name = config.get("screen_name", "unknown")
message = f"player {player_name}, error occurred"
return send_player_feedback(
config=config,
message=message,
status="error",
playlist_version=playlist_version,
error_details=error_message
)
def send_playing_status_feedback(config, playlist_version=None, current_media=None):
"""
Send feedback about current playing status.
Args:
config (dict): Configuration containing server details
playlist_version (int, optional): Current playlist version
current_media (str, optional): Currently playing media file
Returns:
bool: True if feedback sent successfully, False otherwise
"""
player_name = config.get("screen_name", "unknown")
version_info = f"v{playlist_version}" if playlist_version else "unknown"
media_info = f" - {current_media}" if current_media else ""
message = f"player {player_name}, is active, Playing {version_info}{media_info}"
return send_player_feedback(
config=config,
message=message,
status="playing",
playlist_version=playlist_version
)
def is_playlist_up_to_date(local_playlist_path, config):
"""
Compare the version of the local playlist with the server playlist.
@@ -141,6 +290,7 @@ def update_playlist_if_needed(local_playlist_path, config, media_dir, playlist_d
"""
Fetch the server playlist once, compare versions, and update if needed.
Returns True if updated, False if already up to date.
Also sends feedback to server about playlist check.
"""
import json
server_data = fetch_server_playlist(config)
@@ -151,7 +301,12 @@ def update_playlist_if_needed(local_playlist_path, config, media_dir, playlist_d
with open(local_playlist_path, 'r') as f:
local_data = json.load(f)
local_version = local_data.get('version', 0)
Logger.info(f"Local playlist version: {local_version}, Server playlist version: {server_version}")
# Send feedback about playlist check
send_playlist_check_feedback(config, server_version if server_version > 0 else local_version)
if local_version != server_version:
if server_data and server_data.get('playlist'):
updated_playlist = download_media_files(server_data['playlist'], media_dir)
@@ -159,9 +314,19 @@ def update_playlist_if_needed(local_playlist_path, config, media_dir, playlist_d
save_playlist_with_version(server_data, playlist_dir)
# Delete old playlists and unreferenced media
delete_old_playlists_and_media(server_version, playlist_dir, media_dir)
# Send feedback about playlist update
player_name = config.get("screen_name", "unknown")
update_message = f"player {player_name}, playlist updated to v{server_version}"
send_player_feedback(config, update_message, "active", server_version)
return True
else:
Logger.warning("No playlist data fetched from server or playlist is empty.")
# Send error feedback
send_player_error_feedback(config, "No playlist data fetched from server or playlist is empty", local_version)
return False
else:
Logger.info("Local playlist is already up to date.")

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,7 @@ import tkinter as tk
import vlc
import subprocess
import sys
from get_playlists import send_playlist_restart_feedback, send_player_error_feedback, send_playing_status_feedback
CONFIG_PATH = os.path.join(os.path.dirname(__file__), 'main_data', 'app_config.txt')
PLAYLIST_DIR = os.path.join(os.path.dirname(__file__), 'static_data', 'playlist')
@@ -19,6 +20,10 @@ class SimpleTkPlayer:
self.is_transitioning = False # Flag to prevent rapid cycling
self.is_exiting = False # Flag to prevent operations during exit
# Load configuration for feedback
self.config = self.load_config()
self.playlist_version = self.get_current_playlist_version()
# Initialize all timer variables to None
self.hide_controls_timer = None
self.video_watchdog = None
@@ -36,6 +41,55 @@ class SimpleTkPlayer:
self.root.after(300, self.move_mouse_to_corner)
self.root.protocol('WM_DELETE_WINDOW', self.exit_app)
def load_config(self):
"""Load configuration for feedback functionality"""
try:
with open(CONFIG_PATH, 'r') as f:
config = json.load(f)
return config
except Exception as e:
print(f"[CONFIG] Error loading config: {e}")
return {}
def get_current_playlist_version(self):
"""Get the current playlist version from the loaded playlist data"""
try:
# Try to find the latest playlist file and extract version
if os.path.exists(PLAYLIST_DIR):
playlist_files = [f for f in os.listdir(PLAYLIST_DIR) if f.startswith('server_playlist_v') and f.endswith('.json')]
if playlist_files:
# Get the highest version number
versions = [int(f.split('_v')[-1].split('.json')[0]) for f in playlist_files]
return max(versions)
return None
except Exception as e:
print(f"[CONFIG] Error getting playlist version: {e}")
return None
def send_error_feedback(self, error_message):
"""Send error feedback to server"""
try:
if self.config:
send_player_error_feedback(self.config, error_message, self.playlist_version)
except Exception as e:
print(f"[FEEDBACK] Error sending error feedback: {e}")
def send_playing_feedback(self, current_media=None):
"""Send playing status feedback to server"""
try:
if self.config:
send_playing_status_feedback(self.config, self.playlist_version, current_media)
except Exception as e:
print(f"[FEEDBACK] Error sending playing feedback: {e}")
def send_restart_feedback(self):
"""Send playlist restart feedback to server"""
try:
if self.config:
send_playlist_restart_feedback(self.config, self.playlist_version)
except Exception as e:
print(f"[FEEDBACK] Error sending restart feedback: {e}")
def ensure_fullscreen(self):
self.root.attributes('-fullscreen', True)
self.root.update_idletasks()
@@ -247,34 +301,51 @@ class SimpleTkPlayer:
check_end()
except Exception as e:
print(f"[VLC] Error playing video {file_path}: {e}")
self.send_error_feedback(f"Video playback error: {str(e)} - {os.path.basename(file_path)}")
if on_end:
on_end()
def show_current_media(self):
self.root.attributes('-fullscreen', True)
self.root.update_idletasks()
if not self.playlist:
print("[PLAYER] Playlist is empty. No media to show.")
self.label.config(text="No media available", fg='white', font=('Arial', 32))
# Try to reload playlist after 10 seconds
self.root.after(10000, self.reload_playlist_and_continue)
return
media = self.playlist[self.current_index]
file_path = os.path.join(MEDIA_DATA_PATH, media['file_name'])
ext = file_path.lower()
duration = media.get('duration', None)
if not os.path.isfile(file_path):
print(f"[PLAYER] File missing: {file_path}. Skipping to next.")
self.next_media()
return
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')):
# 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)
else:
print(f"[PLAYER] Unsupported file type: {media['file_name']}")
self.label.config(text=f"Unsupported: {media['file_name']}", fg='yellow')
try:
self.root.attributes('-fullscreen', True)
self.root.update_idletasks()
if not self.playlist:
print("[PLAYER] Playlist is empty. No media to show.")
self.label.config(text="No media available", fg='white', font=('Arial', 32))
# Send error feedback
self.send_error_feedback("Playlist is empty, no media to show")
# Try to reload playlist after 10 seconds
self.root.after(10000, self.reload_playlist_and_continue)
return
media = self.playlist[self.current_index]
file_path = os.path.join(MEDIA_DATA_PATH, media['file_name'])
ext = file_path.lower()
duration = media.get('duration', None)
# Send playing status feedback
self.send_playing_feedback(media['file_name'])
if not os.path.isfile(file_path):
print(f"[PLAYER] File missing: {file_path}. Skipping to next.")
self.send_error_feedback(f"Media file missing: {media['file_name']}")
self.next_media()
return
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')):
# 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)
else:
print(f"[PLAYER] Unsupported file type: {media['file_name']}")
self.label.config(text=f"Unsupported: {media['file_name']}", fg='yellow')
self.send_error_feedback(f"Unsupported file type: {media['file_name']}")
self.root.after(2000, self.next_media)
except Exception as e:
print(f"[PLAYER] Error in show_current_media: {e}")
self.send_error_feedback(f"Error showing media: {str(e)}")
# Try to continue with next media after error
self.root.after(2000, self.next_media)
def show_image_via_pil(self, file_path, duration, on_end=None):
@@ -331,6 +402,7 @@ class SimpleTkPlayer:
except Exception as e:
print(f"[PLAYER] PIL image display failed: {e}. Skipping.")
self.send_error_feedback(f"Image display error: {str(e)} - {os.path.basename(file_path)}")
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)
@@ -440,9 +512,18 @@ class SimpleTkPlayer:
return
self.is_transitioning = True
# Check if we're about to restart the playlist (loop back to beginning)
was_at_end = self.current_index == len(self.playlist) - 1
self.current_index = (self.current_index + 1) % len(self.playlist)
print(f"[PLAYER] Moving to next media: index {self.current_index}")
# Send feedback if playlist restarted
if was_at_end and self.current_index == 0:
print("[FEEDBACK] Playlist loop completed, sending restart feedback")
self.send_restart_feedback()
# Clear any existing timers safely
timer_names = ['video_watchdog', 'image_watchdog', 'image_timer']
for timer_name in timer_names:

23
test_feedback.py Executable file
View File

@@ -0,0 +1,23 @@
#!/usr/bin/env python3
import os
import sys
import json
# Add the signage_player directory to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'signage_player'))
from get_playlists import send_playlist_check_feedback
def load_config():
config_path = os.path.join(os.path.dirname(__file__), 'signage_player', 'main_data', 'app_config.txt')
with open(config_path, 'r') as f:
return json.load(f)
def test_feedback():
print("Testing feedback system...")
config = load_config()
result = send_playlist_check_feedback(config, 5)
print(f"Feedback test result: {result}")
if __name__ == "__main__":
test_feedback()