diff --git a/run_app.sh b/run_app.sh index 98a9320..7726135 100755 --- a/run_app.sh +++ b/run_app.sh @@ -3,7 +3,7 @@ # filepath: /home/pi/Desktop/signage-player/run_app.sh # Navigate to the application directory -cd /home/pi/signage-player/src || exit +cd /home/pi/Desktop/signage-player/src || exit # Check for the --verbose flag if [[ "$1" == "--verbose" ]]; then diff --git a/src/Resurse/app_config.txt b/src/Resurse/app_config.txt index c9a19bb..35d7a31 100644 --- a/src/Resurse/app_config.txt +++ b/src/Resurse/app_config.txt @@ -1 +1,10 @@ -{"screen_orientation": "Landscape", "screen_name": "rpi-tv11", "quickconnect_key": "8887779", "server_ip": "192.168.1.74", "port": "5000", "screen_w": "1920", "screen_h": "1080"} \ No newline at end of file +{ + "screen_orientation": "Landscape", + "screen_name": "rpi-tv11", + "quickconnect_key": "8887779", + "server_ip": "192.168.1.74", + "port": "5000", + "screen_w": "1920", + "screen_h": "1080", + "playlist_version": 5 +} \ No newline at end of file diff --git a/src/Resurse/log.txt b/src/Resurse/log.txt index 55b21c3..1924971 100644 --- a/src/Resurse/log.txt +++ b/src/Resurse/log.txt @@ -1,7 +1,27 @@ -2025-06-20 16:32:40 - STARTED: edit_pencil.png -2025-06-20 16:33:00 - STARTED: delete.png -2025-06-20 16:35:13 - STARTED: edit_pencil.png -2025-06-20 16:35:15 - STARTED: delete.png -2025-06-20 16:35:16 - STARTED: edit_pencil.png -2025-06-20 16:35:17 - STARTED: delete.png -2025-06-20 16:35:18 - STARTED: edit_pencil.png +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 diff --git a/src/__pycache__/python_functions.cpython-311.pyc b/src/__pycache__/python_functions.cpython-311.pyc index b22c0c9..da6b773 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 49929c8..0396cf3 100644 --- a/src/kv/media_player.kv +++ b/src/kv/media_player.kv @@ -162,6 +162,23 @@ multiline: False size_hint_x: 0.3 + # New row for testing server connection + BoxLayout: + orientation: 'horizontal' + size_hint_y: None + height: 40 + spacing: 10 + + Button: + text: "Test Connection to Server" + size_hint_x: 0.3 + on_release: root.test_server_connection() + + Label: + id: server_connection_label + text: "Server connection status will appear here." + size_hint_x: 0.7 + # 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/media_player.py b/src/media_player.py index 9a9f257..d10ca09 100644 --- a/src/media_player.py +++ b/src/media_player.py @@ -21,9 +21,10 @@ import os # Import os for file and directory operations import json # Import json for handling JSON data import datetime # Import datetime for timestamping logs import subprocess +import requests # Import functions from python_functions.py -from python_functions import load_local_playlist, download_media_files, clean_unused_files, fetch_server_playlist +from python_functions import load_local_playlist, download_media_files, clean_unused_files,save_local_playlist, fetch_server_playlist # Load the KV file for UI layout Builder.load_file('kv/media_player.kv') @@ -103,34 +104,37 @@ class MediaPlayer(Screen): def on_enter(self): """Called when the screen is entered.""" Logger.info("MediaPlayer: Entering screen...") - + # Attempt to load the local playlist - self.playlist = load_local_playlist() - Logger.info(f"MediaPlayer: Loaded local playlist: {self.playlist}") - + local_playlist_data = load_local_playlist() + self.playlist = local_playlist_data.get('playlist', []) # Extract the playlist key + local_version = local_playlist_data.get('version', 0) # Extract the local playlist version + Logger.info(f"MediaPlayer: Loaded local playlist: {self.playlist}, Version: {local_version}") + if not self.playlist: # If no local playlist exists Logger.warning("MediaPlayer: No local playlist found. Fetching from server...") - + # Fetch the server playlist server_playlist_data = fetch_server_playlist() server_playlist = server_playlist_data.get('playlist', []) server_version = server_playlist_data.get('version', 0) - + if server_playlist: # If server playlist is valid Logger.info("MediaPlayer: Server playlist fetched successfully.") - + # Download media files and save the playlist locally download_media_files(server_playlist, server_version) - self.playlist = load_local_playlist() # Reload the updated local playlist - - if self.playlist: - Logger.info("MediaPlayer: Local playlist updated successfully.") - else: - Logger.error("MediaPlayer: Failed to update local playlist.") + save_local_playlist({'playlist': server_playlist, 'version': server_version}) + update_config_playlist_version(server_version) # Update playlist version in app_config.txt + + # Reload the updated local playlist + local_playlist_data = load_local_playlist() + self.playlist = local_playlist_data.get('playlist', []) # Extract the playlist key + Logger.info("MediaPlayer: Local playlist updated successfully.") else: Logger.error("MediaPlayer: Failed to fetch server playlist. No media to play.") return - + if self.playlist: # If the playlist is loaded successfully self.play_media() # Start playing media self.show_buttons() # Ensure buttons are visible when the screen is entered @@ -272,7 +276,36 @@ class MediaPlayer(Screen): Clock.unschedule(self.image_timer) # Update the current index - self.current_index = (self.current_index + 1) % len(self.playlist) + self.current_index += 1 + + # Check if the end of the playlist is reached + if self.current_index >= len(self.playlist): + Logger.info("End of playlist reached. Checking for updates...") + self.current_index = 0 # Reset the index to start from the beginning + + # Fetch the server playlist + server_playlist_data = fetch_server_playlist() + server_playlist = server_playlist_data.get('playlist', []) + server_version = server_playlist_data.get('version', 0) + + # Load the local playlist + local_playlist_data = load_local_playlist() + local_version = local_playlist_data.get('version', 0) + + # Compare versions + if server_version > local_version: # Update only if server version is newer + Logger.info(f"Playlist version mismatch detected. Local version: {local_version}, Server version: {server_version}") + + # Clean up old media files + clean_unused_files(local_playlist_data.get('playlist', [])) + + # Update the local playlist and download new media files + download_media_files(server_playlist, server_version) + local_playlist_data = load_local_playlist() # Reload the updated local playlist + self.playlist = local_playlist_data.get('playlist', []) # Extract the playlist key + Logger.info("Playlist updated successfully.") + else: + Logger.info("Playlist versions match. No update needed.") # Play the next media self.play_media() @@ -349,25 +382,40 @@ class MediaPlayer(Screen): self.ids.play_pause_button.background_normal = './Resurse/play.png' def check_playlist_updates(self, dt): - """Check for updates to the playlist.""" + """Check for updates to the playlist using the playlist_version from app_config.txt.""" Logger.info("Checking for playlist updates...") - + + # Load the playlist version from app_config.txt + if os.path.exists(CONFIG_FILE): + try: + with open(CONFIG_FILE, 'r') as file: + config_data = json.load(file) + local_version = config_data.get('playlist_version', 0) # Get the local playlist version + Logger.info(f"Loaded local playlist version from app_config.txt: {local_version}") + except json.JSONDecodeError as e: + Logger.error(f"Failed to parse app_config.txt. Error: {e}") + local_version = 0 + else: + Logger.warning(f"Configuration file {CONFIG_FILE} not found. Defaulting local playlist version to 0.") + local_version = 0 + # Fetch the server playlist - server_playlist_data = fetch_server_playlist() # Fetch the playlist from the server + server_playlist_data = fetch_server_playlist() server_playlist = server_playlist_data.get('playlist', []) server_version = server_playlist_data.get('version', 0) - # Load the local playlist - local_playlist_data = load_local_playlist() # Load the local playlist - local_version = local_playlist_data.get('version', 0) if local_playlist_data else 0 - # Compare versions - if server_version != local_version: # If versions differ + if server_version > local_version: # Update only if server version is newer Logger.info(f"Playlist version mismatch detected. Local version: {local_version}, Server version: {server_version}") + # Clean up old media files + local_playlist_data = load_local_playlist() + clean_unused_files(local_playlist_data.get('playlist', [])) + # Update the local playlist and download new media files - download_media_files(server_playlist) # Download media files from the server - self.playlist = load_local_playlist() # Reload the updated local playlist + download_media_files(server_playlist, server_version) + local_playlist_data = load_local_playlist() # Reload the updated local playlist + self.playlist = local_playlist_data.get('playlist', []) # Extract the playlist key Logger.info("Playlist updated successfully.") else: Logger.info("Playlist versions match. No update needed.") @@ -391,7 +439,8 @@ class SettingsScreen(Screen): "server_ip": "", "port": "", "screen_w": "", # Default width - "screen_h": "" # Default height + "screen_h": "", # Default height + "playlist_version": 0 # Default playlist version } def save_config(self): @@ -456,6 +505,45 @@ class SettingsScreen(Screen): Logger.warning("Incorrect password. Returning to SettingsScreen.") popup.dismiss() # Close the popup + def test_server_connection(self): + """Test the connection to the server and update the label.""" + Logger.info("SettingsScreen: Testing connection to server...") + + # Load configuration data + server_ip = self.config_data.get("server_ip", "") + port = self.config_data.get("port", "") + local_version = self.config_data.get("playlist_version", 0) + + if not server_ip or not port: + self.ids.server_connection_label.text = "Server IP or port is not configured." + Logger.error("SettingsScreen: Server IP or port is missing in configuration.") + return + + try: + # Construct the server URL + url = f"http://{server_ip}:{port}/api/playlists" + params = { + 'hostname': self.config_data.get("screen_name", ""), + 'quickconnect_code': self.config_data.get("quickconnect_key", "") + } + Logger.info(f"SettingsScreen: Sending request to {url} with params: {params}...") + response = requests.get(url, params=params, timeout=5) + + if response.status_code == 200: + response_data = response.json() + server_version = response_data.get("playlist_version", "Unknown") + self.ids.server_connection_label.text = ( + f"Server is reachable. Playlist version on server: {server_version}. " + f"Playlist version on player: {local_version}." + ) + Logger.info("SettingsScreen: Server is reachable.") + else: + self.ids.server_connection_label.text = f"Server unreachable. Status code: {response.status_code}." + Logger.error(f"SettingsScreen: Failed to reach server. Status code: {response.status_code}.") + except requests.exceptions.RequestException as e: + self.ids.server_connection_label.text = "Server unreachable. Check connection settings." + Logger.error(f"SettingsScreen: Error connecting to server: {e}") + class MediaPlayerApp(App): """Main application class.""" diff --git a/src/python_functions.py b/src/python_functions.py index f2cb39f..2c9b1b0 100644 --- a/src/python_functions.py +++ b/src/python_functions.py @@ -32,23 +32,23 @@ port = config_data.get("port", "") Logger.info(f"python_functions: Configuration loaded: server={server}, host={host}, quick={quick}, port={port}") def load_local_playlist(): - """Load the playlist from local storage.""" + """Load the playlist and version 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) Logger.info(f"python_functions: Local playlist loaded: {local_playlist}") if isinstance(local_playlist, dict) and 'playlist' in local_playlist and 'version' in local_playlist: - return local_playlist.get('playlist', []) + return local_playlist # Return the full playlist data else: Logger.error("python_functions: Invalid local playlist structure.") - return [] + return {'playlist': [], 'version': 0} except json.JSONDecodeError as e: Logger.error(f"python_functions: Failed to parse local playlist file. Error: {e}") - return [] + return {'playlist': [], 'version': 0} else: Logger.warning("python_functions: Local playlist file not found.") - return [] + return {'playlist': [], 'version': 0} def save_local_playlist(playlist): """Save the updated playlist locally.""" @@ -72,26 +72,32 @@ def fetch_server_playlist(): 'hostname': host, 'quickconnect_code': quick } + Logger.info(f"Fetching playlist from URL: {url} with params: {params}") response = requests.get(url, params=params) if response.status_code == 200: response_data = response.json() + Logger.info(f"Server response: {response_data}") 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: if bcrypt.checkpw(quick.encode('utf-8'), hashed_quickconnect.encode('utf-8')): - Logger.info("python_functions: Fetched updated playlist from server.") + Logger.info("Fetched updated playlist from server.") + + # Update the playlist version in app_config.txt + update_config_playlist_version(version) + return {'playlist': playlist, 'version': version} else: - Logger.error("python_functions: Quickconnect code validation failed.") + Logger.error("Quickconnect code validation failed.") else: - Logger.error("python_functions: Failed to retrieve playlist or hashed quickconnect from the response.") + Logger.error("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}") + Logger.error(f"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}") + Logger.error(f"Failed to fetch playlist: {e}") return {'playlist': [], 'version': 0} @@ -157,4 +163,22 @@ def clean_unused_files(playlist): 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}") \ No newline at end of file + Logger.error(f"python_functions: Failed to delete {file_path}: {e}") + +def update_config_playlist_version(version): + """Update the playlist version in app_config.txt.""" + if not os.path.exists(CONFIG_FILE): + Logger.error(f"python_functions: Configuration file {CONFIG_FILE} not found.") + return + + try: + with open(CONFIG_FILE, 'r') as file: + config_data = json.load(file) + + config_data['playlist_version'] = version # Add or update the playlist version + + with open(CONFIG_FILE, 'w') as file: + json.dump(config_data, file, indent=4) + Logger.info(f"python_functions: Updated playlist version in app_config.txt to {version}.") + except (IOError, json.JSONDecodeError) as e: + Logger.error(f"python_functions: Failed to update playlist version in app_config.txt. Error: {e}") \ No newline at end of file diff --git a/src/static/local_playlist.json b/src/static/local_playlist.json index 8a0e395..4817252 100644 --- a/src/static/local_playlist.json +++ b/src/static/local_playlist.json @@ -1 +1 @@ -{"playlist": [{"file_name": "edit_pencil.png", "url": "/home/pi/Desktop/signage-player/src/static/resurse/edit_pencil.png", "duration": 20}, {"file_name": "delete.png", "url": "/home/pi/Desktop/signage-player/src/static/resurse/delete.png", "duration": 20}], "version": 2} \ No newline at end of file +{"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 diff --git a/src/static/resurse/123.jpeg b/src/static/resurse/123.jpeg new file mode 100644 index 0000000..29d8372 Binary files /dev/null and b/src/static/resurse/123.jpeg differ diff --git a/src/static/resurse/20250605_09h44m12s_grim.png b/src/static/resurse/20250605_09h44m12s_grim.png new file mode 100644 index 0000000..8ee25bd Binary files /dev/null and b/src/static/resurse/20250605_09h44m12s_grim.png differ diff --git a/src/static/resurse/delete.png b/src/static/resurse/delete.png deleted file mode 100644 index eb96774..0000000 Binary files a/src/static/resurse/delete.png and /dev/null differ diff --git a/src/static/resurse/edit_pencil.png b/src/static/resurse/edit_pencil.png deleted file mode 100644 index da079d9..0000000 Binary files a/src/static/resurse/edit_pencil.png and /dev/null differ