diff --git a/src/Resurse/app_config.txt b/src/Resurse/app_config.txt index 35d7a31..1191f88 100644 --- a/src/Resurse/app_config.txt +++ b/src/Resurse/app_config.txt @@ -6,5 +6,5 @@ "port": "5000", "screen_w": "1920", "screen_h": "1080", - "playlist_version": 5 + "playlist_version": 6 } \ No newline at end of file diff --git a/src/Resurse/log.txt b/src/Resurse/log.txt index 1924971..0ada227 100644 --- a/src/Resurse/log.txt +++ b/src/Resurse/log.txt @@ -1,27 +1,6 @@ -2025-06-24 14:10:03 - STARTED: edit_pencil.png -2025-06-24 14:10:22 - STARTED: delete.png -2025-06-24 14:16:01 - STARTED: edit_pencil.png -2025-06-24 14:16:21 - STARTED: delete.png -2025-06-24 14:17:04 - STARTED: edit_pencil.png -2025-06-24 14:17:24 - STARTED: delete.png -2025-06-24 14:17:44 - STARTED: edit_pencil.png -2025-06-24 14:17:59 - STARTED: edit_pencil.png -2025-06-24 14:18:14 - STARTED: edit_pencil.png -2025-06-24 14:19:22 - STARTED: edit_pencil.png -2025-06-24 14:19:37 - STARTED: edit_pencil.png -2025-06-24 14:19:52 - STARTED: edit_pencil.png -2025-06-24 14:20:08 - STARTED: edit_pencil.png -2025-06-24 14:44:17 - STARTED: edit_pencil.png -2025-06-24 14:44:32 - STARTED: edit_pencil.png -2025-06-24 14:44:47 - STARTED: edit_pencil.png -2025-06-24 14:48:16 - STARTED: 20250605_09h44m12s_grim.png -2025-06-24 15:23:13 - STARTED: 20250605_09h44m12s_grim.png -2025-06-24 15:23:33 - STARTED: 20250605_09h44m12s_grim.png -2025-06-24 15:27:25 - STARTED: 20250605_09h44m12s_grim.png -2025-06-24 15:27:45 - STARTED: 20250605_09h44m12s_grim.png -2025-06-24 15:29:48 - STARTED: 20250605_09h44m12s_grim.png -2025-06-24 15:30:08 - STARTED: 20250605_09h44m12s_grim.png -2025-06-24 15:30:09 - STARTED: 20250605_09h44m12s_grim.png -2025-06-24 15:39:48 - STARTED: 20250605_09h44m12s_grim.png -2025-06-24 15:39:55 - STARTED: 20250605_09h44m12s_grim.png -2025-06-24 15:40:16 - STARTED: 123.jpeg +2025-06-24 16:02:45 - STARTED: 123.jpeg +2025-06-24 16:04:27 - STARTED: 123.jpeg +2025-06-24 16:05:01 - STARTED: 123.jpeg +2025-06-24 16:08:00 - STARTED: 123.jpeg +2025-06-24 16:09:01 - STARTED: 123.jpeg +2025-06-24 16:09:31 - STARTED: 20250417_09h25m37s_grim.png diff --git a/src/__pycache__/logging_config.cpython-311.pyc b/src/__pycache__/logging_config.cpython-311.pyc new file mode 100644 index 0000000..4826be5 Binary files /dev/null and b/src/__pycache__/logging_config.cpython-311.pyc differ diff --git a/src/__pycache__/python_functions.cpython-311.pyc b/src/__pycache__/python_functions.cpython-311.pyc index da6b773..5ae29a8 100644 Binary files a/src/__pycache__/python_functions.cpython-311.pyc and b/src/__pycache__/python_functions.cpython-311.pyc differ diff --git a/src/kv/media_player.kv b/src/kv/media_player.kv index 0396cf3..df57845 100644 --- a/src/kv/media_player.kv +++ b/src/kv/media_player.kv @@ -179,6 +179,20 @@ text: "Server connection status will appear here." size_hint_x: 0.7 + # Multi-row label box for log messages + BoxLayout: + orientation: 'vertical' + size_hint_y: None + height: 200 # Adjust height as needed + + Label: + id: log_messages_label + text: "Log messages will appear here." + font_size: 14 + halign: 'left' + valign: 'top' + text_size: self.size + # Buttons in the lower part of the screen BoxLayout: size_hint_y: 0.4 # Allocate 40% of the screen height for buttons diff --git a/src/logging_config.py b/src/logging_config.py new file mode 100644 index 0000000..1024fbb --- /dev/null +++ b/src/logging_config.py @@ -0,0 +1,18 @@ +import logging +import os + +# Path to the log file +LOG_FILE_PATH = os.path.join(os.path.dirname(__file__), 'Resurse', 'log.txt') + +# Configure the logger to write to the log file +logging.basicConfig( + level=logging.INFO, # Set the logging level to INFO + format='[%(levelname)s] [%(name)s] %(message)s', # Format for log messages + handlers=[ + logging.FileHandler(LOG_FILE_PATH, mode='a'), # Append logs to the file + logging.StreamHandler() # Also log to the console + ] +) + +# Create a shared logger instance +Logger = logging.getLogger('SignageApp') \ No newline at end of file diff --git a/src/media_player.py b/src/media_player.py index d10ca09..a92483d 100644 --- a/src/media_player.py +++ b/src/media_player.py @@ -9,7 +9,7 @@ from kivy.clock import Clock # Import Clock for scheduling tasks from kivy.core.window import Window # Import Window for handling window events from kivy.uix.video import Video # Import Video widget for video playback from kivy.uix.image import Image # Import Image widget for displaying images -from kivy.logger import Logger # Import Logger for logging messages +from kivy.logger import Logger, LoggerHistory # Import Logger for logging messages from kivy.lang import Builder # Import Builder for loading KV files from kivy.animation import Animation # Import Animation for fade effects from kivy.uix.popup import Popup @@ -22,9 +22,11 @@ import json # Import json for handling JSON data import datetime # Import datetime for timestamping logs import subprocess import requests +import logging # Import functions from python_functions.py from python_functions import load_local_playlist, download_media_files, clean_unused_files,save_local_playlist, fetch_server_playlist +from logging_config import Logger # Import the shared logger # Load the KV file for UI layout Builder.load_file('kv/media_player.kv') @@ -32,6 +34,7 @@ Builder.load_file('kv/media_player.kv') # Path to the configuration file CONFIG_FILE = './Resurse/app_config.txt' + class MediaPlayer(Screen): # Main screen for media playback. @@ -443,6 +446,27 @@ class SettingsScreen(Screen): "playlist_version": 0 # Default playlist version } + def load_log_messages(self): + """Load the last 10 log messages and update the label.""" + log_file_path = os.path.join(os.path.dirname(__file__), 'Resurse', 'log.txt') + if not os.path.exists(log_file_path): + self.ids.log_messages_label.text = "No log messages available." + Logger.warning("SettingsScreen: Log file not found.") + return + + try: + with open(log_file_path, 'r') as log_file: + lines = log_file.readlines() + # Get the last 10 log messages + last_messages = lines[-10:] if len(lines) > 10 else lines + # Format the messages for display + formatted_messages = "\n".join([line.strip() for line in last_messages]) + self.ids.log_messages_label.text = formatted_messages + Logger.info("SettingsScreen: Log messages loaded successfully.") + except Exception as e: + self.ids.log_messages_label.text = "Failed to load log messages." + Logger.error(f"SettingsScreen: Error loading log messages: {e}") + def save_config(self): """Save the configuration to the config file.""" self.config_data["screen_orientation"] = self.ids.orientation_input.text @@ -470,6 +494,9 @@ class SettingsScreen(Screen): self.ids.screen_width_input.text = self.config_data.get("screen_w", "") self.ids.screen_height_input.text = self.config_data.get("screen_h", "") + # Load the last 10 log messages + self.load_log_messages() + def show_exit_popup(self): # Create the popup layout layout = BoxLayout(orientation='vertical', spacing=10, padding=10) @@ -559,16 +586,7 @@ class MediaPlayerApp(App): """Switch to the SettingsScreen.""" self.root.current = 'settings' -def convert_video_to_mp4(input_path, output_path): - """Convert a video to H.264 MP4 format.""" - try: - subprocess.run( - ['ffmpeg', '-i', input_path, '-vcodec', 'libx264', '-acodec', 'aac', '-strict', 'experimental', output_path], - check=True - ) - Logger.info(f"Converted video: {input_path} to {output_path}") - except subprocess.CalledProcessError as e: - Logger.error(f"Failed to convert video: {input_path}. Error: {e}") + if __name__ == '__main__': MediaPlayerApp().run() # Run the app \ No newline at end of file diff --git a/src/python_functions.py b/src/python_functions.py index 2c9b1b0..98f0afc 100644 --- a/src/python_functions.py +++ b/src/python_functions.py @@ -1,7 +1,7 @@ import os import json import requests -from kivy.logger import Logger +from logging_config import Logger # Import the shared logger import bcrypt import time diff --git a/src/python_functions2.py b/src/python_functions2.py deleted file mode 100644 index 48d330d..0000000 --- a/src/python_functions2.py +++ /dev/null @@ -1,248 +0,0 @@ -import os -import json -import requests -from kivy.logger import Logger -import bcrypt -import time - -CONFIG_FILE = './Resurse/app_config.txt' -LOCAL_PLAYLIST_FILE = './static/local_playlist.json' # Path to the local playlist file -def load_config(): - """Load configuration from app_config.txt.""" - if os.path.exists(CONFIG_FILE): - try: - with open(CONFIG_FILE, 'r') as file: - Logger.info("python_functions: Configuration file loaded successfully.") - return json.load(file) - except json.JSONDecodeError as e: - Logger.error(f"python_functions: Failed to parse configuration file. Error: {e}") - return { - "screen_orientation": "Landscape", - "screen_name": "", - "quickconnect_key": "", - "server_ip": "", - "port": "" - } - else: - Logger.error(f"python_functions: Configuration file {CONFIG_FILE} not found.") - return { - "screen_orientation": "Landscape", - "screen_name": "", - "quickconnect_key": "", - "server_ip": "", - "port": "" - } - -# Load configuration and initialize variables -config_data = load_config() -server = config_data.get("server_ip", "") -host = config_data.get("screen_name", "") -quick = config_data.get("quickconnect_key", "") -port = config_data.get("port", "") -print(server, host, quick, port) -# Determine the configuration status -if server and host and quick and port: - config_status = "ok" -else: - config_status = "not_ok" - -Logger.info(f"python_functions: Configuration loaded: server={server}, host={host}, quick={quick}, port={port}") -Logger.info(f"python_functions: Configuration status: {config_status}") - - -def load_playlist(): - """Load playlist from the server or local storage and periodically check for updates.""" - local_playlist = load_local_playlist() - local_version = local_playlist.get('version', 0) if local_playlist else 0 - - while True: - try: - Logger.info("python_functions: Checking playlist version on the server...") - server_ip = f'{server}:{port}' # Construct the server IP with port - url = f'http://{server_ip}/api/playlist_version' - params = { - 'hostname': host, - 'quickconnect_code': quick - } - response = requests.get(url, params=params) - - if response.status_code == 200: - response_data = response.json() - server_version = response_data.get('playlist_version', None) - hashed_quickconnect = response_data.get('hashed_quickconnect', None) - - if server_version is not None and hashed_quickconnect is not None: - # Validate the quickconnect code using bcrypt - if bcrypt.checkpw(quick.encode('utf-8'), hashed_quickconnect.encode('utf-8')): - Logger.info(f"python_functions: Server playlist version: {server_version}, Local playlist version: {local_version}") - - if server_version != local_version: - Logger.info("python_functions: Playlist versions differ. Updating local playlist...") - updated_playlist = fetch_server_playlist() - if updated_playlist and 'playlist' in updated_playlist: - save_local_playlist(updated_playlist) # Update local playlist - local_version = server_version # Update local version - else: - Logger.error("python_functions: Failed to update local playlist. Using existing playlist.") - else: - Logger.info("python_functions: Playlist versions match. No update needed.") - else: - Logger.error("python_functions: Quickconnect code validation failed.") - else: - Logger.error("python_functions: Failed to retrieve playlist version or hashed quickconnect from the response.") - else: - Logger.error(f"python_functions: Failed to retrieve playlist version. Status Code: {response.status_code}") - except requests.exceptions.RequestException as e: - Logger.error(f"python_functions: Failed to check playlist version: {e}") - - # Wait for 5 minutes before checking again - time.sleep(300) - -def load_local_playlist(): - """Load the playlist from local storage.""" - if os.path.exists(LOCAL_PLAYLIST_FILE): - try: - with open(LOCAL_PLAYLIST_FILE, 'r') as local_file: - local_playlist = json.load(local_file) - - # Validate the structure of the local playlist - if isinstance(local_playlist, dict) and 'playlist' in local_playlist and 'version' in local_playlist: - Logger.info("python_functions: Loaded and validated local playlist.") - return local_playlist - else: - Logger.error("python_functions: Invalid local playlist structure.") - return None - except json.JSONDecodeError: - Logger.error("python_functions: Failed to parse local playlist file.") - return None - else: - Logger.warning("python_functions: Local playlist file not found.") - return None - -def download_media_files(playlist): - """Download media files from the server and update the local playlist.""" - Logger.info("python_functions: Starting media file download...") - base_dir = os.path.join(os.path.dirname(__file__), 'static', 'resurse') # Path to the local folder - if not os.path.exists(base_dir): - os.makedirs(base_dir) - Logger.info(f"python_functions: Created directory {base_dir} for media files.") - - updated_playlist = [] # List to store updated media entries - - for media in playlist: - file_name = media.get('file_name', '') - file_url = media.get('url', '') - duration = media.get('duration', 10) # Default duration if not provided - file_path = os.path.join(base_dir, file_name) - - Logger.debug(f"python_functions: Preparing to download {file_name} from {file_url}...") - - if os.path.exists(file_path): - Logger.info(f"python_functions: File {file_name} already exists. Skipping download.") - else: - try: - response = requests.get(file_url, timeout=10) # Add timeout for better error handling - if response.status_code == 200: - with open(file_path, 'wb') as file: - file.write(response.content) - Logger.info(f"python_functions: Successfully downloaded {file_name} to {file_path}") - else: - Logger.error(f"python_functions: Failed to download {file_name}. Status Code: {response.status_code}") - continue - except requests.exceptions.RequestException as e: - Logger.error(f"python_functions: Error downloading {file_name}: {e}") - continue - - # Update the media entry with the local file path and duration - updated_media = { - 'file_name': file_name, - 'url': file_path, # Update URL to local path - 'duration': duration - } - updated_playlist.append(updated_media) - - # Save the updated playlist locally - save_local_playlist({'playlist': updated_playlist, 'version': playlist.get('version', 0)}) - -def clean_unused_files(playlist): - """Remove unused media files from the resource folder.""" - Logger.info("python_functions: Cleaning unused media files...") - base_dir = os.path.join(os.path.dirname(__file__), 'static', 'resurse') # Update this to the correct path - if not os.path.exists(base_dir): - Logger.debug(f"python_functions: Directory {base_dir} does not exist. No files to clean.") - return - - # Get all file names from the playlist - playlist_files = {media.get('file_name', '') for media in playlist} - - # Get all files in the directory - all_files = set(os.listdir(base_dir)) - - # Determine unused files - unused_files = all_files - playlist_files - - # Delete unused files - for file_name in unused_files: - file_path = os.path.join(base_dir, file_name) - try: - os.remove(file_path) - Logger.info(f"python_functions: Deleted unused file: {file_path}") - except OSError as e: - Logger.error(f"python_functions: Failed to delete {file_path}: {e}") - -def fetch_server_playlist(): - """Fetch the updated playlist from the server.""" - try: - server_ip = f'{server}:{port}' # Construct the server IP with port - url = f'http://{server_ip}/api/playlists' - params = { - 'hostname': host, - 'quickconnect_code': quick - } - response = requests.get(url, params=params) - - if response.status_code == 200: - response_data = response.json() - playlist = response_data.get('playlist', []) - version = response_data.get('playlist_version', None) - hashed_quickconnect = response_data.get('hashed_quickconnect', None) - - if version is not None and hashed_quickconnect is not None: - # Validate the quickconnect code using bcrypt - if bcrypt.checkpw(quick.encode('utf-8'), hashed_quickconnect.encode('utf-8')): - Logger.info("python_functions: Fetched updated playlist from server.") - return {'playlist': playlist, 'version': version} - else: - Logger.error("python_functions: Quickconnect code validation failed.") - else: - Logger.error("python_functions: Failed to retrieve playlist or hashed quickconnect from the response.") - else: - Logger.error(f"python_functions: Failed to fetch playlist. Status Code: {response.status_code}") - except requests.exceptions.RequestException as e: - Logger.error(f"python_functions: Failed to fetch playlist: {e}") - - # Return an empty playlist if fetching fails - return {'playlist': [], 'version': 0} - -def save_local_playlist(playlist): - """Save the updated playlist locally.""" - if not playlist or 'playlist' not in playlist: - Logger.error("python_functions: Invalid playlist data. Cannot save local playlist.") - return - - try: - with open(LOCAL_PLAYLIST_FILE, 'w') as local_file: - json.dump(playlist, local_file) - Logger.info("python_functions: Updated local playlist with server data.") - except IOError as e: - Logger.error(f"python_functions: Failed to save local playlist: {e}") - -def check_playlist_updates(self, dt): - """Check for updates to the playlist.""" - new_playlist = load_playlist() # Load the new playlist - if new_playlist != self.playlist: # Compare the new playlist with the current one - Logger.info("Playlist updated. Changes detected.") - self.updated_playlist = new_playlist # Store the updated playlist - self.is_playlist_update_pending = True # Mark the update as pending - else: - Logger.info("Playlist update skipped. No changes detected.") \ No newline at end of file diff --git a/src/static/local_playlist.json b/src/static/local_playlist.json index 4817252..3a0eab2 100644 --- a/src/static/local_playlist.json +++ b/src/static/local_playlist.json @@ -1 +1 @@ -{"playlist": [{"file_name": "123.jpeg", "url": "/home/pi/Desktop/signage-player/src/static/resurse/123.jpeg", "duration": 30}], "version": 5} \ No newline at end of file +{"playlist": [{"file_name": "20250417_09h25m37s_grim.png", "url": "/home/pi/Desktop/signage-player/src/static/resurse/20250417_09h25m37s_grim.png", "duration": 20}], "version": 6} \ No newline at end of file diff --git a/src/static/resurse/20250417_09h25m37s_grim.png b/src/static/resurse/20250417_09h25m37s_grim.png new file mode 100644 index 0000000..1bcc6ab Binary files /dev/null and b/src/static/resurse/20250417_09h25m37s_grim.png differ diff --git a/src/static/resurse/20250605_09h44m12s_grim.png b/src/static/resurse/20250605_09h44m12s_grim.png deleted file mode 100644 index 8ee25bd..0000000 Binary files a/src/static/resurse/20250605_09h44m12s_grim.png and /dev/null differ