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 requests
import bcrypt import bcrypt
import re import re
import datetime
from logging_config import Logger 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): def is_playlist_up_to_date(local_playlist_path, config):
""" """
Compare the version of the local playlist with the server playlist. 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. Fetch the server playlist once, compare versions, and update if needed.
Returns True if updated, False if already up to date. Returns True if updated, False if already up to date.
Also sends feedback to server about playlist check.
""" """
import json import json
server_data = fetch_server_playlist(config) 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: with open(local_playlist_path, 'r') as f:
local_data = json.load(f) local_data = json.load(f)
local_version = local_data.get('version', 0) local_version = local_data.get('version', 0)
Logger.info(f"Local playlist version: {local_version}, Server playlist version: {server_version}") 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 local_version != server_version:
if server_data and server_data.get('playlist'): if server_data and server_data.get('playlist'):
updated_playlist = download_media_files(server_data['playlist'], media_dir) 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) save_playlist_with_version(server_data, playlist_dir)
# Delete old playlists and unreferenced media # Delete old playlists and unreferenced media
delete_old_playlists_and_media(server_version, playlist_dir, media_dir) 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 return True
else: else:
Logger.warning("No playlist data fetched from server or playlist is empty.") 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 return False
else: else:
Logger.info("Local playlist is already up to date.") 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 vlc
import subprocess import subprocess
import sys 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') 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') 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_transitioning = False # Flag to prevent rapid cycling
self.is_exiting = False # Flag to prevent operations during exit 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 # Initialize all timer variables to None
self.hide_controls_timer = None self.hide_controls_timer = None
self.video_watchdog = None self.video_watchdog = None
@@ -36,6 +41,55 @@ class SimpleTkPlayer:
self.root.after(300, self.move_mouse_to_corner) self.root.after(300, self.move_mouse_to_corner)
self.root.protocol('WM_DELETE_WINDOW', self.exit_app) 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): def ensure_fullscreen(self):
self.root.attributes('-fullscreen', True) self.root.attributes('-fullscreen', True)
self.root.update_idletasks() self.root.update_idletasks()
@@ -247,26 +301,37 @@ class SimpleTkPlayer:
check_end() check_end()
except Exception as e: except Exception as e:
print(f"[VLC] Error playing video {file_path}: {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: if on_end:
on_end() on_end()
def show_current_media(self): def show_current_media(self):
try:
self.root.attributes('-fullscreen', True) self.root.attributes('-fullscreen', True)
self.root.update_idletasks() self.root.update_idletasks()
if not self.playlist: if not self.playlist:
print("[PLAYER] Playlist is empty. No media to show.") print("[PLAYER] Playlist is empty. No media to show.")
self.label.config(text="No media available", fg='white', font=('Arial', 32)) 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 # Try to reload playlist after 10 seconds
self.root.after(10000, self.reload_playlist_and_continue) self.root.after(10000, self.reload_playlist_and_continue)
return return
media = self.playlist[self.current_index] media = self.playlist[self.current_index]
file_path = os.path.join(MEDIA_DATA_PATH, media['file_name']) file_path = os.path.join(MEDIA_DATA_PATH, media['file_name'])
ext = file_path.lower() ext = file_path.lower()
duration = media.get('duration', None) duration = media.get('duration', None)
# Send playing status feedback
self.send_playing_feedback(media['file_name'])
if not os.path.isfile(file_path): if not os.path.isfile(file_path):
print(f"[PLAYER] File missing: {file_path}. Skipping to next.") print(f"[PLAYER] File missing: {file_path}. Skipping to next.")
self.send_error_feedback(f"Media file missing: {media['file_name']}")
self.next_media() self.next_media()
return return
if ext.endswith(('.mp4', '.avi', '.mov', '.mkv')): if ext.endswith(('.mp4', '.avi', '.mov', '.mkv')):
self.show_video(file_path, on_end=self.next_media, duration=duration) self.show_video(file_path, on_end=self.next_media, duration=duration)
elif ext.endswith(('.jpg', '.jpeg', '.png', '.bmp', '.gif')): elif ext.endswith(('.jpg', '.jpeg', '.png', '.bmp', '.gif')):
@@ -275,6 +340,12 @@ class SimpleTkPlayer:
else: else:
print(f"[PLAYER] Unsupported file type: {media['file_name']}") print(f"[PLAYER] Unsupported file type: {media['file_name']}")
self.label.config(text=f"Unsupported: {media['file_name']}", fg='yellow') 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) self.root.after(2000, self.next_media)
def show_image_via_pil(self, file_path, duration, on_end=None): def show_image_via_pil(self, file_path, duration, on_end=None):
@@ -331,6 +402,7 @@ class SimpleTkPlayer:
except Exception as e: except Exception as e:
print(f"[PLAYER] PIL image display failed: {e}. Skipping.") 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.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) self.root.after(2000, lambda: on_end() if on_end else None)
@@ -440,9 +512,18 @@ class SimpleTkPlayer:
return return
self.is_transitioning = True 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) self.current_index = (self.current_index + 1) % len(self.playlist)
print(f"[PLAYER] Moving to next media: index {self.current_index}") 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 # Clear any existing timers safely
timer_names = ['video_watchdog', 'image_watchdog', 'image_timer'] timer_names = ['video_watchdog', 'image_watchdog', 'image_timer']
for timer_name in timer_names: 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()