diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eba74f4 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +venv/ \ No newline at end of file diff --git a/Install.sh b/Install.sh deleted file mode 100644 index 7022266..0000000 --- a/Install.sh +++ /dev/null @@ -1,134 +0,0 @@ -#!/bin/bash - -# Define variables -REPO_URL="https://gitea.moto-adv.com/ske087/signage-player.git" -DEFAULT_INSTALL_DIR="/home/pi/signage-player" -SERVICE_FILE="/etc/systemd/system/signage_player.service" -RUN_APP_FILE="$DEFAULT_INSTALL_DIR/run_app.sh" - -# Ask the user whether to use the default installation directory -echo "The default installation directory is: $DEFAULT_INSTALL_DIR" -read -p "Do you want to use the default installation directory? (y/n): " response - -# Handle the user's response -if [[ "$response" == "y" || "$response" == "Y" ]]; then - INSTALL_DIR="$DEFAULT_INSTALL_DIR" - echo "Using default installation directory: $INSTALL_DIR" -else - read -p "Enter the custom installation directory: " CUSTOM_INSTALL_DIR - INSTALL_DIR="$CUSTOM_INSTALL_DIR" - if [[ ! -d "$INSTALL_DIR" ]]; then - echo "Directory $INSTALL_DIR does not exist. Creating it..." - mkdir -p "$INSTALL_DIR" - if [[ $? -ne 0 ]]; then - echo "Error: Failed to create directory $INSTALL_DIR." - read -p "Press any key to close the terminal..." -n 1 -s - exit 1 - fi - fi - echo "Using custom installation directory: $INSTALL_DIR" -fi - -# Update system packages -echo "Updating system packages..." -sudo apt update && sudo apt upgrade -y || { - echo "Error: Failed to update system packages." - read -p "Press any key to close the terminal..." -n 1 -s - exit 1 -} -echo "Installation directory is set to: $INSTALL_DIR and is starting the installation process." -# Install required system packages -echo "Installing required system packages..." -sudo apt install -y python3 python3-pip git || { - echo "Error: Failed to install required system packages." - read -p "Press any key to close the terminal..." -n 1 -s - exit 1 -} - -# Clone the repository -echo "Cloning the repository..." -if [ -d "$INSTALL_DIR" ]; then - echo "Directory $INSTALL_DIR already exists. Removing it..." - sudo rm -rf "$INSTALL_DIR" -fi -git clone "$REPO_URL" "$INSTALL_DIR" || { - echo "Error: Failed to clone the repository." - read -p "Press any key to close the terminal..." -n 1 -s - exit 1 -} - -# Navigate to the cloned repository -cd "$INSTALL_DIR" || { - echo "Error: Failed to navigate to the cloned repository." - read -p "Press any key to close the terminal..." -n 1 -s - exit 1 -} - -# Install Python dependencies -echo "Installing Python dependencies..." -pip3 install -r requirements.txt --break-system-packages || { - echo "Error: Failed to install Python dependencies." - read -p "Press any key to close the terminal..." -n 1 -s - exit 1 -} - -# Set permissions for the run_app.sh script -echo "Setting permissions for run_app.sh..." -chmod +x "$INSTALL_DIR/run_app.sh" || { - echo "Error: Failed to set permissions for run_app.sh." - read -p "Press any key to close the terminal..." -n 1 -s - exit 1 -} - -# Create the signage_player.service file in the installation folder -SERVICE_TEMPLATE="$INSTALL_DIR/signage_player.service" -echo "Creating signage_player.service file in the installation folder..." -cat < "$SERVICE_TEMPLATE" -[Unit] -Description=Signage Player Service -After=network.target - -[Service] -Type=simple -WorkingDirectory=$INSTALL_DIR -ExecStart=$INSTALL_DIR/run_app.sh -Restart=always -RestartSec=5 - -[Install] -WantedBy=multi-user.target -EOF - -# Copy the service file to the systemd service directory -echo "Copying signage_player.service file to $SERVICE_FILE..." -sudo cp "$SERVICE_TEMPLATE" "$SERVICE_FILE" || { - echo "Error: Failed to copy signage_player.service file." - read -p "Press any key to close the terminal..." -n 1 -s - exit 1 -} - -# Reload systemd daemon -echo "Reloading systemd daemon..." -sudo systemctl daemon-reload || { - echo "Error: Failed to reload systemd daemon." - read -p "Press any key to close the terminal..." -n 1 -s - exit 1 -} - -# Enable the service -echo "Enabling signage_player.service..." -sudo systemctl enable signage_player.service || { - echo "Error: Failed to enable signage_player.service." - read -p "Press any key to close the terminal..." -n 1 -s - exit 1 -} - -echo "signage_player.service file created, copied, and enabled successfully." - -# Restart the system to check if the service starts -echo "Restarting the system to check if the service starts..." -sudo reboot || { - echo "Error: Failed to restart the system." - read -p "Press any key to close the terminal..." -n 1 -s - exit 1 -} \ No newline at end of file diff --git a/install_tkinter.sh b/install_tkinter.sh new file mode 100755 index 0000000..0707917 --- /dev/null +++ b/install_tkinter.sh @@ -0,0 +1,68 @@ +#!/bin/bash +# Tkinter Media Player Installation Script + +echo "Installing Tkinter Media Player..." + +# Update system packages +echo "Updating system packages..." +sudo apt update +sudo apt upgrade -y + +# Install system dependencies +echo "Installing system dependencies..." +sudo apt install -y python3 python3-pip python3-venv python3-tk +sudo apt install -y ffmpeg libopencv-dev python3-opencv +sudo apt install -y libsdl2-dev libsdl2-mixer-dev libsdl2-image-dev libsdl2-ttf-dev +sudo apt install -y libjpeg-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev + +# Create project directory if it doesn't exist +PROJECT_DIR="/home/pi/Desktop/signage-player" +if [ ! -d "$PROJECT_DIR" ]; then + echo "Project directory not found. Please ensure the signage-player directory exists." + exit 1 +fi + +cd "$PROJECT_DIR" + +# Create virtual environment +echo "Creating Python virtual environment..." +python3 -m venv venv + +# Activate virtual environment and install requirements +echo "Installing Python dependencies..." +source venv/bin/activate +pip install --upgrade pip +pip install -r tkinter_requirements.txt +deactivate + +# Make launcher script executable +chmod +x run_tkinter_app.sh + +# Create systemd service for auto-start +echo "Creating systemd service..." +sudo tee /etc/systemd/system/tkinter-signage-player.service > /dev/null < /dev/null 2>&1 & -fi \ No newline at end of file diff --git a/run_tkinter_debug.sh b/run_tkinter_debug.sh new file mode 100755 index 0000000..185f43d --- /dev/null +++ b/run_tkinter_debug.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# Debugging launch script for the tkinter player application + +# Activate the virtual environment +source venv/bin/activate + +# Change to the tkinter app src directory +cd tkinter_app/src + +# Run the main application with full error output +python main.py + +# Deactivate virtual environment when done +deactivate \ No newline at end of file diff --git a/signage-player.service b/signage-player.service deleted file mode 100644 index 3e110ce..0000000 --- a/signage-player.service +++ /dev/null @@ -1,15 +0,0 @@ -[Unit] -Description=Signage Player Service -After=network.targhet - -[Service] -ExecStart=/home/pi/signage-player/run_app.sh -WorkingDirectory=/home/pi/signage-player -StandardOutput=inherit -StandardError=inherit -Restart=always -User=pi -Enviroment=DISPLAY=:0 - -[Install] -WantedBy=multi-user.targhet diff --git a/src/Resurse/app_config.txt b/src/Resurse/app_config.txt deleted file mode 100644 index c8147a0..0000000 --- a/src/Resurse/app_config.txt +++ /dev/null @@ -1 +0,0 @@ -{"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": 7} \ No newline at end of file diff --git a/src/Resurse/log.txt b/src/Resurse/log.txt deleted file mode 100644 index e943027..0000000 --- a/src/Resurse/log.txt +++ /dev/null @@ -1,43 +0,0 @@ -[INFO] [SignageApp] python_functions: Starting load_config function. -[INFO] [SignageApp] python_functions: Configuration file loaded successfully. -[INFO] [SignageApp] python_functions: Configuration loaded: server=192.168.1.74, host=, quick=8887779, port=5000 -[INFO] [SignageApp] Screen size set to 1920x1080 -[INFO] [SignageApp] MediaPlayer: Starting on_enter method. -[INFO] [SignageApp] MediaPlayer: Loaded server settings: server=192.168.1.74, host=, quick=8887779, port=5000 -[INFO] [SignageApp] python_functions: Starting load_local_playlist function. -[WARNING] [SignageApp] python_functions: Local playlist file not found. -[INFO] [SignageApp] MediaPlayer: Loaded local playlist: [], Version: 0 -[WARNING] [SignageApp] MediaPlayer: No local playlist found. Attempting to load demo playlist... -[INFO] [SignageApp] MediaPlayer: Demo playlist loaded successfully. -[WARNING] [SignageApp] MediaPlayer: Invalid server settings. Using demo playlist. -[INFO] [SignageApp] MediaPlayer: Starting play_media method. -[INFO] [SignageApp] MediaPlayer: Playing media: Resurse/demo1.jpg -[INFO] [SignageApp] MediaPlayer: Starting log_event function. -2025-06-25 15:53:54 - STARTED: demo1.jpg -[INFO] [SignageApp] MediaPlayer: Logged event: 2025-06-25 15:53:54 - STARTED: demo1.jpg -[INFO] [SignageApp] MediaPlayer: Finished log_event function. -[INFO] [SignageApp] Showing image: Resurse/demo1.jpg -[INFO] [SignageApp] MediaPlayer: Finished play_media method. -[INFO] [SignageApp] MediaPlayer: Finished on_enter method. -[INFO] [SignageApp] SettingsScreen: Log messages loaded successfully. -[INFO] [SignageApp] SettingsScreen: Configuration saved. -[INFO] [SignageApp] MediaPlayer: Starting on_enter method. -[INFO] [SignageApp] MediaPlayer: Loaded server settings: server=192.168.1.74, host=rpi-tv11, quick=8887779, port=5000 -[INFO] [SignageApp] python_functions: Starting load_local_playlist function. -[WARNING] [SignageApp] python_functions: Local playlist file not found. -[INFO] [SignageApp] MediaPlayer: Loaded local playlist: [], Version: 0 -[WARNING] [SignageApp] MediaPlayer: No local playlist found. Attempting to load demo playlist... -[INFO] [SignageApp] MediaPlayer: Demo playlist loaded successfully. -[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.74:5000/api/playlists with params: {'hostname': '', 'quickconnect_code': '8887779'} -[INFO] [SignageApp] python_functions: Starting load_config function. -[INFO] [SignageApp] python_functions: Configuration file loaded successfully. -[INFO] [SignageApp] python_functions: Configuration loaded: server=192.168.1.74, host=rpi-tv11, quick=8887779, port=5000 -[INFO] [SignageApp] Screen size set to 1920x1080 -[INFO] [SignageApp] MediaPlayer: Starting on_enter method. -[INFO] [SignageApp] MediaPlayer: Loaded server settings: server=192.168.1.74, host=rpi-tv11, quick=8887779, port=5000 -[INFO] [SignageApp] python_functions: Starting load_local_playlist function. -[WARNING] [SignageApp] python_functions: Local playlist file not found. -[INFO] [SignageApp] MediaPlayer: Loaded local playlist: [], Version: 0 -[WARNING] [SignageApp] MediaPlayer: No local playlist found. Attempting to load demo playlist... -[INFO] [SignageApp] MediaPlayer: Demo playlist loaded successfully. -[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.74:5000/api/playlists with params: {'hostname': 'rpi-tv11', 'quickconnect_code': '8887779'} diff --git a/src/__pycache__/python_functions2.cpython-311.pyc b/src/__pycache__/python_functions2.cpython-311.pyc deleted file mode 100644 index d77abfc..0000000 Binary files a/src/__pycache__/python_functions2.cpython-311.pyc and /dev/null differ diff --git a/src/kv/media_player.kv b/src/kv/media_player.kv deleted file mode 100644 index a0f85b0..0000000 --- a/src/kv/media_player.kv +++ /dev/null @@ -1,227 +0,0 @@ -: - video_player: video_player - image_display: image_display - - Video: - id: video_player - allow_stretch: True - size_hint: (1, 1) - pos_hint: {'center_x': 0.5, 'center_y': 0.5} - opacity: 0 - keep_ratio: True - - Image: - id: image_display - allow_stretch: True - keep_ratio: True - size_hint: (1, 1) - pos_hint: {'center_x': 0.5, 'center_y': 0.5} - opacity: 0 - - # Settings (Home) button - Button: - id: settings_button - size_hint: None, None - size: 75, 75 - pos_hint: {'right': 0.98, 'bottom': 0.98} - background_normal: './Resurse/home_icon.png' - background_down: './Resurse/home_icon.png' - background_color: 1, 1, 1, 0.9 # Temporary red background for debugging - border: [0, 0, 0, 0] # Remove the default border - opacity: 1 - on_release: app.open_settings() - - # Right Arrow button - Button: - id: right_arrow_button - size_hint: None, None - size: 75, 75 - pos_hint: {'right': 0.93, 'bottom': 0.98} - background_normal: './Resurse/left-arrow-blue.png' - background_down: './Resurse/left-arrow-green.png' - background_color: 1, 1, 1, 0.9 # Temporary green background for debugging - border: [0, 0, 0, 0] # Remove the default border - opacity: 1 - on_release: root.next_media(None) - - # Play/Pause button - Button: - id: play_pause_button - size_hint: None, None - size: 75, 75 - pos_hint: {'right': 0.88, 'bottom': 0.98} - background_normal: './Resurse/play.png' # Initial state - background_down: './Resurse/play.png' # Initial state - background_color: 1, 1, 1, 0.9 # White with 90% transparency - border: [0, 0, 0, 0] - opacity: 1 - on_press: root.manage_play_pause_state() # Call the new function - - # Left Arrow button - Button: - id: left_arrow_button - size_hint: None, None - size: 75, 75 - pos_hint: {'right': 0.83, 'bottom': 0.98} - background_normal: './Resurse/right-arrow-blue.png' - background_down: './Resurse/right-arrow-green.png' - background_color: 1, 1, 1, 0.9 # Temporary yellow background for debugging - border: [0, 0, 0, 0] # Remove the default border - opacity: 1 - on_release: root.previous_media() - -: - BoxLayout: - orientation: 'vertical' - padding: 20 - spacing: 10 - - # Input fields in the upper half of the screen - BoxLayout: - orientation: 'vertical' - size_hint_y: 0.7 # Allocate 85% of the screen height for input fields - - Label: - text: "Screen Orientation" - size_hint_y: None - height: 40 - - TextInput: - id: orientation_input - hint_text: "Enter 'portrait' or 'landscape'" - multiline: False - size_hint_y: None - height: 40 - - Label: - text: "Screen Name" - size_hint_y: None - height: 40 - - TextInput: - id: screen_name_input - hint_text: "Enter screen name" - multiline: False - size_hint_y: None - height: 40 - - Label: - text: "QuickConnect Key" - size_hint_y: None - height: 40 - - TextInput: - id: quickconnect_key_input - hint_text: "Enter QuickConnect key" - multiline: False - size_hint_y: None - height: 40 - - Label: - text: "Server IP / Hostname" - size_hint_y: None - height: 40 - - TextInput: - id: server_ip_input - hint_text: "Enter server IP or hostname" - multiline: False - size_hint_y: None - height: 40 - - Label: - text: "Set Port" - size_hint_y: None - height: 40 - - TextInput: - id: port_input - hint_text: "Enter port number" - multiline: False - size_hint_y: None - height: 40 - Label: - text: "Screen Size Width / Height" - size_hint_y: None - height: 40 - - # New row for Screen Size Width / Height - BoxLayout: - orientation: 'horizontal' - size_hint_y: None - height: 40 - spacing: 10 - TextInput: - id: screen_width_input - hint_text: "Width" - multiline: False - size_hint_x: 0.3 - - TextInput: - id: screen_height_input - hint_text: "Height" - 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 - - # Multi-row label box for log messages - BoxLayout: - orientation: 'vertical' - size_hint_y: None - height: 200 # Adjust height as needed - padding: 10 - spacing: 5 - canvas.before: - Color: - rgba: 0.2, 0.2, 0.2, 1 # Dark gray background - Rectangle: - pos: self.pos - size: self.size - Color: - rgba: 1, 1, 1, 1 # White border - Line: - width: 2 - rectangle: (self.x, self.y, self.width, self.height) - - Label: - id: log_messages_label - text: "Log messages will appear here." - font_size: 14 - font_name: "RobotoMono-Regular.ttf" # Use a monospace font - halign: 'left' - valign: 'top' - text_size: self.size - color: 1, 1, 1, 1 # White text - - # Buttons in the lower part of the screen - BoxLayout: - size_hint_y: 0.4 # Allocate 40% of the screen height for buttons - spacing: 20 - - Button: - text: "Save" - size_hint_y: None - height: 50 - on_release: root.save_config() - - Button: - text: "Exit App" - size_hint_y: None - height: 50 - on_release: root.show_exit_popup() \ No newline at end of file diff --git a/src/media_player.py b/src/media_player.py deleted file mode 100644 index 98184d0..0000000 --- a/src/media_player.py +++ /dev/null @@ -1,631 +0,0 @@ -from kivy.config import Config -Config.set('kivy', 'video', 'ffpyplayer') -# Now import other Kivy modules -from kivy.app import App -from kivy.uix.screenmanager import ScreenManager, Screen # Import ScreenManager and Screen for managing screens -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, 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 -from kivy.uix.boxlayout import BoxLayout -from kivy.uix.label import Label -from kivy.uix.textinput import TextInput -from kivy.uix.button import Button -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 logging - -# Import functions from python_functions.py -from python_functions import load_local_playlist, download_media_files, clean_unused_files,save_local_playlist, update_config_playlist_version, 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') - -# Path to the configuration file -CONFIG_FILE = './Resurse/app_config.txt' - - -class MediaPlayer(Screen): - # Main screen for media playback. - - def __init__(self, **kwargs): - super(MediaPlayer, self).__init__(**kwargs) - self.playlist = [] # Initialize the playlist - self.current_index = 0 # Index of the currently playing media - self.updated_playlist = None # Store the updated playlist - self.is_playlist_update_pending = False # Flag to indicate a pending playlist update - self.video_player = self.ids.video_player # Reference to the Video widget - self.image_display = self.ids.image_display # Reference to the Image widget - self.log_file = os.path.join(os.path.dirname(__file__), 'Resurse', 'log.txt') # Path to the log file - self.is_paused = False # Track the state of the play/pause button - self.reset_timer = None # Timer to reset the button state after 3 minutes - self.image_timer = None # Timer for scheduling the next media for images - - # Load screen size from the configuration file - self.load_screen_size() - - # Schedule periodic updates to check for playlist updates - Clock.schedule_interval(self.check_playlist_updates, 300) # Every 5 minutes - # Bind key events to handle fullscreen toggle - Window.bind(on_key_down=self.on_key_down) - - # Start a timer to hide the buttons after 10 seconds - self.hide_button_timer = Clock.schedule_once(self.hide_buttons, 10) - - def load_screen_size(self): - """Load screen size from the configuration file and set the window size.""" - if os.path.exists(CONFIG_FILE): - with open(CONFIG_FILE, 'r') as file: - config_data = json.load(file) - screen_w = config_data.get("screen_w", "1920") - screen_h = config_data.get("screen_h", "1080") - - # Set the window size - try: - Window.size = (int(screen_w), int(screen_h)) - Logger.info(f"Screen size set to {screen_w}x{screen_h}") - except ValueError: - Logger.error("Invalid screen size values in configuration file.") - - def on_touch_down(self, touch): - # Handle touch events to reset the button visibility. - self.show_buttons() # Make all buttons visible - if hasattr(self, 'hide_button_timer'): - Clock.unschedule(self.hide_button_timer) # Cancel the existing hide timer - self.hide_button_timer = Clock.schedule_once(self.hide_buttons, 10) # Restart the hide timer - return super(MediaPlayer, self).on_touch_down(touch) - - def hide_buttons(self, *args): - # Hide all buttons after inactivity. - self.ids.settings_button.opacity = 0 # Hide the Home button - self.ids.right_arrow_button.opacity = 0 # Hide the Right Arrow button - self.ids.play_pause_button.opacity = 0 # Hide the Play/Pause button - self.ids.left_arrow_button.opacity = 0 # Hide the Left Arrow button - - def show_buttons(self): - # Show all buttons. - self.ids.settings_button.opacity = 1 # Show the Home button - self.ids.right_arrow_button.opacity = 1 # Show the Right Arrow button - self.ids.play_pause_button.opacity = 1 # Show the Play/Pause button - self.ids.left_arrow_button.opacity = 1 # Show the Left Arrow button - - def on_key_down(self, window, key, *args): - # Handle key events for toggling fullscreen mode. - if key == 102: # 'f' key - Window.fullscreen = not Window.fullscreen - - def on_enter(self): - """Called when the screen is entered.""" - Logger.info("MediaPlayer: Starting on_enter method.") - - # Load server settings from the configuration file - if os.path.exists(CONFIG_FILE): - try: - with open(CONFIG_FILE, 'r') as file: - config_data = json.load(file) - server = config_data.get("server_ip", "") - host = config_data.get("screen_name", "") - quick = config_data.get("quickconnect_key", "") - port = config_data.get("port", "") - Logger.info(f"MediaPlayer: Loaded server settings: server={server}, host={host}, quick={quick}, port={port}") - except json.JSONDecodeError as e: - Logger.error(f"MediaPlayer: Failed to parse configuration file. Error: {e}") - server, host, quick, port = "", "", "", "" - else: - Logger.warning(f"MediaPlayer: Configuration file {CONFIG_FILE} not found. Defaulting server settings to empty.") - server, host, quick, port = "", "", "", "" - - # Attempt to load the local 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}") - - # Check if the local playlist exists - if not self.playlist: # If no local playlist exists - Logger.warning("MediaPlayer: No local playlist found. Attempting to load demo playlist...") - - # Load the demo playlist - demo_playlist_path = os.path.join(os.path.dirname(__file__), 'Resurse', 'demo_playlist.json') - if os.path.exists(demo_playlist_path): - try: - with open(demo_playlist_path, 'r') as demo_file: - demo_playlist_data = json.load(demo_file) # Pass the file object to json.load() - self.playlist = demo_playlist_data.get('playlist', []) # Extract the playlist key - Logger.info("MediaPlayer: Demo playlist loaded successfully.") - except json.JSONDecodeError as e: - Logger.error(f"MediaPlayer: Failed to parse demo playlist file. Error: {e}") - else: - Logger.error("MediaPlayer: Demo playlist file not found. No media to play.") - return - - # Check if server settings are valid - if not self.playlist or not server or not host or not quick or not port: - Logger.warning("MediaPlayer: Invalid server settings. Using demo playlist.") - else: - # 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) - 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 - else: - Logger.warning("MediaPlayer: Playlist is empty. No media to play.") - Logger.info("MediaPlayer: Finished on_enter method.") - - def log_event(self, file_name, event): - """Log the start or stop event of a media file.""" - Logger.info("MediaPlayer: Starting log_event function.") - try: - # Get the current timestamp - timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') - log_message = f"{timestamp} - {event}: {file_name}\n" # Format the log message - - # Write the log message to the log file - with open(self.log_file, 'a') as log: - log.write(log_message) - Logger.info(f"MediaPlayer: Logged event: {log_message.strip()}") - except Exception as e: - Logger.error(f"MediaPlayer: Failed to log event: {e}") - Logger.info("MediaPlayer: Finished log_event function.") - - def cleanup_old_logs(self): - """Delete log entries older than 24 hours.""" - Logger.info("MediaPlayer: Starting cleanup_old_logs function.") - try: - # Read all log entries - if os.path.exists(self.log_file): - with open(self.log_file, 'r') as log: - lines = log.readlines() - - # Get the current time - now = datetime.datetime.now() - - # Filter out log entries older than 24 hours - filtered_lines = [] - for line in lines: - try: - # Extract the timestamp from the log entry - timestamp_str = line.split(' - ')[0] - log_time = datetime.datetime.strptime(timestamp_str, '%Y-%m-%d %H:%M:%S') - - # Keep the log entry if it's within the last 24 hours - if (now - log_time).total_seconds() <= 86400: # 24 hours in seconds - filtered_lines.append(line) - except (ValueError, IndexError): - # If the log entry is malformed, log the issue but do not raise warnings - Logger.info(f"MediaPlayer: Skipping malformed log entry: {line.strip()}") - - # Write the filtered log entries back to the log file - with open(self.log_file, 'w') as log: - log.writelines(filtered_lines) - Logger.info("MediaPlayer: Old log entries cleaned up successfully.") - except Exception as e: - Logger.error(f"MediaPlayer: Failed to clean up old logs: {e}") - Logger.info("MediaPlayer: Finished cleanup_old_logs function.") - - def play_media(self): - """Play the current media in the playlist.""" - Logger.info("MediaPlayer: Starting play_media method.") - if not self.playlist or not isinstance(self.playlist, list): - Logger.error("MediaPlayer: Playlist is invalid or empty. Cannot play media.") - return - - media = self.playlist[self.current_index] # Get the current media - file_name = media.get('file_name', '') # Get the file name - file_path = media.get('url', '') # Use the exact path specified in the playlist - duration = media.get('duration', 10) # Get the duration (default: 10 seconds) - - Logger.info(f"MediaPlayer: Playing media: {file_path}") - - # Check if the file exists - if not os.path.exists(file_path): - Logger.error(f"MediaPlayer: Media file not found: {file_path}") - return - - # Cancel any existing timers - if self.image_timer: - Logger.info("MediaPlayer: Canceling existing image timer.") - Clock.unschedule(self.image_timer) - - # Log the start of the media - self.log_event(file_name, "STARTED") - - # Determine the type of media and play it - file_extension = os.path.splitext(file_name)[1].lower() # Get the file extension - if file_extension in ['.mp4', '.avi', '.mov']: - self.play_video(file_path) # Play video - elif file_extension in ['.jpg', '.jpeg', '.png', '.gif']: - self.show_image(file_path, duration) # Show image - else: - Logger.error(f"MediaPlayer: Unsupported media type for file: {file_name}") - Logger.info("MediaPlayer: Finished play_media method.") - - def play_video(self, file_path): - """Play a video file without a fade-in effect.""" - Logger.info(f"MediaPlayer: Attempting to play video: {file_path}") - if not os.path.exists(file_path): - Logger.error(f"MediaPlayer: Video file not found: {file_path}") - return - - Logger.info(f"MediaPlayer: Video file exists. Setting up video player...") - # Set the video source and start playback - self.video_player.source = file_path - self.video_player.state = 'play' # Start playing the video - self.video_player.audio = True # Enable audio playback - self.video_player.opacity = 1 # Ensure the video is fully visible - self.image_display.opacity = 0 # Hide the image display - - # Schedule the next media after the video's duration - if self.video_player.duration > 0: - Logger.info(f"MediaPlayer: Video duration detected: {self.video_player.duration} seconds.") - Clock.schedule_once(self.next_media, self.video_player.duration) - else: - Logger.warning("MediaPlayer: Video duration is unknown. Using default duration.") - - def show_image(self, file_path, duration): - """Display an image with a fade-in effect.""" - Logger.info(f"Showing image: {file_path}") - if not os.path.exists(file_path): - Logger.error(f"Image file not found: {file_path}") - return - - # Set the image source - self.image_display.source = file_path - self.image_display.opacity = 0 # Start with the image hidden - self.image_display.reload() # Reload the image to ensure it updates - self.video_player.opacity = 0 # Hide the video player - - # Create a fade-in animation - fade_in = Animation(opacity=1, duration=1) # Fade in over 1 second - fade_in.start(self.image_display) # Start the fade-in animation - - # Schedule the next media after the duration - self.image_timer = Clock.schedule_once(self.next_media, duration) - - def next_media(self, dt=None): - """Move to the next media in the playlist.""" - Logger.info("Navigating to the next media.") - - # Cancel any existing timers - if self.image_timer: - Logger.info("Canceling image timer.") - Clock.unschedule(self.image_timer) - - # Update the current index - 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() - - def previous_media(self): - """Move to the previous media in the playlist.""" - Logger.info("Navigating to the previous media.") - - # Cancel any existing timers - if self.image_timer: - Logger.info("Canceling image timer.") - Clock.unschedule(self.image_timer) - - # Update the current index - self.current_index = (self.current_index - 1) % len(self.playlist) - - # Play the previous media - self.play_media() - - def toggle_play_pause(self): - #Toggle the play/pause button state and update its appearance. - self.manage_play_pause_state() - - def manage_play_pause_state(self): - # Manage the state of the play/pause button and media playback. - if self.is_paused: - Logger.info("Resuming media playback.") - self.video_player.state = 'play' - - # Resume the image timer if it exists - if self.image_timer: - Logger.info("Resuming image timer.") - self.image_timer() - - # Update the button to indicate the playing state - self.ids.play_pause_button.background_down = './Resurse/play.png' - self.ids.play_pause_button.background_normal = './Resurse/play.png' - - # Cancel the reset timer if it exists - if self.reset_timer: - Clock.unschedule(self.reset_timer) - else: - Logger.info("Pausing media playback.") - self.video_player.state = 'pause' - - # Pause the image timer if it exists - if self.image_timer: - Logger.info("Pausing image timer.") - Clock.unschedule(self.image_timer) - - # Update the button to indicate the paused state - self.ids.play_pause_button.background_down = './Resurse/pause.png' - self.ids.play_pause_button.background_normal = './Resurse/pause.png' - - # Start a timer to reset the button state after 30 seconds - self.reset_timer = Clock.schedule_once(self.reset_play_pause_state, 30) - - # Toggle the state - self.is_paused = not self.is_paused - - def reset_play_pause_state(self, dt): - # Reset the play/pause button state to 'play' after 30 seconds. - Logger.info("Resetting play/pause button state to 'play' after timeout.") - self.is_paused = False - self.video_player.state = 'play' - - # Resume the image timer if it exists - if self.image_timer: - Logger.info("Resuming image timer.") - self.image_timer() - - # Update the button appearance - self.ids.play_pause_button.background_down = './Resurse/play.png' - self.ids.play_pause_button.background_normal = './Resurse/play.png' - - def check_playlist_updates(self, dt): - """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() - server_playlist = server_playlist_data.get('playlist', []) - server_version = server_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 - 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, 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.") - -class SettingsScreen(Screen): - """Settings screen for configuring the app.""" - - def __init__(self, **kwargs): - super(SettingsScreen, self).__init__(**kwargs) - self.config_data = self.load_config() # Load the configuration data - - def load_config(self): - """Load the configuration from the config file.""" - if os.path.exists(CONFIG_FILE): - with open(CONFIG_FILE, 'r') as file: - return json.load(file) - return { - "screen_orientation": "", - "screen_name": "", - "quickconnect_key": "", - "server_ip": "", - "port": "", - "screen_w": "", # Default width - "screen_h": "", # Default height - "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 - self.config_data["screen_name"] = self.ids.screen_name_input.text - self.config_data["quickconnect_key"] = self.ids.quickconnect_key_input.text - self.config_data["server_ip"] = self.ids.server_ip_input.text - self.config_data["port"] = self.ids.port_input.text - self.config_data["screen_w"] = self.ids.screen_width_input.text - self.config_data["screen_h"] = self.ids.screen_height_input.text - - with open(CONFIG_FILE, 'w') as file: - json.dump(self.config_data, file) - Logger.info("SettingsScreen: Configuration saved.") - - # Return to the MediaPlayer screen after saving - self.manager.current = 'media_player' - - def on_pre_enter(self): - """Populate input fields with current config data.""" - self.ids.orientation_input.text = self.config_data.get("screen_orientation", "landscape") - self.ids.screen_name_input.text = self.config_data.get("screen_name", "") - self.ids.quickconnect_key_input.text = self.config_data.get("quickconnect_key", "") - self.ids.server_ip_input.text = self.config_data.get("server_ip", "") - self.ids.port_input.text = self.config_data.get("port", "8080") - 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) - - # Add a label - label = Label(text="Enter Password to Exit", size_hint=(1, 0.3)) - layout.add_widget(label) - - # Add a password input field - password_input = TextInput(password=True, multiline=False, size_hint=(1, 0.3)) - layout.add_widget(password_input) - - # Add buttons for "OK" and "Cancel" - button_layout = BoxLayout(size_hint=(1, 0.3), spacing=10) - ok_button = Button(text="OK", on_release=lambda *args: self.validate_exit_password(password_input.text, popup)) - cancel_button = Button(text="Cancel", on_release=lambda *args: popup.dismiss()) - button_layout.add_widget(ok_button) - button_layout.add_widget(cancel_button) - layout.add_widget(button_layout) - - # Create the popup - popup = Popup(title="Exit App", content=layout, size_hint=(0.8, 0.4)) - popup.open() - - def validate_exit_password(self, password, popup): - # Validate the entered password - quickconnect_key = self.config_data.get("quickconnect_key", "") - - if password == quickconnect_key: - Logger.info("Password correct. Exiting app.") - App.get_running_app().stop() # Exit the app - else: - 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.""" - - def build(self): - """Build the app and initialize screens.""" - Window.fullscreen = True # Start the app in fullscreen mode - sm = ScreenManager() # Create a screen manager - sm.add_widget(MediaPlayer(name='media_player')) # Add the MediaPlayer screen - sm.add_widget(SettingsScreen(name='settings')) # Add the SettingsScreen - return sm - - def open_settings(self): - """Switch to the SettingsScreen.""" - self.root.current = 'settings' - - - -if __name__ == '__main__': - MediaPlayerApp().run() # Run the app \ No newline at end of file diff --git a/tkinter_app/resources/app_config.txt b/tkinter_app/resources/app_config.txt new file mode 100644 index 0000000..3c65e24 --- /dev/null +++ b/tkinter_app/resources/app_config.txt @@ -0,0 +1,10 @@ +{ + "screen_orientation": "Landscape", + "screen_name": "tv-holba1", + "quickconnect_key": "8887779", + "server_ip": "192.168.1.245", + "port": "5000", + "screen_w": "1920", + "screen_h": "1080", + "playlist_version": 5 +} \ No newline at end of file diff --git a/src/Resurse/demo1.jpg b/tkinter_app/resources/demo1.jpg similarity index 100% rename from src/Resurse/demo1.jpg rename to tkinter_app/resources/demo1.jpg diff --git a/src/Resurse/demo2.jpeg b/tkinter_app/resources/demo2.jpeg similarity index 100% rename from src/Resurse/demo2.jpeg rename to tkinter_app/resources/demo2.jpeg diff --git a/src/Resurse/demo_playlist.json b/tkinter_app/resources/demo_playlist.json similarity index 100% rename from src/Resurse/demo_playlist.json rename to tkinter_app/resources/demo_playlist.json diff --git a/src/Resurse/home_icon.png b/tkinter_app/resources/home_icon.png similarity index 100% rename from src/Resurse/home_icon.png rename to tkinter_app/resources/home_icon.png diff --git a/src/Resurse/left-arrow-blue.png b/tkinter_app/resources/left-arrow-blue.png similarity index 100% rename from src/Resurse/left-arrow-blue.png rename to tkinter_app/resources/left-arrow-blue.png diff --git a/src/Resurse/left-arrow-green.png b/tkinter_app/resources/left-arrow-green.png similarity index 100% rename from src/Resurse/left-arrow-green.png rename to tkinter_app/resources/left-arrow-green.png diff --git a/tkinter_app/resources/local_playlist.json b/tkinter_app/resources/local_playlist.json new file mode 100644 index 0000000..c8813bc --- /dev/null +++ b/tkinter_app/resources/local_playlist.json @@ -0,0 +1,20 @@ +{ + "playlist": [ + { + "file_name": "1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg", + "url": "static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg", + "duration": 20 + }, + { + "file_name": "wp2782770-1846651530.jpg", + "url": "static/resurse/wp2782770-1846651530.jpg", + "duration": 15 + }, + { + "file_name": "SampleVideo_1280x720_1mb.mp4", + "url": "static/resurse/SampleVideo_1280x720_1mb.mp4", + "duration": 5 + } + ], + "version": 5 +} \ No newline at end of file diff --git a/tkinter_app/resources/log.txt b/tkinter_app/resources/log.txt new file mode 100644 index 0000000..14903b8 --- /dev/null +++ b/tkinter_app/resources/log.txt @@ -0,0 +1,833 @@ +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=192.168.1.74, host=, quick=8887779, port=5000 +[INFO] [SignageApp] Screen size set to 1920x1080 +[INFO] [SignageApp] MediaPlayer: Starting on_enter method. +[INFO] [SignageApp] MediaPlayer: Loaded server settings: server=192.168.1.74, host=, quick=8887779, port=5000 +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[WARNING] [SignageApp] python_functions: Local playlist file not found. +[INFO] [SignageApp] MediaPlayer: Loaded local playlist: [], Version: 0 +[WARNING] [SignageApp] MediaPlayer: No local playlist found. Attempting to load demo playlist... +[INFO] [SignageApp] MediaPlayer: Demo playlist loaded successfully. +[WARNING] [SignageApp] MediaPlayer: Invalid server settings. Using demo playlist. +[INFO] [SignageApp] MediaPlayer: Starting play_media method. +[INFO] [SignageApp] MediaPlayer: Playing media: Resurse/demo1.jpg +[INFO] [SignageApp] MediaPlayer: Starting log_event function. +2025-06-25 15:53:54 - STARTED: demo1.jpg +[INFO] [SignageApp] MediaPlayer: Logged event: 2025-06-25 15:53:54 - STARTED: demo1.jpg +[INFO] [SignageApp] MediaPlayer: Finished log_event function. +[INFO] [SignageApp] Showing image: Resurse/demo1.jpg +[INFO] [SignageApp] MediaPlayer: Finished play_media method. +[INFO] [SignageApp] MediaPlayer: Finished on_enter method. +[INFO] [SignageApp] SettingsScreen: Log messages loaded successfully. +[INFO] [SignageApp] SettingsScreen: Configuration saved. +[INFO] [SignageApp] MediaPlayer: Starting on_enter method. +[INFO] [SignageApp] MediaPlayer: Loaded server settings: server=192.168.1.74, host=rpi-tv11, quick=8887779, port=5000 +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[WARNING] [SignageApp] python_functions: Local playlist file not found. +[INFO] [SignageApp] MediaPlayer: Loaded local playlist: [], Version: 0 +[WARNING] [SignageApp] MediaPlayer: No local playlist found. Attempting to load demo playlist... +[INFO] [SignageApp] MediaPlayer: Demo playlist loaded successfully. +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.74:5000/api/playlists with params: {'hostname': '', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=192.168.1.74, host=rpi-tv11, quick=8887779, port=5000 +[INFO] [SignageApp] Screen size set to 1920x1080 +[INFO] [SignageApp] MediaPlayer: Starting on_enter method. +[INFO] [SignageApp] MediaPlayer: Loaded server settings: server=192.168.1.74, host=rpi-tv11, quick=8887779, port=5000 +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[WARNING] [SignageApp] python_functions: Local playlist file not found. +[INFO] [SignageApp] MediaPlayer: Loaded local playlist: [], Version: 0 +[WARNING] [SignageApp] MediaPlayer: No local playlist found. Attempting to load demo playlist... +[INFO] [SignageApp] MediaPlayer: Demo playlist loaded successfully. +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.74:5000/api/playlists with params: {'hostname': 'rpi-tv11', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=192.168.1.74, host=rpi-tv11, quick=8887779, port=5000 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[WARNING] [SignageApp] python_functions: Local playlist file not found. +[WARNING] [SignageApp] No local playlist found, creating demo content +[INFO] [SignageApp] Created demo playlist with 9 images +[INFO] [SignageApp] Playing media: left-arrow-green.png +2025-08-05 12:22:33 - STARTED: left-arrow-green.png +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] Playing media: pause.png +2025-08-05 12:22:38 - STARTED: pause.png +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Playing media: home_icon.png +2025-08-05 12:22:44 - STARTED: home_icon.png +[INFO] [SignageApp] Playing media: play.png +2025-08-05 12:22:49 - STARTED: play.png +[INFO] [SignageApp] Playing media: demo1.jpg +2025-08-05 12:22:54 - STARTED: demo1.jpg +[INFO] [SignageApp] Playing media: demo2.jpeg +2025-08-05 12:22:59 - STARTED: demo2.jpeg +[INFO] [SignageApp] Playing media: left-arrow-blue.png +2025-08-05 12:23:04 - STARTED: left-arrow-blue.png +[INFO] [SignageApp] Playing media: right-arrow-blue.png +2025-08-05 12:23:09 - STARTED: right-arrow-blue.png +[INFO] [SignageApp] Playing media: right-arrow-green.png +2025-08-05 12:23:15 - STARTED: right-arrow-green.png +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] Playing media: left-arrow-green.png +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +2025-08-05 12:23:20 - STARTED: left-arrow-green.png +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.74:5000/api/playlists with params: {'hostname': 'rpi-tv11', 'quickconnect_code': '8887779'} +[ERROR] [SignageApp] Failed to fetch playlist: HTTPConnectionPool(host='192.168.1.74', port=5000): Max retries exceeded with url: /api/playlists?hostname=rpi-tv11&quickconnect_code=8887779 (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 113] No route to host')) +[INFO] [SignageApp] Playing media: pause.png +2025-08-05 12:23:25 - STARTED: pause.png +[INFO] [SignageApp] Playing media: home_icon.png +2025-08-05 12:23:30 - STARTED: home_icon.png +[INFO] [SignageApp] Playing media: play.png +2025-08-05 12:23:35 - STARTED: play.png +[INFO] [SignageApp] Playing media: demo1.jpg +2025-08-05 12:23:40 - STARTED: demo1.jpg +[INFO] [SignageApp] Playing media: demo2.jpeg +2025-08-05 12:23:45 - STARTED: demo2.jpeg +[INFO] [SignageApp] Playing media: left-arrow-blue.png +2025-08-05 12:23:51 - STARTED: left-arrow-blue.png +[INFO] [SignageApp] Playing media: right-arrow-blue.png +2025-08-05 12:23:56 - STARTED: right-arrow-blue.png +[INFO] [SignageApp] Playing media: right-arrow-green.png +2025-08-05 12:24:01 - STARTED: right-arrow-green.png +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] Playing media: left-arrow-green.png +2025-08-05 12:24:06 - STARTED: left-arrow-green.png +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.74:5000/api/playlists with params: {'hostname': 'rpi-tv11', 'quickconnect_code': '8887779'} +[ERROR] [SignageApp] Failed to fetch playlist: HTTPConnectionPool(host='192.168.1.74', port=5000): Max retries exceeded with url: /api/playlists?hostname=rpi-tv11&quickconnect_code=8887779 (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 113] No route to host')) +[INFO] [SignageApp] Playing media: pause.png +2025-08-05 12:24:11 - STARTED: pause.png +[INFO] [SignageApp] Playing media: home_icon.png +2025-08-05 12:24:16 - STARTED: home_icon.png +[INFO] [SignageApp] Playing media: play.png +2025-08-05 12:24:21 - STARTED: play.png +[INFO] [SignageApp] Playing media: demo1.jpg +2025-08-05 12:24:26 - STARTED: demo1.jpg +[INFO] [SignageApp] Playing media: demo2.jpeg +2025-08-05 12:24:31 - STARTED: demo2.jpeg +[INFO] [SignageApp] Playing media: left-arrow-blue.png +2025-08-05 12:24:37 - STARTED: left-arrow-blue.png +[INFO] [SignageApp] Playing media: right-arrow-blue.png +2025-08-05 12:24:42 - STARTED: right-arrow-blue.png +[INFO] [SignageApp] Playing media: right-arrow-green.png +2025-08-05 12:24:47 - STARTED: right-arrow-green.png +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] Playing media: left-arrow-green.png +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +2025-08-05 12:24:52 - STARTED: left-arrow-green.png +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.74:5000/api/playlists with params: {'hostname': 'rpi-tv11', 'quickconnect_code': '8887779'} +[ERROR] [SignageApp] Failed to fetch playlist: HTTPConnectionPool(host='192.168.1.74', port=5000): Max retries exceeded with url: /api/playlists?hostname=rpi-tv11&quickconnect_code=8887779 (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 113] No route to host')) +[INFO] [SignageApp] Playing media: pause.png +2025-08-05 12:24:57 - STARTED: pause.png +[INFO] [SignageApp] Playing media: home_icon.png +2025-08-05 12:25:02 - STARTED: home_icon.png +[INFO] [SignageApp] Playing media: play.png +2025-08-05 12:25:07 - STARTED: play.png +[INFO] [SignageApp] Playing media: demo1.jpg +2025-08-05 12:25:12 - STARTED: demo1.jpg +[INFO] [SignageApp] Playing media: demo2.jpeg +2025-08-05 12:25:17 - STARTED: demo2.jpeg +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Configuration saved via settings window +[INFO] [SignageApp] Playing media: left-arrow-blue.png +2025-08-05 12:25:23 - STARTED: left-arrow-blue.png +[INFO] [SignageApp] Playing media: right-arrow-blue.png +2025-08-05 12:25:28 - STARTED: right-arrow-blue.png +[INFO] [SignageApp] Playing media: right-arrow-green.png +2025-08-05 12:25:33 - STARTED: right-arrow-green.png +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] Playing media: left-arrow-green.png +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +2025-08-05 12:25:38 - STARTED: left-arrow-green.png +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.74:5000/api/playlists with params: {'hostname': 'rpi-tv11', 'quickconnect_code': '8887779'} +[ERROR] [SignageApp] Failed to fetch playlist: HTTPConnectionPool(host='192.168.1.74', port=5000): Max retries exceeded with url: /api/playlists?hostname=rpi-tv11&quickconnect_code=8887779 (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 113] No route to host')) +[INFO] [SignageApp] Playing media: pause.png +2025-08-05 12:25:43 - STARTED: pause.png +[INFO] [SignageApp] Playing media: home_icon.png +2025-08-05 12:25:48 - STARTED: home_icon.png +[INFO] [SignageApp] Playing media: play.png +2025-08-05 12:25:50 - STARTED: play.png +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Playing media: demo1.jpg +2025-08-05 12:25:55 - STARTED: demo1.jpg +[INFO] [SignageApp] Playing media: demo2.jpeg +2025-08-05 12:26:01 - STARTED: demo2.jpeg +[INFO] [SignageApp] Playing media: left-arrow-blue.png +2025-08-05 12:26:06 - STARTED: left-arrow-blue.png +[INFO] [SignageApp] Application exit requested +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=192.168.1.245, host=tv-holba1, quick=8887779, port=5000 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[WARNING] [SignageApp] python_functions: Local playlist file not found. +[WARNING] [SignageApp] No local playlist found, creating demo content +[INFO] [SignageApp] Created demo playlist with 9 images +[INFO] [SignageApp] Playing media: left-arrow-green.png +2025-08-05 12:29:21 - STARTED: left-arrow-green.png +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] Playing media: pause.png +2025-08-05 12:29:26 - STARTED: pause.png +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Playing media: home_icon.png +2025-08-05 12:29:31 - STARTED: home_icon.png +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Playing media: play.png +2025-08-05 12:29:36 - STARTED: play.png +[INFO] [SignageApp] Playing media: demo1.jpg +2025-08-05 12:29:41 - STARTED: demo1.jpg +[INFO] [SignageApp] Application exit requested +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=192.168.1.245, host=tv-holba1, quick=8887779, port=5000 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=192.168.1.245, host=tv-holba1, quick=8887779, port=5000 +[INFO] [SignageApp] python_functions: Starting load_config function. +[ERROR] [SignageApp] python_functions: Configuration file ./Resurse/app_config.txt not found. +[INFO] [SignageApp] python_functions: Configuration loaded: server=, host=, quick=, port= +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=192.168.1.245, host=tv-holba1, quick=8887779, port=5000 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Initializing with settings: server=192.168.1.245, host=tv-holba1, port=5000 +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$ReP1AQ/YknPWyoFLGwSgH.80kBv.Bm13XdRUTALyA6vD20T1vEOl.', 'playlist': [{'duration': 20, 'file_name': 'har_page_001.jpg', 'url': 'http://192.168.1.245:5000/media/har_page_001.jpg'}, {'duration': 20, 'file_name': 'har_page_002.jpg', 'url': 'http://192.168.1.245:5000/media/har_page_002.jpg'}, {'duration': 20, 'file_name': 'har_page_003.jpg', 'url': 'http://192.168.1.245:5000/media/har_page_003.jpg'}, {'duration': 20, 'file_name': 'har_page_004.jpg', 'url': 'http://192.168.1.245:5000/media/har_page_004.jpg'}, {'duration': 20, 'file_name': 'har_page_005.jpg', 'url': 'http://192.168.1.245:5000/media/har_page_005.jpg'}, {'duration': 20, 'file_name': 'har_page_006.jpg', 'url': 'http://192.168.1.245:5000/media/har_page_006.jpg'}, {'duration': 20, 'file_name': 'har_page_007.jpg', 'url': 'http://192.168.1.245:5000/media/har_page_007.jpg'}, {'duration': 20, 'file_name': 'har_page_008.jpg', 'url': 'http://192.168.1.245:5000/media/har_page_008.jpg'}, {'duration': 20, 'file_name': 'har_page_009.jpg', 'url': 'http://192.168.1.245:5000/media/har_page_009.jpg'}, {'duration': 20, 'file_name': 'har_page_010.jpg', 'url': 'http://192.168.1.245:5000/media/har_page_010.jpg'}], 'playlist_version': 4} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 4. +[INFO] [SignageApp] Server playlist found with 10 items, version 4 +[INFO] [SignageApp] python_functions: Starting media file download... +[INFO] [SignageApp] python_functions: Created directory /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse for media files. +[INFO] [SignageApp] python_functions: Successfully downloaded har_page_001.jpg to /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/har_page_001.jpg +[INFO] [SignageApp] python_functions: Successfully downloaded har_page_002.jpg to /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/har_page_002.jpg +[INFO] [SignageApp] python_functions: Successfully downloaded har_page_003.jpg to /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/har_page_003.jpg +[INFO] [SignageApp] python_functions: Successfully downloaded har_page_004.jpg to /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/har_page_004.jpg +[INFO] [SignageApp] python_functions: Successfully downloaded har_page_005.jpg to /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/har_page_005.jpg +[INFO] [SignageApp] python_functions: Successfully downloaded har_page_006.jpg to /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/har_page_006.jpg +[INFO] [SignageApp] python_functions: Successfully downloaded har_page_007.jpg to /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/har_page_007.jpg +[INFO] [SignageApp] python_functions: Successfully downloaded har_page_008.jpg to /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/har_page_008.jpg +[INFO] [SignageApp] python_functions: Successfully downloaded har_page_009.jpg to /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/har_page_009.jpg +[INFO] [SignageApp] python_functions: Successfully downloaded har_page_010.jpg to /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/har_page_010.jpg +[INFO] [SignageApp] python_functions: Starting save_local_playlist function. +[INFO] [SignageApp] python_functions: Updated local playlist with server data. +[INFO] [SignageApp] python_functions: Finished save_local_playlist function. +[INFO] [SignageApp] python_functions: Finished media file download and updated local playlist. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 4. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': 'har_page_001.jpg', 'url': 'static/resurse/har_page_001.jpg', 'duration': 20}, {'file_name': 'har_page_002.jpg', 'url': 'static/resurse/har_page_002.jpg', 'duration': 20}, {'file_name': 'har_page_003.jpg', 'url': 'static/resurse/har_page_003.jpg', 'duration': 20}, {'file_name': 'har_page_004.jpg', 'url': 'static/resurse/har_page_004.jpg', 'duration': 20}, {'file_name': 'har_page_005.jpg', 'url': 'static/resurse/har_page_005.jpg', 'duration': 20}, {'file_name': 'har_page_006.jpg', 'url': 'static/resurse/har_page_006.jpg', 'duration': 20}, {'file_name': 'har_page_007.jpg', 'url': 'static/resurse/har_page_007.jpg', 'duration': 20}, {'file_name': 'har_page_008.jpg', 'url': 'static/resurse/har_page_008.jpg', 'duration': 20}, {'file_name': 'har_page_009.jpg', 'url': 'static/resurse/har_page_009.jpg', 'duration': 20}, {'file_name': 'har_page_010.jpg', 'url': 'static/resurse/har_page_010.jpg', 'duration': 20}], 'version': 4} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Successfully loaded 10 items from server +[INFO] [SignageApp] Playing media: har_page_001.jpg from static/resurse/har_page_001.jpg +2025-08-05 13:52:51 - STARTED: har_page_001.jpg +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$ReP1AQ/YknPWyoFLGwSgH.80kBv.Bm13XdRUTALyA6vD20T1vEOl.', 'playlist': [{'duration': 20, 'file_name': 'har_page_001.jpg', 'url': 'http://192.168.1.245:5000/media/har_page_001.jpg'}, {'duration': 20, 'file_name': 'har_page_002.jpg', 'url': 'http://192.168.1.245:5000/media/har_page_002.jpg'}, {'duration': 20, 'file_name': 'har_page_003.jpg', 'url': 'http://192.168.1.245:5000/media/har_page_003.jpg'}, {'duration': 20, 'file_name': 'har_page_004.jpg', 'url': 'http://192.168.1.245:5000/media/har_page_004.jpg'}, {'duration': 20, 'file_name': 'har_page_005.jpg', 'url': 'http://192.168.1.245:5000/media/har_page_005.jpg'}, {'duration': 20, 'file_name': 'har_page_006.jpg', 'url': 'http://192.168.1.245:5000/media/har_page_006.jpg'}, {'duration': 20, 'file_name': 'har_page_007.jpg', 'url': 'http://192.168.1.245:5000/media/har_page_007.jpg'}, {'duration': 20, 'file_name': 'har_page_008.jpg', 'url': 'http://192.168.1.245:5000/media/har_page_008.jpg'}, {'duration': 20, 'file_name': 'har_page_009.jpg', 'url': 'http://192.168.1.245:5000/media/har_page_009.jpg'}, {'duration': 20, 'file_name': 'har_page_010.jpg', 'url': 'http://192.168.1.245:5000/media/har_page_010.jpg'}], 'playlist_version': 4} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 4. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': 'har_page_001.jpg', 'url': 'static/resurse/har_page_001.jpg', 'duration': 20}, {'file_name': 'har_page_002.jpg', 'url': 'static/resurse/har_page_002.jpg', 'duration': 20}, {'file_name': 'har_page_003.jpg', 'url': 'static/resurse/har_page_003.jpg', 'duration': 20}, {'file_name': 'har_page_004.jpg', 'url': 'static/resurse/har_page_004.jpg', 'duration': 20}, {'file_name': 'har_page_005.jpg', 'url': 'static/resurse/har_page_005.jpg', 'duration': 20}, {'file_name': 'har_page_006.jpg', 'url': 'static/resurse/har_page_006.jpg', 'duration': 20}, {'file_name': 'har_page_007.jpg', 'url': 'static/resurse/har_page_007.jpg', 'duration': 20}, {'file_name': 'har_page_008.jpg', 'url': 'static/resurse/har_page_008.jpg', 'duration': 20}, {'file_name': 'har_page_009.jpg', 'url': 'static/resurse/har_page_009.jpg', 'duration': 20}, {'file_name': 'har_page_010.jpg', 'url': 'static/resurse/har_page_010.jpg', 'duration': 20}], 'version': 4} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] python_functions: Cleaning unused media files... +[INFO] [SignageApp] python_functions: Starting media file download... +[INFO] [SignageApp] python_functions: File har_page_001.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File har_page_002.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File har_page_003.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File har_page_004.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File har_page_005.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File har_page_006.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File har_page_007.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File har_page_008.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File har_page_009.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File har_page_010.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: Starting save_local_playlist function. +[INFO] [SignageApp] python_functions: Updated local playlist with server data. +[INFO] [SignageApp] python_functions: Finished save_local_playlist function. +[INFO] [SignageApp] python_functions: Finished media file download and updated local playlist. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 4. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': 'har_page_001.jpg', 'url': 'static/resurse/har_page_001.jpg', 'duration': 20}, {'file_name': 'har_page_002.jpg', 'url': 'static/resurse/har_page_002.jpg', 'duration': 20}, {'file_name': 'har_page_003.jpg', 'url': 'static/resurse/har_page_003.jpg', 'duration': 20}, {'file_name': 'har_page_004.jpg', 'url': 'static/resurse/har_page_004.jpg', 'duration': 20}, {'file_name': 'har_page_005.jpg', 'url': 'static/resurse/har_page_005.jpg', 'duration': 20}, {'file_name': 'har_page_006.jpg', 'url': 'static/resurse/har_page_006.jpg', 'duration': 20}, {'file_name': 'har_page_007.jpg', 'url': 'static/resurse/har_page_007.jpg', 'duration': 20}, {'file_name': 'har_page_008.jpg', 'url': 'static/resurse/har_page_008.jpg', 'duration': 20}, {'file_name': 'har_page_009.jpg', 'url': 'static/resurse/har_page_009.jpg', 'duration': 20}, {'file_name': 'har_page_010.jpg', 'url': 'static/resurse/har_page_010.jpg', 'duration': 20}], 'version': 4} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Playing media: har_page_001.jpg from static/resurse/har_page_001.jpg +2025-08-05 13:53:08 - STARTED: har_page_001.jpg +[INFO] [SignageApp] Playing media: har_page_002.jpg from static/resurse/har_page_002.jpg +2025-08-05 13:53:15 - STARTED: har_page_002.jpg +[INFO] [SignageApp] Playing media: har_page_003.jpg from static/resurse/har_page_003.jpg +2025-08-05 13:53:17 - STARTED: har_page_003.jpg +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Application exit requested +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=192.168.1.245, host=tv-holba1, quick=8887779, port=5000 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Initializing with settings: server=192.168.1.245, host=tv-holba1, port=5000 +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$ReP1AQ/YknPWyoFLGwSgH.80kBv.Bm13XdRUTALyA6vD20T1vEOl.', 'playlist': [{'duration': 20, 'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'http://192.168.1.245:5000/media/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg'}, {'duration': 15, 'file_name': 'wp2782770-1846651530.jpg', 'url': 'http://192.168.1.245:5000/media/wp2782770-1846651530.jpg'}], 'playlist_version': 5} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] Server playlist found with 2 items, version 5 +[INFO] [SignageApp] python_functions: Starting media file download... +[INFO] [SignageApp] python_functions: Successfully downloaded 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg to /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] python_functions: Successfully downloaded wp2782770-1846651530.jpg to /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/wp2782770-1846651530.jpg +[INFO] [SignageApp] python_functions: Starting save_local_playlist function. +[INFO] [SignageApp] python_functions: Updated local playlist with server data. +[INFO] [SignageApp] python_functions: Finished save_local_playlist function. +[INFO] [SignageApp] python_functions: Finished media file download and updated local playlist. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'duration': 20}, {'file_name': 'wp2782770-1846651530.jpg', 'url': 'static/resurse/wp2782770-1846651530.jpg', 'duration': 15}], 'version': 5} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Successfully loaded 2 items from server +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +2025-08-05 13:55:59 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] Playing media: wp2782770-1846651530.jpg from static/resurse/wp2782770-1846651530.jpg +2025-08-05 13:56:19 - STARTED: wp2782770-1846651530.jpg +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Application exit requested +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=192.168.1.245, host=tv-holba1, quick=8887779, port=5000 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Initializing with settings: server=192.168.1.245, host=tv-holba1, port=5000 +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$ReP1AQ/YknPWyoFLGwSgH.80kBv.Bm13XdRUTALyA6vD20T1vEOl.', 'playlist': [{'duration': 20, 'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'http://192.168.1.245:5000/media/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg'}, {'duration': 15, 'file_name': 'wp2782770-1846651530.jpg', 'url': 'http://192.168.1.245:5000/media/wp2782770-1846651530.jpg'}], 'playlist_version': 5} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] Server playlist found with 2 items, version 5 +[INFO] [SignageApp] python_functions: Starting media file download... +[INFO] [SignageApp] python_functions: File 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File wp2782770-1846651530.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: Starting save_local_playlist function. +[INFO] [SignageApp] python_functions: Updated local playlist with server data. +[INFO] [SignageApp] python_functions: Finished save_local_playlist function. +[INFO] [SignageApp] python_functions: Finished media file download and updated local playlist. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'duration': 20}, {'file_name': 'wp2782770-1846651530.jpg', 'url': 'static/resurse/wp2782770-1846651530.jpg', 'duration': 15}], 'version': 5} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Successfully loaded 2 items from server +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +2025-08-05 14:03:41 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Playing media: wp2782770-1846651530.jpg from static/resurse/wp2782770-1846651530.jpg +2025-08-05 14:04:01 - STARTED: wp2782770-1846651530.jpg +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Application exit requested +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=192.168.1.245, host=tv-holba1, quick=8887779, port=5000 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Initializing with settings: server=192.168.1.245, host=tv-holba1, port=5000 +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$ReP1AQ/YknPWyoFLGwSgH.80kBv.Bm13XdRUTALyA6vD20T1vEOl.', 'playlist': [{'duration': 20, 'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'http://192.168.1.245:5000/media/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg'}, {'duration': 15, 'file_name': 'wp2782770-1846651530.jpg', 'url': 'http://192.168.1.245:5000/media/wp2782770-1846651530.jpg'}], 'playlist_version': 5} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] Server playlist found with 2 items, version 5 +[INFO] [SignageApp] python_functions: Starting media file download... +[INFO] [SignageApp] python_functions: File 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File wp2782770-1846651530.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: Starting save_local_playlist function. +[INFO] [SignageApp] python_functions: Updated local playlist with server data. +[INFO] [SignageApp] python_functions: Finished save_local_playlist function. +[INFO] [SignageApp] python_functions: Finished media file download and updated local playlist. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'duration': 20}, {'file_name': 'wp2782770-1846651530.jpg', 'url': 'static/resurse/wp2782770-1846651530.jpg', 'duration': 15}], 'version': 5} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Successfully loaded 2 items from server +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +2025-08-05 14:09:20 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] Playing media: wp2782770-1846651530.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/wp2782770-1846651530.jpg +2025-08-05 14:09:40 - STARTED: wp2782770-1846651530.jpg +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +2025-08-05 14:09:56 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$ReP1AQ/YknPWyoFLGwSgH.80kBv.Bm13XdRUTALyA6vD20T1vEOl.', 'playlist': [{'duration': 20, 'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'http://192.168.1.245:5000/media/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg'}, {'duration': 15, 'file_name': 'wp2782770-1846651530.jpg', 'url': 'http://192.168.1.245:5000/media/wp2782770-1846651530.jpg'}], 'playlist_version': 5} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] No playlist updates available +[INFO] [SignageApp] Playing media: wp2782770-1846651530.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/wp2782770-1846651530.jpg +2025-08-05 14:10:16 - STARTED: wp2782770-1846651530.jpg +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +2025-08-05 14:10:32 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$ReP1AQ/YknPWyoFLGwSgH.80kBv.Bm13XdRUTALyA6vD20T1vEOl.', 'playlist': [{'duration': 20, 'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'http://192.168.1.245:5000/media/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg'}, {'duration': 15, 'file_name': 'wp2782770-1846651530.jpg', 'url': 'http://192.168.1.245:5000/media/wp2782770-1846651530.jpg'}], 'playlist_version': 5} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] No playlist updates available +[INFO] [SignageApp] Playing media: wp2782770-1846651530.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/wp2782770-1846651530.jpg +2025-08-05 14:10:52 - STARTED: wp2782770-1846651530.jpg +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +2025-08-05 14:11:09 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$ReP1AQ/YknPWyoFLGwSgH.80kBv.Bm13XdRUTALyA6vD20T1vEOl.', 'playlist': [{'duration': 20, 'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'http://192.168.1.245:5000/media/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg'}, {'duration': 15, 'file_name': 'wp2782770-1846651530.jpg', 'url': 'http://192.168.1.245:5000/media/wp2782770-1846651530.jpg'}], 'playlist_version': 5} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] No playlist updates available +[INFO] [SignageApp] Playing media: wp2782770-1846651530.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/wp2782770-1846651530.jpg +2025-08-05 14:11:29 - STARTED: wp2782770-1846651530.jpg +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +2025-08-05 14:11:45 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$ReP1AQ/YknPWyoFLGwSgH.80kBv.Bm13XdRUTALyA6vD20T1vEOl.', 'playlist': [{'duration': 20, 'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'http://192.168.1.245:5000/media/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg'}, {'duration': 15, 'file_name': 'wp2782770-1846651530.jpg', 'url': 'http://192.168.1.245:5000/media/wp2782770-1846651530.jpg'}], 'playlist_version': 5} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] No playlist updates available +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Playing media: wp2782770-1846651530.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/wp2782770-1846651530.jpg +2025-08-05 14:12:05 - STARTED: wp2782770-1846651530.jpg +[INFO] [SignageApp] Application exit requested +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=192.168.1.245, host=tv-holba1, quick=8887779, port=5000 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Initializing with settings: server=192.168.1.245, host=tv-holba1, port=5000 +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$ReP1AQ/YknPWyoFLGwSgH.80kBv.Bm13XdRUTALyA6vD20T1vEOl.', 'playlist': [{'duration': 20, 'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'http://192.168.1.245:5000/media/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg'}, {'duration': 15, 'file_name': 'wp2782770-1846651530.jpg', 'url': 'http://192.168.1.245:5000/media/wp2782770-1846651530.jpg'}, {'duration': 5, 'file_name': 'SampleVideo_1280x720_1mb.mp4', 'url': 'http://192.168.1.245:5000/media/SampleVideo_1280x720_1mb.mp4'}], 'playlist_version': 5} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] Server playlist found with 3 items, version 5 +[INFO] [SignageApp] python_functions: Starting media file download... +[INFO] [SignageApp] python_functions: File 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File wp2782770-1846651530.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: Successfully downloaded SampleVideo_1280x720_1mb.mp4 to /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/SampleVideo_1280x720_1mb.mp4 +[INFO] [SignageApp] python_functions: Starting save_local_playlist function. +[INFO] [SignageApp] python_functions: Updated local playlist with server data. +[INFO] [SignageApp] python_functions: Finished save_local_playlist function. +[INFO] [SignageApp] python_functions: Finished media file download and updated local playlist. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'duration': 20}, {'file_name': 'wp2782770-1846651530.jpg', 'url': 'static/resurse/wp2782770-1846651530.jpg', 'duration': 15}, {'file_name': 'SampleVideo_1280x720_1mb.mp4', 'url': 'static/resurse/SampleVideo_1280x720_1mb.mp4', 'duration': 5}], 'version': 5} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Successfully loaded 3 items from server +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +2025-08-05 14:24:44 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] Playing media: wp2782770-1846651530.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/wp2782770-1846651530.jpg +2025-08-05 14:25:04 - STARTED: wp2782770-1846651530.jpg +[INFO] [SignageApp] Playing media: SampleVideo_1280x720_1mb.mp4 from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/SampleVideo_1280x720_1mb.mp4 +2025-08-05 14:25:20 - STARTED: SampleVideo_1280x720_1mb.mp4 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +2025-08-05 14:25:25 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$ReP1AQ/YknPWyoFLGwSgH.80kBv.Bm13XdRUTALyA6vD20T1vEOl.', 'playlist': [{'duration': 20, 'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'http://192.168.1.245:5000/media/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg'}, {'duration': 15, 'file_name': 'wp2782770-1846651530.jpg', 'url': 'http://192.168.1.245:5000/media/wp2782770-1846651530.jpg'}, {'duration': 5, 'file_name': 'SampleVideo_1280x720_1mb.mp4', 'url': 'http://192.168.1.245:5000/media/SampleVideo_1280x720_1mb.mp4'}], 'playlist_version': 5} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] No playlist updates available +[INFO] [SignageApp] Playing media: wp2782770-1846651530.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/wp2782770-1846651530.jpg +2025-08-05 14:25:46 - STARTED: wp2782770-1846651530.jpg +[INFO] [SignageApp] Playing media: SampleVideo_1280x720_1mb.mp4 from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/SampleVideo_1280x720_1mb.mp4 +2025-08-05 14:26:02 - STARTED: SampleVideo_1280x720_1mb.mp4 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +2025-08-05 14:26:07 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$ReP1AQ/YknPWyoFLGwSgH.80kBv.Bm13XdRUTALyA6vD20T1vEOl.', 'playlist': [{'duration': 20, 'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'http://192.168.1.245:5000/media/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg'}, {'duration': 15, 'file_name': 'wp2782770-1846651530.jpg', 'url': 'http://192.168.1.245:5000/media/wp2782770-1846651530.jpg'}, {'duration': 5, 'file_name': 'SampleVideo_1280x720_1mb.mp4', 'url': 'http://192.168.1.245:5000/media/SampleVideo_1280x720_1mb.mp4'}], 'playlist_version': 5} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] No playlist updates available +[INFO] [SignageApp] Application exit requested +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=192.168.1.245, host=tv-holba1, quick=8887779, port=5000 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Initializing with settings: server=192.168.1.245, host=tv-holba1, port=5000 +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$ReP1AQ/YknPWyoFLGwSgH.80kBv.Bm13XdRUTALyA6vD20T1vEOl.', 'playlist': [{'duration': 20, 'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'http://192.168.1.245:5000/media/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg'}, {'duration': 15, 'file_name': 'wp2782770-1846651530.jpg', 'url': 'http://192.168.1.245:5000/media/wp2782770-1846651530.jpg'}, {'duration': 5, 'file_name': 'SampleVideo_1280x720_1mb.mp4', 'url': 'http://192.168.1.245:5000/media/SampleVideo_1280x720_1mb.mp4'}], 'playlist_version': 5} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] Server playlist found with 3 items, version 5 +[INFO] [SignageApp] python_functions: Starting media file download... +[INFO] [SignageApp] python_functions: File 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File wp2782770-1846651530.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File SampleVideo_1280x720_1mb.mp4 already exists. Skipping download. +[INFO] [SignageApp] python_functions: Starting save_local_playlist function. +[INFO] [SignageApp] python_functions: Updated local playlist with server data. +[INFO] [SignageApp] python_functions: Finished save_local_playlist function. +[INFO] [SignageApp] python_functions: Finished media file download and updated local playlist. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'duration': 20}, {'file_name': 'wp2782770-1846651530.jpg', 'url': 'static/resurse/wp2782770-1846651530.jpg', 'duration': 15}, {'file_name': 'SampleVideo_1280x720_1mb.mp4', 'url': 'static/resurse/SampleVideo_1280x720_1mb.mp4', 'duration': 5}], 'version': 5} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Successfully loaded 3 items from server +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +2025-08-05 14:40:29 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] Playing media: wp2782770-1846651530.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/wp2782770-1846651530.jpg +2025-08-05 14:40:50 - STARTED: wp2782770-1846651530.jpg +[INFO] [SignageApp] Playing media: SampleVideo_1280x720_1mb.mp4 from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/SampleVideo_1280x720_1mb.mp4 +2025-08-05 14:41:06 - STARTED: SampleVideo_1280x720_1mb.mp4 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +2025-08-05 14:41:27 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$ReP1AQ/YknPWyoFLGwSgH.80kBv.Bm13XdRUTALyA6vD20T1vEOl.', 'playlist': [{'duration': 20, 'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'http://192.168.1.245:5000/media/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg'}, {'duration': 15, 'file_name': 'wp2782770-1846651530.jpg', 'url': 'http://192.168.1.245:5000/media/wp2782770-1846651530.jpg'}, {'duration': 5, 'file_name': 'SampleVideo_1280x720_1mb.mp4', 'url': 'http://192.168.1.245:5000/media/SampleVideo_1280x720_1mb.mp4'}], 'playlist_version': 5} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] No playlist updates available +[INFO] [SignageApp] Playing media: wp2782770-1846651530.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/wp2782770-1846651530.jpg +2025-08-05 14:41:47 - STARTED: wp2782770-1846651530.jpg +[INFO] [SignageApp] Playing media: SampleVideo_1280x720_1mb.mp4 from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/SampleVideo_1280x720_1mb.mp4 +2025-08-05 14:42:03 - STARTED: SampleVideo_1280x720_1mb.mp4 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +2025-08-05 14:42:25 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$ReP1AQ/YknPWyoFLGwSgH.80kBv.Bm13XdRUTALyA6vD20T1vEOl.', 'playlist': [{'duration': 20, 'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'http://192.168.1.245:5000/media/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg'}, {'duration': 15, 'file_name': 'wp2782770-1846651530.jpg', 'url': 'http://192.168.1.245:5000/media/wp2782770-1846651530.jpg'}, {'duration': 5, 'file_name': 'SampleVideo_1280x720_1mb.mp4', 'url': 'http://192.168.1.245:5000/media/SampleVideo_1280x720_1mb.mp4'}], 'playlist_version': 5} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] No playlist updates available +[INFO] [SignageApp] Playing media: wp2782770-1846651530.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/wp2782770-1846651530.jpg +2025-08-05 14:42:45 - STARTED: wp2782770-1846651530.jpg +[INFO] [SignageApp] Playing media: SampleVideo_1280x720_1mb.mp4 from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/SampleVideo_1280x720_1mb.mp4 +2025-08-05 14:43:01 - STARTED: SampleVideo_1280x720_1mb.mp4 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +2025-08-05 14:43:21 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$ReP1AQ/YknPWyoFLGwSgH.80kBv.Bm13XdRUTALyA6vD20T1vEOl.', 'playlist': [{'duration': 20, 'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'http://192.168.1.245:5000/media/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg'}, {'duration': 15, 'file_name': 'wp2782770-1846651530.jpg', 'url': 'http://192.168.1.245:5000/media/wp2782770-1846651530.jpg'}, {'duration': 5, 'file_name': 'SampleVideo_1280x720_1mb.mp4', 'url': 'http://192.168.1.245:5000/media/SampleVideo_1280x720_1mb.mp4'}], 'playlist_version': 5} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] No playlist updates available +[INFO] [SignageApp] Playing media: wp2782770-1846651530.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/wp2782770-1846651530.jpg +2025-08-05 14:43:42 - STARTED: wp2782770-1846651530.jpg +[INFO] [SignageApp] Playing media: SampleVideo_1280x720_1mb.mp4 from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/SampleVideo_1280x720_1mb.mp4 +2025-08-05 14:43:58 - STARTED: SampleVideo_1280x720_1mb.mp4 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +2025-08-05 14:44:18 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$ReP1AQ/YknPWyoFLGwSgH.80kBv.Bm13XdRUTALyA6vD20T1vEOl.', 'playlist': [{'duration': 20, 'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'http://192.168.1.245:5000/media/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg'}, {'duration': 15, 'file_name': 'wp2782770-1846651530.jpg', 'url': 'http://192.168.1.245:5000/media/wp2782770-1846651530.jpg'}, {'duration': 5, 'file_name': 'SampleVideo_1280x720_1mb.mp4', 'url': 'http://192.168.1.245:5000/media/SampleVideo_1280x720_1mb.mp4'}], 'playlist_version': 5} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] No playlist updates available +[INFO] [SignageApp] Playing media: wp2782770-1846651530.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/wp2782770-1846651530.jpg +2025-08-05 14:44:38 - STARTED: wp2782770-1846651530.jpg +[INFO] [SignageApp] Playing media: SampleVideo_1280x720_1mb.mp4 from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/SampleVideo_1280x720_1mb.mp4 +2025-08-05 14:44:54 - STARTED: SampleVideo_1280x720_1mb.mp4 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +2025-08-05 14:45:15 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$ReP1AQ/YknPWyoFLGwSgH.80kBv.Bm13XdRUTALyA6vD20T1vEOl.', 'playlist': [{'duration': 20, 'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'http://192.168.1.245:5000/media/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg'}, {'duration': 15, 'file_name': 'wp2782770-1846651530.jpg', 'url': 'http://192.168.1.245:5000/media/wp2782770-1846651530.jpg'}, {'duration': 5, 'file_name': 'SampleVideo_1280x720_1mb.mp4', 'url': 'http://192.168.1.245:5000/media/SampleVideo_1280x720_1mb.mp4'}], 'playlist_version': 5} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] No playlist updates available +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Application exit requested +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=192.168.1.245, host=tv-holba1, quick=8887779, port=5000 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Initializing with settings: server=192.168.1.245, host=tv-holba1, port=5000 +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$ReP1AQ/YknPWyoFLGwSgH.80kBv.Bm13XdRUTALyA6vD20T1vEOl.', 'playlist': [{'duration': 20, 'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'http://192.168.1.245:5000/media/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg'}, {'duration': 15, 'file_name': 'wp2782770-1846651530.jpg', 'url': 'http://192.168.1.245:5000/media/wp2782770-1846651530.jpg'}, {'duration': 5, 'file_name': 'SampleVideo_1280x720_1mb.mp4', 'url': 'http://192.168.1.245:5000/media/SampleVideo_1280x720_1mb.mp4'}], 'playlist_version': 5} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] Server playlist found with 3 items, version 5 +[INFO] [SignageApp] python_functions: Starting media file download... +[INFO] [SignageApp] python_functions: File 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File wp2782770-1846651530.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File SampleVideo_1280x720_1mb.mp4 already exists. Skipping download. +[INFO] [SignageApp] python_functions: Starting save_local_playlist function. +[INFO] [SignageApp] python_functions: Updated local playlist with server data. +[INFO] [SignageApp] python_functions: Finished save_local_playlist function. +[INFO] [SignageApp] python_functions: Finished media file download and updated local playlist. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'duration': 20}, {'file_name': 'wp2782770-1846651530.jpg', 'url': 'static/resurse/wp2782770-1846651530.jpg', 'duration': 15}, {'file_name': 'SampleVideo_1280x720_1mb.mp4', 'url': 'static/resurse/SampleVideo_1280x720_1mb.mp4', 'duration': 5}], 'version': 5} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Successfully loaded 3 items from server +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +2025-08-05 15:13:11 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] Playing media: wp2782770-1846651530.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/wp2782770-1846651530.jpg +2025-08-05 15:13:31 - STARTED: wp2782770-1846651530.jpg +[INFO] [SignageApp] Playing media: SampleVideo_1280x720_1mb.mp4 from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/SampleVideo_1280x720_1mb.mp4 +2025-08-05 15:13:47 - STARTED: SampleVideo_1280x720_1mb.mp4 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +2025-08-05 15:14:08 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$ReP1AQ/YknPWyoFLGwSgH.80kBv.Bm13XdRUTALyA6vD20T1vEOl.', 'playlist': [{'duration': 20, 'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'http://192.168.1.245:5000/media/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg'}, {'duration': 15, 'file_name': 'wp2782770-1846651530.jpg', 'url': 'http://192.168.1.245:5000/media/wp2782770-1846651530.jpg'}, {'duration': 5, 'file_name': 'SampleVideo_1280x720_1mb.mp4', 'url': 'http://192.168.1.245:5000/media/SampleVideo_1280x720_1mb.mp4'}], 'playlist_version': 5} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] No playlist updates available +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Application exit requested +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=192.168.1.245, host=tv-holba1, quick=8887779, port=5000 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Initializing with settings: server=192.168.1.245, host=tv-holba1, port=5000 +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$ReP1AQ/YknPWyoFLGwSgH.80kBv.Bm13XdRUTALyA6vD20T1vEOl.', 'playlist': [{'duration': 20, 'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'http://192.168.1.245:5000/media/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg'}, {'duration': 15, 'file_name': 'wp2782770-1846651530.jpg', 'url': 'http://192.168.1.245:5000/media/wp2782770-1846651530.jpg'}, {'duration': 5, 'file_name': 'SampleVideo_1280x720_1mb.mp4', 'url': 'http://192.168.1.245:5000/media/SampleVideo_1280x720_1mb.mp4'}], 'playlist_version': 5} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] Server playlist found with 3 items, version 5 +[INFO] [SignageApp] python_functions: Starting media file download... +[INFO] [SignageApp] python_functions: File 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File wp2782770-1846651530.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File SampleVideo_1280x720_1mb.mp4 already exists. Skipping download. +[INFO] [SignageApp] python_functions: Starting save_local_playlist function. +[INFO] [SignageApp] python_functions: Updated local playlist with server data. +[INFO] [SignageApp] python_functions: Finished save_local_playlist function. +[INFO] [SignageApp] python_functions: Finished media file download and updated local playlist. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'duration': 20}, {'file_name': 'wp2782770-1846651530.jpg', 'url': 'static/resurse/wp2782770-1846651530.jpg', 'duration': 15}, {'file_name': 'SampleVideo_1280x720_1mb.mp4', 'url': 'static/resurse/SampleVideo_1280x720_1mb.mp4', 'duration': 5}], 'version': 5} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Successfully loaded 3 items from server +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +2025-08-05 15:34:34 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Application exit requested +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=192.168.1.245, host=tv-holba1, quick=8887779, port=5000 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Initializing with settings: server=192.168.1.245, host=tv-holba1, port=5000 +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$ReP1AQ/YknPWyoFLGwSgH.80kBv.Bm13XdRUTALyA6vD20T1vEOl.', 'playlist': [{'duration': 20, 'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'http://192.168.1.245:5000/media/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg'}, {'duration': 15, 'file_name': 'wp2782770-1846651530.jpg', 'url': 'http://192.168.1.245:5000/media/wp2782770-1846651530.jpg'}, {'duration': 5, 'file_name': 'SampleVideo_1280x720_1mb.mp4', 'url': 'http://192.168.1.245:5000/media/SampleVideo_1280x720_1mb.mp4'}], 'playlist_version': 5} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] Server playlist found with 3 items, version 5 +[INFO] [SignageApp] python_functions: Starting media file download... +[INFO] [SignageApp] python_functions: File 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File wp2782770-1846651530.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File SampleVideo_1280x720_1mb.mp4 already exists. Skipping download. +[INFO] [SignageApp] python_functions: Starting save_local_playlist function. +[INFO] [SignageApp] python_functions: Updated local playlist with server data. +[INFO] [SignageApp] python_functions: Finished save_local_playlist function. +[INFO] [SignageApp] python_functions: Finished media file download and updated local playlist. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 5. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'duration': 20}, {'file_name': 'wp2782770-1846651530.jpg', 'url': 'static/resurse/wp2782770-1846651530.jpg', 'duration': 15}, {'file_name': 'SampleVideo_1280x720_1mb.mp4', 'url': 'static/resurse/SampleVideo_1280x720_1mb.mp4', 'duration': 5}], 'version': 5} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Successfully loaded 3 items from server +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +2025-08-05 15:35:55 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Application exit requested +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=192.168.1.245, host=tv-holba1, quick=8887779, port=5000 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Initializing with settings: server=192.168.1.245, host=tv-holba1, port=5000 +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[ERROR] [SignageApp] Failed to fetch playlist: HTTPConnectionPool(host='192.168.1.245', port=5000): Max retries exceeded with url: /api/playlists?hostname=tv-holba1&quickconnect_code=8887779 (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) +[WARNING] [SignageApp] Server returned empty playlist +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'duration': 20}, {'file_name': 'wp2782770-1846651530.jpg', 'url': 'static/resurse/wp2782770-1846651530.jpg', 'duration': 15}, {'file_name': 'SampleVideo_1280x720_1mb.mp4', 'url': 'static/resurse/SampleVideo_1280x720_1mb.mp4', 'duration': 5}], 'version': 5} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Loaded existing local playlist with 3 items +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +2025-08-05 15:58:32 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=192.168.1.245, host=tv-holba1, quick=8887779, port=5000 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'duration': 20}, {'file_name': 'wp2782770-1846651530.jpg', 'url': 'static/resurse/wp2782770-1846651530.jpg', 'duration': 15}, {'file_name': 'SampleVideo_1280x720_1mb.mp4', 'url': 'static/resurse/SampleVideo_1280x720_1mb.mp4', 'duration': 5}], 'version': 5} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Found fallback playlist with 3 items +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Initializing with settings: server=192.168.1.245, host=tv-holba1, port=5000 +[INFO] [SignageApp] Attempting to connect to server... +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[ERROR] [SignageApp] Failed to fetch playlist: HTTPConnectionPool(host='192.168.1.245', port=5000): Max retries exceeded with url: /api/playlists?hostname=tv-holba1&quickconnect_code=8887779 (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) +[WARNING] [SignageApp] Server returned empty playlist, falling back to local playlist +[INFO] [SignageApp] Loaded fallback playlist with 3 items +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +2025-08-05 16:04:48 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Playing media: wp2782770-1846651530.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/wp2782770-1846651530.jpg +2025-08-05 16:05:08 - STARTED: wp2782770-1846651530.jpg +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Playing media: SampleVideo_1280x720_1mb.mp4 from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/SampleVideo_1280x720_1mb.mp4 +2025-08-05 16:05:24 - STARTED: SampleVideo_1280x720_1mb.mp4 +[INFO] [SignageApp] Application exit requested +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=192.168.1.245, host=tv-holba1, quick=8887779, port=5000 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'duration': 20}, {'file_name': 'wp2782770-1846651530.jpg', 'url': 'static/resurse/wp2782770-1846651530.jpg', 'duration': 15}, {'file_name': 'SampleVideo_1280x720_1mb.mp4', 'url': 'static/resurse/SampleVideo_1280x720_1mb.mp4', 'duration': 5}], 'version': 5} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Found fallback playlist with 3 items +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Initializing with settings: server=192.168.1.245, host=tv-holba1, port=5000 +[INFO] [SignageApp] Attempting to connect to server... +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[ERROR] [SignageApp] Failed to fetch playlist: HTTPConnectionPool(host='192.168.1.245', port=5000): Max retries exceeded with url: /api/playlists?hostname=tv-holba1&quickconnect_code=8887779 (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) +[WARNING] [SignageApp] Server returned empty playlist, falling back to local playlist +[INFO] [SignageApp] Loaded fallback playlist with 3 items +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +2025-08-05 16:15:53 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] Playing media: wp2782770-1846651530.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/wp2782770-1846651530.jpg +2025-08-05 16:16:14 - STARTED: wp2782770-1846651530.jpg +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Playing media: SampleVideo_1280x720_1mb.mp4 from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/SampleVideo_1280x720_1mb.mp4 +2025-08-05 16:16:30 - STARTED: SampleVideo_1280x720_1mb.mp4 +[INFO] [SignageApp] Application exit requested +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=192.168.1.245, host=tv-holba1, quick=8887779, port=5000 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'duration': 20}, {'file_name': 'wp2782770-1846651530.jpg', 'url': 'static/resurse/wp2782770-1846651530.jpg', 'duration': 15}, {'file_name': 'SampleVideo_1280x720_1mb.mp4', 'url': 'static/resurse/SampleVideo_1280x720_1mb.mp4', 'duration': 5}], 'version': 5} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Found fallback playlist with 3 items +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Initializing with settings: server=192.168.1.245, host=tv-holba1, port=5000 +[INFO] [SignageApp] Attempting to connect to server... +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[ERROR] [SignageApp] Failed to fetch playlist: HTTPConnectionPool(host='192.168.1.245', port=5000): Max retries exceeded with url: /api/playlists?hostname=tv-holba1&quickconnect_code=8887779 (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) +[WARNING] [SignageApp] Server returned empty playlist, falling back to local playlist +[INFO] [SignageApp] Loaded fallback playlist with 3 items +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +2025-08-05 16:28:45 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Application exit requested +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=192.168.1.245, host=tv-holba1, quick=8887779, port=5000 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'duration': 20}, {'file_name': 'wp2782770-1846651530.jpg', 'url': 'static/resurse/wp2782770-1846651530.jpg', 'duration': 15}, {'file_name': 'SampleVideo_1280x720_1mb.mp4', 'url': 'static/resurse/SampleVideo_1280x720_1mb.mp4', 'duration': 5}], 'version': 5} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Found fallback playlist with 3 items +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Initializing with settings: server=192.168.1.245, host=tv-holba1, port=5000 +[INFO] [SignageApp] Attempting to connect to server... +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[ERROR] [SignageApp] Failed to fetch playlist: HTTPConnectionPool(host='192.168.1.245', port=5000): Max retries exceeded with url: /api/playlists?hostname=tv-holba1&quickconnect_code=8887779 (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) +[WARNING] [SignageApp] Server returned empty playlist, falling back to local playlist +[INFO] [SignageApp] Loaded fallback playlist with 3 items +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +2025-08-05 16:34:00 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] Loading configuration in settings window +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Config loaded: {'screen_orientation': 'Landscape', 'screen_name': 'tv-holba1', 'quickconnect_key': '8887779', 'server_ip': '192.168.1.245', 'port': '5000', 'screen_w': '1920', 'screen_h': '1080', 'playlist_version': 5} +[INFO] [SignageApp] Configuration values loaded successfully in settings +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Playing media: wp2782770-1846651530.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/wp2782770-1846651530.jpg +2025-08-05 16:34:20 - STARTED: wp2782770-1846651530.jpg +[INFO] [SignageApp] Application exit requested +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=192.168.1.245, host=tv-holba1, quick=8887779, port=5000 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': '1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'url': 'static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg', 'duration': 20}, {'file_name': 'wp2782770-1846651530.jpg', 'url': 'static/resurse/wp2782770-1846651530.jpg', 'duration': 15}, {'file_name': 'SampleVideo_1280x720_1mb.mp4', 'url': 'static/resurse/SampleVideo_1280x720_1mb.mp4', 'duration': 5}], 'version': 5} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Found fallback playlist with 3 items +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Initializing with settings: server=192.168.1.245, host=tv-holba1, port=5000 +[INFO] [SignageApp] Attempting to connect to server... +[INFO] [SignageApp] Fetching playlist from URL: http://192.168.1.245:5000/api/playlists with params: {'hostname': 'tv-holba1', 'quickconnect_code': '8887779'} +[ERROR] [SignageApp] Failed to fetch playlist: HTTPConnectionPool(host='192.168.1.245', port=5000): Max retries exceeded with url: /api/playlists?hostname=tv-holba1&quickconnect_code=8887779 (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) +[WARNING] [SignageApp] Server returned empty playlist, falling back to local playlist +[INFO] [SignageApp] Loaded fallback playlist with 3 items +[INFO] [SignageApp] Playing media: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg from /home/pi/Desktop/signage-player/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +2025-08-05 16:44:37 - STARTED: 1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Application exit requested diff --git a/src/Resurse/pause.png b/tkinter_app/resources/pause.png similarity index 100% rename from src/Resurse/pause.png rename to tkinter_app/resources/pause.png diff --git a/src/Resurse/play.png b/tkinter_app/resources/play.png similarity index 100% rename from src/Resurse/play.png rename to tkinter_app/resources/play.png diff --git a/src/Resurse/right-arrow-blue.png b/tkinter_app/resources/right-arrow-blue.png similarity index 100% rename from src/Resurse/right-arrow-blue.png rename to tkinter_app/resources/right-arrow-blue.png diff --git a/src/Resurse/right-arrow-green.png b/tkinter_app/resources/right-arrow-green.png similarity index 100% rename from src/Resurse/right-arrow-green.png rename to tkinter_app/resources/right-arrow-green.png diff --git a/src/__pycache__/logging_config.cpython-311.pyc b/tkinter_app/src/__pycache__/logging_config.cpython-311.pyc similarity index 58% rename from src/__pycache__/logging_config.cpython-311.pyc rename to tkinter_app/src/__pycache__/logging_config.cpython-311.pyc index 7db5b9d..b5f8328 100644 Binary files a/src/__pycache__/logging_config.cpython-311.pyc and b/tkinter_app/src/__pycache__/logging_config.cpython-311.pyc differ diff --git a/src/__pycache__/python_functions.cpython-311.pyc b/tkinter_app/src/__pycache__/python_functions.cpython-311.pyc similarity index 72% rename from src/__pycache__/python_functions.cpython-311.pyc rename to tkinter_app/src/__pycache__/python_functions.cpython-311.pyc index bc5bd4b..b2535e9 100644 Binary files a/src/__pycache__/python_functions.cpython-311.pyc and b/tkinter_app/src/__pycache__/python_functions.cpython-311.pyc differ diff --git a/tkinter_app/src/__pycache__/tkinter_simple_player.cpython-311.pyc b/tkinter_app/src/__pycache__/tkinter_simple_player.cpython-311.pyc new file mode 100644 index 0000000..14b84f5 Binary files /dev/null and b/tkinter_app/src/__pycache__/tkinter_simple_player.cpython-311.pyc differ diff --git a/src/logging_config.py b/tkinter_app/src/logging_config.py similarity index 83% rename from src/logging_config.py rename to tkinter_app/src/logging_config.py index ba2a5a6..8710312 100644 --- a/src/logging_config.py +++ b/tkinter_app/src/logging_config.py @@ -2,7 +2,8 @@ import logging import os # Path to the log file -LOG_FILE_PATH = os.path.join(os.path.dirname(__file__), 'Resurse', 'log.txt') +# Update the path to point to the new resources directory +LOG_FILE_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'resources', 'log.txt') # Create a logger instance Logger = logging.getLogger('SignageApp') diff --git a/tkinter_app/src/main.py b/tkinter_app/src/main.py new file mode 100644 index 0000000..119c0dd --- /dev/null +++ b/tkinter_app/src/main.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +""" +Main entry point for the tkinter-based signage player application. +This file acts as the main executable for launching the tkinter player. +""" +import os +import sys +import cv2 # Import OpenCV to confirm it's available + +# Add the current directory to the path so we can import our modules +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +# Import the player module +from tkinter_simple_player import SimpleMediaPlayerApp + +if __name__ == "__main__": + print(f"Using OpenCV version: {cv2.__version__}") + # Create and run the player + player = SimpleMediaPlayerApp() + player.run() \ No newline at end of file diff --git a/src/python_functions.py b/tkinter_app/src/python_functions.py similarity index 97% rename from src/python_functions.py rename to tkinter_app/src/python_functions.py index 429e6dc..279b46e 100644 --- a/src/python_functions.py +++ b/tkinter_app/src/python_functions.py @@ -5,8 +5,9 @@ from logging_config import Logger # Import the shared logger import bcrypt import time -CONFIG_FILE = './Resurse/app_config.txt' -LOCAL_PLAYLIST_FILE = './static/local_playlist.json' # Path to the local playlist file +# Update paths to use the new directory structure +CONFIG_FILE = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'resources', 'app_config.txt') +LOCAL_PLAYLIST_FILE = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'resources', 'local_playlist.json') def load_config(): """Load configuration from app_config.txt.""" diff --git a/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg b/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg new file mode 100644 index 0000000..ad8cba1 Binary files /dev/null and b/tkinter_app/src/static/resurse/1307306470-nature_wallpaper_hd_hd_nature_3-3828209637.jpg differ diff --git a/tkinter_app/src/static/resurse/SampleVideo_1280x720_1mb.mp4 b/tkinter_app/src/static/resurse/SampleVideo_1280x720_1mb.mp4 new file mode 100644 index 0000000..ed139d6 Binary files /dev/null and b/tkinter_app/src/static/resurse/SampleVideo_1280x720_1mb.mp4 differ diff --git a/tkinter_app/src/static/resurse/har_page_001.jpg b/tkinter_app/src/static/resurse/har_page_001.jpg new file mode 100644 index 0000000..d3278b0 Binary files /dev/null and b/tkinter_app/src/static/resurse/har_page_001.jpg differ diff --git a/tkinter_app/src/static/resurse/har_page_002.jpg b/tkinter_app/src/static/resurse/har_page_002.jpg new file mode 100644 index 0000000..d3278b0 Binary files /dev/null and b/tkinter_app/src/static/resurse/har_page_002.jpg differ diff --git a/tkinter_app/src/static/resurse/har_page_003.jpg b/tkinter_app/src/static/resurse/har_page_003.jpg new file mode 100644 index 0000000..d3278b0 Binary files /dev/null and b/tkinter_app/src/static/resurse/har_page_003.jpg differ diff --git a/tkinter_app/src/static/resurse/har_page_004.jpg b/tkinter_app/src/static/resurse/har_page_004.jpg new file mode 100644 index 0000000..d3278b0 Binary files /dev/null and b/tkinter_app/src/static/resurse/har_page_004.jpg differ diff --git a/tkinter_app/src/static/resurse/har_page_005.jpg b/tkinter_app/src/static/resurse/har_page_005.jpg new file mode 100644 index 0000000..d3278b0 Binary files /dev/null and b/tkinter_app/src/static/resurse/har_page_005.jpg differ diff --git a/tkinter_app/src/static/resurse/har_page_006.jpg b/tkinter_app/src/static/resurse/har_page_006.jpg new file mode 100644 index 0000000..d3278b0 Binary files /dev/null and b/tkinter_app/src/static/resurse/har_page_006.jpg differ diff --git a/tkinter_app/src/static/resurse/har_page_007.jpg b/tkinter_app/src/static/resurse/har_page_007.jpg new file mode 100644 index 0000000..d3278b0 Binary files /dev/null and b/tkinter_app/src/static/resurse/har_page_007.jpg differ diff --git a/tkinter_app/src/static/resurse/har_page_008.jpg b/tkinter_app/src/static/resurse/har_page_008.jpg new file mode 100644 index 0000000..d3278b0 Binary files /dev/null and b/tkinter_app/src/static/resurse/har_page_008.jpg differ diff --git a/tkinter_app/src/static/resurse/har_page_009.jpg b/tkinter_app/src/static/resurse/har_page_009.jpg new file mode 100644 index 0000000..d3278b0 Binary files /dev/null and b/tkinter_app/src/static/resurse/har_page_009.jpg differ diff --git a/tkinter_app/src/static/resurse/har_page_010.jpg b/tkinter_app/src/static/resurse/har_page_010.jpg new file mode 100644 index 0000000..d3278b0 Binary files /dev/null and b/tkinter_app/src/static/resurse/har_page_010.jpg differ diff --git a/tkinter_app/src/static/resurse/wp2782770-1846651530.jpg b/tkinter_app/src/static/resurse/wp2782770-1846651530.jpg new file mode 100644 index 0000000..d6613fb Binary files /dev/null and b/tkinter_app/src/static/resurse/wp2782770-1846651530.jpg differ diff --git a/tkinter_app/src/tkinter_simple_player.py b/tkinter_app/src/tkinter_simple_player.py new file mode 100644 index 0000000..3659555 --- /dev/null +++ b/tkinter_app/src/tkinter_simple_player.py @@ -0,0 +1,1126 @@ +#!/usr/bin/env python3 +""" +Tkinter Simple Media Player - A lightweight version with minimal dependencies +Features: +- Image display +- Basic playlist management +- Settings configuration +- Auto-hiding controls +""" + +import tkinter as tk +from tkinter import ttk, messagebox, simpledialog +import threading +import time +import os +import json +import datetime +from pathlib import Path +import subprocess +import sys +import requests # Required for server communication +import queue +import cv2 # For video playback +import pygame # For video audio + +# Try importing PIL but provide fallback +try: + from PIL import Image, ImageTk + PIL_AVAILABLE = True +except ImportError: + PIL_AVAILABLE = False + print("WARNING: PIL not available. Image display functionality will be limited.") + +# Import existing functions +from python_functions import ( + load_local_playlist, download_media_files, clean_unused_files, + save_local_playlist, update_config_playlist_version, fetch_server_playlist, + load_config +) +from logging_config import Logger + +# Update the config file path to use the resources directory +CONFIG_FILE = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'resources', 'app_config.txt') + +class SimpleMediaPlayerApp: + def __init__(self): + self.root = tk.Tk() + self.setup_window() + + # Media player state + self.playlist = [] + self.current_index = 0 + self.is_paused = False + self.is_fullscreen = True + self.auto_advance_timer = None + self.hide_controls_timer = None + self.video_thread = None + self.update_queue = queue.Queue() + + # UI Elements + self.image_label = None + self.status_label = None + self.control_frame = None + self.settings_window = None + + # Running state + self.running = True + + # Initialize pygame for video audio + try: + pygame.init() + pygame.mixer.init() + except Exception as e: + Logger.warning(f"Failed to initialize pygame mixer for audio: {e}") + + self.setup_ui() + + # Initialize from server + self.initialize_playlist_from_server() + + # Start periodic playlist checks + self.start_periodic_checks() + + def setup_window(self): + """Configure the main window""" + self.root.title("Simple Signage Player") + self.root.configure(bg='black') + + # Load window size from config + try: + config = load_config() + width = int(config.get('screen_w', 1920)) + height = int(config.get('screen_h', 1080)) + except: + width, height = 800, 600 # Fallback size if config fails + + self.root.geometry(f"{width}x{height}") + self.root.attributes('-fullscreen', True) + + # Bind events + self.root.bind('', self.on_key_press) + self.root.bind('', self.on_mouse_click) + self.root.bind('', self.on_mouse_motion) + self.root.focus_set() + + def setup_ui(self): + """Create the user interface""" + # Main content area - make sure it's black and fill the entire window + self.content_frame = tk.Frame(self.root, bg='black') + self.content_frame.pack(fill=tk.BOTH, expand=True) + + # Image display - expand to fill the entire window + self.image_label = tk.Label(self.content_frame, bg='black') + self.image_label.pack(fill=tk.BOTH, expand=True) + + # Status label - hidden by default + self.status_label = tk.Label( + self.content_frame, + bg='black', + fg='white', + font=('Arial', 24), + text="" + ) + # Don't place the status label by default to keep it hidden + + # Control panel + self.create_control_panel() + self.show_controls() + self.schedule_hide_controls() + + def create_control_panel(self): + """Create the control panel with navigation buttons""" + # Create a semi-transparent frame with rounded corners look + self.control_frame = tk.Frame( + self.root, + bg='#1a1a1a', # Dark background + bd=1, + relief=tk.RAISED, + padx=10, + pady=10 + ) + self.control_frame.place(relx=0.98, rely=0.98, anchor='se') + + # Modern button style with consistent sizes + button_config = { + 'bg': '#333333', # Dark gray background + 'fg': 'white', # White text + 'activebackground': '#555555', # Lighter gray when clicked + 'activeforeground': 'white', + 'relief': tk.FLAT, # Flat buttons for modern look + 'borderwidth': 0, + 'width': 8, + 'height': 2, + 'font': ('Arial', 9, 'bold') + } + + # Create a horizontal layout for buttons + # Previous button + self.prev_btn = tk.Button( + self.control_frame, + text="◀ Prev", + command=self.previous_media, + **button_config + ) + self.prev_btn.grid(row=0, column=0, padx=3) + + # Play/Pause button + self.play_pause_btn = tk.Button( + self.control_frame, + text="⏸ Pause" if not self.is_paused else "▶ Play", + command=self.toggle_play_pause, + **button_config + ) + self.play_pause_btn.grid(row=0, column=1, padx=3) + + # Next button + self.next_btn = tk.Button( + self.control_frame, + text="Next ▶", + command=self.next_media, + **button_config + ) + self.next_btn.grid(row=0, column=2, padx=3) + + # Settings button + self.settings_btn = tk.Button( + self.control_frame, + text="⚙ Settings", + command=self.open_settings, + **button_config + ) + self.settings_btn.grid(row=0, column=3, padx=3) + + # Exit button - special styling + self.exit_btn = tk.Button( + self.control_frame, + text="EXIT", + command=self.show_exit_dialog, + bg='#cc0000', # Red background + fg='white', + activebackground='#ff0000', + activeforeground='white', + relief=tk.FLAT, + borderwidth=0, + width=6, + height=2, + font=('Arial', 9, 'bold') + ) + self.exit_btn.grid(row=0, column=4, padx=3) + + def initialize_playlist_from_server(self): + """Initialize the playlist from the server on startup with fallback to local playlist""" + # First try to load any existing local playlist as fallback + fallback_playlist = None + try: + local_playlist_data = load_local_playlist() + fallback_playlist = local_playlist_data.get('playlist', []) + if fallback_playlist: + Logger.info(f"Found fallback playlist with {len(fallback_playlist)} items") + except Exception as e: + Logger.warning(f"No fallback playlist available: {e}") + + # Show connection status + self.status_label.config(text="Connecting to server...\nPlease wait") + self.status_label.place(relx=0.5, rely=0.5, anchor='center') + self.root.update() + + # Load configuration + config = load_config() + server = config.get("server_ip", "") + host = config.get("screen_name", "") + quick = config.get("quickconnect_key", "") + port = config.get("port", "") + + Logger.info(f"Initializing with settings: server={server}, host={host}, port={port}") + + if not server or not host or not quick or not port: + Logger.warning("Missing server configuration, using fallback playlist") + self.status_label.place_forget() + self.load_fallback_playlist(fallback_playlist) + return + + # Attempt to fetch server playlist with timeout + server_connection_successful = False + try: + # Add connection timeout and retry logic + Logger.info("Attempting to connect to server...") + self.status_label.config(text="Connecting to server...\nAttempting connection") + self.root.update() + + server_playlist_data = fetch_server_playlist() + server_playlist = server_playlist_data.get('playlist', []) + server_version = server_playlist_data.get('version', 0) + + if server_playlist: + Logger.info(f"Server playlist found with {len(server_playlist)} items, version {server_version}") + server_connection_successful = True + + # Download media files and update local playlist + self.status_label.config(text="Downloading media files...\nPlease wait") + self.root.update() + + download_media_files(server_playlist, server_version) + update_config_playlist_version(server_version) + + # Load the updated local playlist + local_playlist_data = load_local_playlist() + self.playlist = local_playlist_data.get('playlist', []) + + if self.playlist: + Logger.info(f"Successfully loaded {len(self.playlist)} items from server") + self.status_label.place_forget() + self.play_current_media() + return + else: + Logger.warning("Server playlist was empty, falling back to local playlist") + else: + Logger.warning("Server returned empty playlist, falling back to local playlist") + + except requests.exceptions.ConnectTimeout: + Logger.error("Server connection timeout, using fallback playlist") + except requests.exceptions.ConnectionError: + Logger.error("Cannot connect to server, using fallback playlist") + except requests.exceptions.Timeout: + Logger.error("Server request timeout, using fallback playlist") + except Exception as e: + Logger.error(f"Failed to fetch playlist from server: {e}, using fallback playlist") + + # If we reach here, server connection failed - use fallback + if not server_connection_successful: + self.status_label.config(text="Server unavailable\nLoading last playlist...") + self.root.update() + time.sleep(1) # Brief pause to show message + + self.status_label.place_forget() + self.load_fallback_playlist(fallback_playlist) + + def load_fallback_playlist(self, fallback_playlist): + """Load fallback playlist when server is unavailable""" + if fallback_playlist and len(fallback_playlist) > 0: + self.playlist = fallback_playlist + Logger.info(f"Loaded fallback playlist with {len(self.playlist)} items") + self.play_current_media() + else: + Logger.warning("No fallback playlist available, loading demo content") + self.load_demo_or_local_playlist() + + def load_demo_or_local_playlist(self): + """Load either the existing local playlist or demo content""" + # First try to load the local playlist + local_playlist_data = load_local_playlist() + self.playlist = local_playlist_data.get('playlist', []) + + if self.playlist: + Logger.info(f"Loaded existing local playlist with {len(self.playlist)} items") + self.play_current_media() + return + + # If no local playlist, try loading demo content + Logger.info("No local playlist found, loading demo content") + self.create_demo_content() + + if self.playlist: + self.play_current_media() + else: + self.show_no_content_message() + + def create_demo_content(self): + """Create demo content for testing""" + demo_images = [] + + # First check static/resurse folder for any media + static_dir = os.path.join(os.path.dirname(__file__), 'static', 'resurse') + if os.path.exists(static_dir): + for file in os.listdir(static_dir): + if file.lower().endswith(('.jpg', '.jpeg', '.png', '.gif')): + full_path = os.path.join(static_dir, file) + demo_images.append({ + 'file_name': file, + 'url': full_path, + 'duration': 5 + }) + + # If no files found in static/resurse, look in Resurse folder + if not demo_images: + demo_dir = './Resurse' + if os.path.exists(demo_dir): + for file in os.listdir(demo_dir): + if file.lower().endswith(('.jpg', '.jpeg', '.png', '.gif')): + demo_images.append({ + 'file_name': file, + 'url': os.path.join(demo_dir, file), + 'duration': 5 + }) + + if demo_images: + self.playlist = demo_images + Logger.info(f"Created demo playlist with {len(demo_images)} images") + else: + # Create a text-only demo if no images found + self.playlist = [{ + 'file_name': 'Demo Text', + 'url': 'text://Welcome to Tkinter Media Player!\n\nPlease configure server settings', + 'duration': 5 + }] + + def show_no_content_message(self): + """Show message when no content is available""" + self.image_label.config(image='') + self.status_label.config( + text="No media content available.\nPress Settings to configure server connection." + ) + def show_error_message(self, message): + """Show error message""" + self.image_label.config(image='') + self.status_label.config(text=f"Error: {message}") + + def play_current_media(self): + """Play the current media item""" + if not self.playlist or self.current_index >= len(self.playlist): + self.show_no_content_message() + return + + media = self.playlist[self.current_index] + file_path = media.get('url', '') + file_name = media.get('file_name', '') + duration = media.get('duration', 10) + + # Handle relative paths by converting to absolute paths + if file_path.startswith('static/resurse/'): + # Convert relative path to absolute + absolute_path = os.path.join(os.path.dirname(__file__), file_path) + file_path = absolute_path + + Logger.info(f"Playing media: {file_name} from {file_path}") + + # Log media start + self.log_event(file_name, "STARTED") + + # Cancel existing timers + self.cancel_timers() + + # Handle different media types + if file_path.startswith('text://'): + self.show_text_content(file_path[7:], duration) + elif file_path.lower().endswith(('.mp4', '.avi', '.mov', '.mkv')): + self.play_video(file_path) + elif os.path.exists(file_path) and file_path.lower().endswith(('.jpg', '.jpeg', '.png', '.gif', '.bmp')): + self.show_image(file_path, duration) + else: + Logger.error(f"Unsupported or missing media: {file_path}") + self.status_label.config(text=f"Missing or unsupported media:\n{file_name}") + # Schedule next media after short delay + self.auto_advance_timer = self.root.after(5000, self.next_media) + + def play_video(self, file_path): + """Play video file""" + # Clear any status text + self.status_label.config(text="") + + # Use OpenCV to play video in a separate thread + def video_player(): + try: + cap = cv2.VideoCapture(file_path) + + if not cap.isOpened(): + raise ValueError(f"Cannot open video file: {file_path}") + + # Get video properties + fps = cap.get(cv2.CAP_PROP_FPS) + delay = int(1000 / fps) if fps > 0 else 30 # Default to 30 FPS if unknown + + while self.current_index < len(self.playlist) and not self.is_paused: + ret, frame = cap.read() + + if not ret: + break # End of video + + # Convert color from BGR to RGB + frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + + # Convert to PhotoImage and display + img = Image.fromarray(frame) + img.thumbnail((self.root.winfo_width(), self.root.winfo_height()), Image.LANCZOS) + photo = ImageTk.PhotoImage(img) + + self.image_label.config(image=photo) + self.image_label.image = photo # Keep reference + + # Wait for the next frame + time.sleep(delay / 1000) + + cap.release() + + # Schedule next media + self.auto_advance_timer = self.root.after(1000, self.next_media) + + except Exception as e: + Logger.error(f"Error playing video {file_path}: {e}") + self.status_label.config(text=f"Error playing video: {e}") + self.auto_advance_timer = self.root.after(5000, self.next_media) + + # Start video player thread + threading.Thread(target=video_player, daemon=True).start() + + def show_text_content(self, text, duration): + """Display text content""" + self.image_label.config(image='') + self.status_label.config(text=text) + + # Schedule next media + self.auto_advance_timer = self.root.after( + int(duration * 1000), + self.next_media + ) + + def show_image(self, file_path, duration): + """Display an image""" + try: + self.status_label.config(text="") + + if PIL_AVAILABLE: + # Use PIL for better image handling + img = Image.open(file_path) + screen_width = self.root.winfo_width() + screen_height = self.root.winfo_height() + + # Resize while maintaining aspect ratio + img.thumbnail((screen_width, screen_height), Image.LANCZOS) + photo = ImageTk.PhotoImage(img) + + # Display image + self.image_label.config(image=photo) + self.image_label.image = photo # Keep reference + else: + # Fall back to basic text display if PIL not available + self.image_label.config(image='') + self.status_label.config(text=f"IMAGE: {os.path.basename(file_path)}\n\n(Install PIL for image display)") + + # Schedule next media + self.auto_advance_timer = self.root.after( + int(duration * 1000), + self.next_media + ) + + except Exception as e: + Logger.error(f"Failed to show image {file_path}: {e}") + self.status_label.config(text=f"Image error: {e}") + self.auto_advance_timer = self.root.after(5000, self.next_media) + + def next_media(self): + """Move to next media""" + self.cancel_timers() + + if not self.playlist: + return + + self.current_index = (self.current_index + 1) % len(self.playlist) + + # Check for playlist updates at end of cycle + if self.current_index == 0: + threading.Thread(target=self.check_playlist_updates, daemon=True).start() + + self.play_current_media() + + def previous_media(self): + """Move to previous media""" + self.cancel_timers() + + if not self.playlist: + return + + self.current_index = (self.current_index - 1) % len(self.playlist) + self.play_current_media() + + def toggle_play_pause(self): + """Toggle play/pause state""" + self.is_paused = not self.is_paused + + if self.is_paused: + self.play_pause_btn.config(text="▶ Play") + self.cancel_timers() + else: + self.play_pause_btn.config(text="⏸ Pause") + # Resume current media + self.play_current_media() + + Logger.info(f"Media {'paused' if self.is_paused else 'resumed'}") + + def cancel_timers(self): + """Cancel all active timers""" + if self.auto_advance_timer: + self.root.after_cancel(self.auto_advance_timer) + self.auto_advance_timer = None + + def show_controls(self): + """Show control panel""" + if self.control_frame: + self.control_frame.place(relx=0.98, rely=0.98, anchor='se') + + def hide_controls(self): + """Hide control panel""" + if self.control_frame: + self.control_frame.place_forget() + + def schedule_hide_controls(self): + """Schedule hiding controls after delay""" + if self.hide_controls_timer: + self.root.after_cancel(self.hide_controls_timer) + self.hide_controls_timer = self.root.after(10000, self.hide_controls) + + def on_mouse_click(self, event): + """Handle mouse clicks""" + self.show_controls() + self.schedule_hide_controls() + + def on_mouse_motion(self, event): + """Handle mouse motion""" + self.show_controls() + self.schedule_hide_controls() + + def on_key_press(self, event): + """Handle keyboard events""" + key = event.keysym.lower() + + if key == 'f': + self.toggle_fullscreen() + elif key == 'space': + self.toggle_play_pause() + elif key == 'left': + self.previous_media() + elif key == 'right': + self.next_media() + elif key == 'escape': + self.show_exit_dialog() + + self.show_controls() + self.schedule_hide_controls() + + def toggle_fullscreen(self): + """Toggle fullscreen mode""" + self.is_fullscreen = not self.is_fullscreen + self.root.attributes('-fullscreen', self.is_fullscreen) + + def open_settings(self): + """Open settings window""" + if hasattr(self, 'settings_window') and self.settings_window and self.settings_window.winfo_exists(): + self.settings_window.lift() + return + + self.settings_window = SettingsWindow(self.root, self) + + def show_exit_dialog(self): + """Show modern password-protected exit dialog""" + try: + config = load_config() + quickconnect_key = config.get('quickconnect_key', '') + except: + quickconnect_key = '' + + # Create modern exit dialog + exit_dialog = tk.Toplevel(self.root) + exit_dialog.title("Exit Application") + exit_dialog.geometry("400x200") + exit_dialog.configure(bg='#2d2d2d') + exit_dialog.transient(self.root) + exit_dialog.grab_set() + exit_dialog.resizable(False, False) + + # Center the dialog + exit_dialog.geometry("+%d+%d" % (self.root.winfo_rootx() + 200, self.root.winfo_rooty() + 200)) + + # Header with icon + header_frame = tk.Frame(exit_dialog, bg='#cc0000', height=60) + header_frame.pack(fill=tk.X) + header_frame.pack_propagate(False) + + icon_label = tk.Label(header_frame, text="⚠", font=('Arial', 20, 'bold'), + fg='white', bg='#cc0000') + icon_label.pack(side=tk.LEFT, padx=15, pady=15) + + title_label = tk.Label(header_frame, text="Exit Application", + font=('Arial', 14, 'bold'), fg='white', bg='#cc0000') + title_label.pack(side=tk.LEFT, pady=15) + + # Content frame + content_frame = tk.Frame(exit_dialog, bg='#2d2d2d', padx=20, pady=20) + content_frame.pack(fill=tk.BOTH, expand=True) + + # Password prompt + prompt_label = tk.Label(content_frame, text="Enter password to exit:", + font=('Arial', 11), fg='white', bg='#2d2d2d') + prompt_label.pack(pady=(0, 10)) + + # Password entry + password_var = tk.StringVar() + password_entry = tk.Entry(content_frame, textvariable=password_var, + font=('Arial', 11), show='*', width=25, + bg='#404040', fg='white', insertbackground='white', + relief=tk.FLAT, bd=5) + password_entry.pack(pady=(0, 15)) + password_entry.focus_set() + + # Button frame + button_frame = tk.Frame(content_frame, bg='#2d2d2d') + button_frame.pack(fill=tk.X) + + def check_password(): + if password_var.get() == quickconnect_key: + exit_dialog.destroy() + self.exit_application() + elif password_var.get(): # Only show error if password was entered + # Show error in red + error_label.config(text="✗ Incorrect password", fg='#ff4444') + password_entry.delete(0, tk.END) + password_entry.focus_set() + + def cancel_exit(): + exit_dialog.destroy() + + # Error label (hidden initially) + error_label = tk.Label(content_frame, text="", font=('Arial', 9), + bg='#2d2d2d') + error_label.pack() + + # Buttons + cancel_btn = tk.Button(button_frame, text="Cancel", command=cancel_exit, + bg='#555555', fg='white', font=('Arial', 10, 'bold'), + relief=tk.FLAT, padx=20, pady=8, width=10) + cancel_btn.pack(side=tk.RIGHT, padx=(10, 0)) + + exit_btn = tk.Button(button_frame, text="Exit", command=check_password, + bg='#cc0000', fg='white', font=('Arial', 10, 'bold'), + relief=tk.FLAT, padx=20, pady=8, width=10) + exit_btn.pack(side=tk.RIGHT) + + # Bind Enter key to check password + password_entry.bind('', lambda e: check_password()) + exit_dialog.bind('', lambda e: cancel_exit()) + + def exit_application(self): + """Exit the application""" + Logger.info("Application exit requested") + self.running = False + self.root.quit() + self.root.destroy() + + def check_playlist_updates(self): + """Check for playlist updates from server with fallback protection""" + try: + config = load_config() + local_version = config.get('playlist_version', 0) + + server_playlist_data = fetch_server_playlist() + server_version = server_playlist_data.get('version', 0) + + if server_version > local_version: + Logger.info(f"Updating playlist: {local_version} -> {server_version}") + + # Clean old files + local_playlist_data = load_local_playlist() + clean_unused_files(local_playlist_data.get('playlist', [])) + + # Download new content + download_media_files( + server_playlist_data.get('playlist', []), + server_version + ) + + # Update local playlist + local_playlist_data = load_local_playlist() + self.playlist = local_playlist_data.get('playlist', []) + + # Reset to beginning of playlist + self.current_index = 0 + + Logger.info("Playlist updated successfully") + + # Continue with current media after update + self.play_current_media() + else: + Logger.info("No playlist updates available") + + except requests.exceptions.ConnectTimeout: + Logger.warning("Server connection timeout during update check - continuing with current playlist") + except requests.exceptions.ConnectionError: + Logger.warning("Cannot connect to server during update check - continuing with current playlist") + except requests.exceptions.Timeout: + Logger.warning("Server request timeout during update check - continuing with current playlist") + except Exception as e: + Logger.warning(f"Failed to check playlist updates: {e} - continuing with current playlist") + + def log_event(self, file_name, event): + """Log media events""" + try: + timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + log_message = f"{timestamp} - {event}: {file_name}\n" + + # Update the log file path to the resources directory + log_file = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'resources', 'log.txt') + with open(log_file, 'a') as f: + f.write(log_message) + + except Exception as e: + Logger.error(f"Failed to log event: {e}") + + def start_periodic_checks(self): + """Start periodic playlist checks""" + def check_loop(): + """Background thread for periodic checks""" + while self.running: + try: + time.sleep(300) # Check every 5 minutes + if self.running: + self.check_playlist_updates() + except Exception as e: + Logger.error(f"Error in periodic check: {e}") + + # Start background thread + threading.Thread(target=check_loop, daemon=True).start() + + def run(self): + """Start the application""" + Logger.info("Starting Simple Tkinter Media Player") + try: + self.root.mainloop() + except KeyboardInterrupt: + self.exit_application() + except Exception as e: + Logger.error(f"Application error: {e}") + print(f"Error: {e}") + + +class SettingsWindow: + def __init__(self, parent, app): + self.parent = parent + self.app = app + self.window = tk.Toplevel(parent) + self.setup_window() + self.create_widgets() + self.load_config() + + def setup_window(self): + """Setup settings window""" + self.window.title("Settings") + self.window.geometry("700x500") + self.window.configure(bg='#f0f0f0') + self.window.transient(self.parent) + self.window.grab_set() + + def create_widgets(self): + """Create settings widgets""" + # Main frame + main_frame = ttk.Frame(self.window, padding=10) + main_frame.pack(fill=tk.BOTH, expand=True) + + # Create a notebook (tabbed interface) + notebook = ttk.Notebook(main_frame) + notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) + + # Tab 1: Configuration + config_tab = ttk.Frame(notebook, padding=10) + notebook.add(config_tab, text="Configuration") + + # Screen Name + ttk.Label(config_tab, text="Screen Name:").grid(row=0, column=0, sticky=tk.W, pady=5) + self.screen_name_var = tk.StringVar() + ttk.Entry(config_tab, textvariable=self.screen_name_var, width=30).grid(row=0, column=1, sticky=tk.W, padx=(10, 0)) + + # Server IP + ttk.Label(config_tab, text="Server IP:").grid(row=1, column=0, sticky=tk.W, pady=5) + self.server_ip_var = tk.StringVar() + ttk.Entry(config_tab, textvariable=self.server_ip_var, width=30).grid(row=1, column=1, sticky=tk.W, padx=(10, 0)) + + # Port + ttk.Label(config_tab, text="Port:").grid(row=2, column=0, sticky=tk.W, pady=5) + self.port_var = tk.StringVar() + ttk.Entry(config_tab, textvariable=self.port_var, width=30).grid(row=2, column=1, sticky=tk.W, padx=(10, 0)) + + # QuickConnect Key + ttk.Label(config_tab, text="QuickConnect Key:").grid(row=3, column=0, sticky=tk.W, pady=5) + self.quickconnect_var = tk.StringVar() + ttk.Entry(config_tab, textvariable=self.quickconnect_var, width=30, show='*').grid(row=3, column=1, sticky=tk.W, padx=(10, 0)) + + # Screen Size + ttk.Label(config_tab, text="Screen Width:").grid(row=4, column=0, sticky=tk.W, pady=5) + self.screen_w_var = tk.StringVar() + ttk.Entry(config_tab, textvariable=self.screen_w_var, width=10).grid(row=4, column=1, sticky=tk.W, padx=(10, 0)) + + ttk.Label(config_tab, text="Screen Height:").grid(row=5, column=0, sticky=tk.W, pady=5) + self.screen_h_var = tk.StringVar() + ttk.Entry(config_tab, textvariable=self.screen_h_var, width=10).grid(row=5, column=1, sticky=tk.W, padx=(10, 0)) + + # Server connection test + ttk.Button(config_tab, text="Test Server Connection", command=self.test_connection).grid(row=6, column=0, columnspan=2, sticky=tk.W, pady=10) + + self.connection_status = ttk.Label(config_tab, text="") + self.connection_status.grid(row=7, column=0, columnspan=2, sticky=tk.W) + + # Force playlist refresh + ttk.Button(config_tab, text="Force Playlist Refresh", command=self.force_playlist_refresh).grid(row=8, column=0, columnspan=2, sticky=tk.W, pady=10) + + # Tab 2: Logs + logs_tab = ttk.Frame(notebook, padding=10) + notebook.add(logs_tab, text="Logs") + + # Log display + ttk.Label(logs_tab, text="Recent Logs:").pack(anchor=tk.W, pady=(0, 5)) + + # Log text with scrollbar in a frame + log_frame = ttk.Frame(logs_tab) + log_frame.pack(fill=tk.BOTH, expand=True) + + self.log_text = tk.Text(log_frame, height=15, font=('Courier', 9)) + scrollbar = ttk.Scrollbar(log_frame, command=self.log_text.yview) + self.log_text.configure(yscrollcommand=scrollbar.set) + + self.log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) + scrollbar.pack(side=tk.RIGHT, fill=tk.Y) + + # Refresh logs button + ttk.Button(logs_tab, text="Refresh Logs", command=self.load_logs).pack(anchor=tk.W, pady=10) + + # Tab 3: About + about_tab = ttk.Frame(notebook, padding=10) + notebook.add(about_tab, text="About") + + # About text + about_text = ( + "Tkinter Simple Media Player\n" + "Version 1.0\n\n" + "A lightweight digital signage player built with tkinter.\n" + "Displays images and videos with periodic server synchronization.\n\n" + "Keyboard shortcuts:\n" + "F - Toggle fullscreen\n" + "Space - Play/Pause\n" + "Left/Right Arrow - Previous/Next media\n" + "Escape - Exit (password protected)" + ) + + ttk.Label(about_tab, text=about_text, justify=tk.LEFT).pack(anchor=tk.W) + + # Button frame at bottom + button_frame = ttk.Frame(main_frame) + button_frame.pack(fill=tk.X, pady=10) + + ttk.Button(button_frame, text="Save", command=self.save_config).pack(side=tk.LEFT, padx=5) + ttk.Button(button_frame, text="Cancel", command=self.window.destroy).pack(side=tk.LEFT) + + # Load initial logs + self.load_logs() + + def load_config(self): + """Load current configuration""" + try: + Logger.info("Loading configuration in settings window") + config = load_config() + Logger.info(f"Config loaded: {config}") + + # Set default values if keys don't exist + self.screen_name_var.set(config.get('screen_name', '')) + self.server_ip_var.set(config.get('server_ip', '')) + self.port_var.set(config.get('port', '5000')) # Changed default to 5000 + self.quickconnect_var.set(config.get('quickconnect_key', '')) + self.screen_w_var.set(config.get('screen_w', '1920')) + self.screen_h_var.set(config.get('screen_h', '1080')) + + Logger.info("Configuration values loaded successfully in settings") + + except Exception as e: + Logger.error(f"Failed to load config in settings: {e}") + # Set default values if loading fails + self.screen_name_var.set('') + self.server_ip_var.set('') + self.port_var.set('5000') + self.quickconnect_var.set('') + self.screen_w_var.set('1920') + self.screen_h_var.set('1080') + + # Show error but don't block the window + self.connection_status.configure(text=f"Warning: Could not load existing config: {e}") + + def save_config(self): + """Save configuration""" + try: + # Load existing config or create new one + try: + config = load_config() + except: + config = {} + + # Update with new values + config['screen_name'] = self.screen_name_var.get() + config['server_ip'] = self.server_ip_var.get() + config['port'] = self.port_var.get() + config['quickconnect_key'] = self.quickconnect_var.get() + config['screen_w'] = self.screen_w_var.get() + config['screen_h'] = self.screen_h_var.get() + + # Ensure directory exists + config_dir = os.path.dirname(CONFIG_FILE) + os.makedirs(config_dir, exist_ok=True) + + with open(CONFIG_FILE, 'w') as f: + json.dump(config, f, indent=4) + + Logger.info(f"Configuration saved to {CONFIG_FILE}") + + # Modern success message + self.show_success_message("Configuration saved successfully!") + + # Ask if user wants to refresh playlist now + if messagebox.askyesno("Refresh Playlist", "Do you want to refresh the playlist from server now?"): + self.force_playlist_refresh() + + self.window.destroy() + + except Exception as e: + Logger.error(f"Failed to save configuration: {e}") + messagebox.showerror("Error", f"Failed to save configuration: {e}") + + def show_success_message(self, message): + """Show a modern success message""" + success_window = tk.Toplevel(self.window) + success_window.title("Success") + success_window.geometry("300x120") + success_window.configure(bg='#2d5a2d') + success_window.transient(self.window) + success_window.grab_set() + + # Center the window + success_window.geometry("+%d+%d" % (self.window.winfo_rootx() + 200, self.window.winfo_rooty() + 150)) + + # Success icon and message + icon_label = tk.Label(success_window, text="✓", font=('Arial', 24, 'bold'), + fg='white', bg='#2d5a2d') + icon_label.pack(pady=10) + + msg_label = tk.Label(success_window, text=message, font=('Arial', 10), + fg='white', bg='#2d5a2d', wraplength=250) + msg_label.pack(pady=5) + + ok_btn = tk.Button(success_window, text="OK", command=success_window.destroy, + bg='#4d7d4d', fg='white', font=('Arial', 10, 'bold'), + relief=tk.FLAT, padx=20, pady=5) + ok_btn.pack(pady=10) + + def force_playlist_refresh(self): + """Force refresh of playlist from server""" + try: + # Show connection message + self.connection_status.configure(text="Refreshing playlist from server...") + self.window.update() + + # Fetch 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: + # Clean old files + local_playlist_data = load_local_playlist() + clean_unused_files(local_playlist_data.get('playlist', [])) + + # Download new content + download_media_files(server_playlist, server_version) + update_config_playlist_version(server_version) + + # Let the app know to reload + self.app.playlist = load_local_playlist().get('playlist', []) + self.app.current_index = 0 # Reset to beginning + + self.connection_status.configure(text=f"Playlist refreshed! Downloaded {len(server_playlist)} media files.") + + # Force the app to play the current media + if self.app.playlist: + self.app.play_current_media() + else: + self.connection_status.configure(text="Server returned empty playlist. Check server connection settings.") + + except Exception as e: + self.connection_status.configure(text=f"Error refreshing playlist: {str(e)[:50]}...") + Logger.error(f"Failed to refresh playlist: {e}") + + def test_connection(self): + """Test server connection""" + try: + self.connection_status.configure(text="Testing connection...") + self.window.update() + + server_ip = self.server_ip_var.get() + port = self.port_var.get() + screen_name = self.screen_name_var.get() + quickconnect = self.quickconnect_var.get() + + if not all([server_ip, port, screen_name, quickconnect]): + self.connection_status.configure(text="Please fill all connection fields") + return + + url = f"http://{server_ip}:{port}/api/playlists" + params = { + 'hostname': screen_name, + 'quickconnect_code': quickconnect + } + + response = requests.get(url, params=params, timeout=10) + + if response.status_code == 200: + data = response.json() + version = data.get('playlist_version', 'Unknown') + num_items = len(data.get('playlist', [])) + self.connection_status.configure( + text=f"✓ Connected! Server playlist version: {version}, {num_items} media items available" + ) + else: + self.connection_status.configure( + text=f"✗ Connection failed (Status: {response.status_code})" + ) + + except Exception as e: + self.connection_status.configure(text=f"✗ Connection error: {str(e)[:50]}...") + + def load_logs(self): + """Load recent log entries""" + try: + # Update to use the resources directory + log_file = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'resources', 'log.txt') + + if os.path.exists(log_file): + with open(log_file, 'r') as f: + lines = f.readlines() + + # Show last 20 lines + recent_lines = lines[-20:] if len(lines) > 20 else lines + + self.log_text.delete(1.0, tk.END) + self.log_text.insert(tk.END, ''.join(recent_lines)) + self.log_text.see(tk.END) + else: + self.log_text.delete(1.0, tk.END) + self.log_text.insert(tk.END, "No log file found") + + except Exception as e: + self.log_text.delete(1.0, tk.END) + self.log_text.insert(tk.END, f"Error loading logs: {e}") + + def load_playlist_view(self): + """Load playlist items into treeview""" + try: + # Clear existing items + for item in self.playlist_view.get_children(): + self.playlist_view.delete(item) + except Exception as e: + Logger.error(f"Failed to load playlist view: {e}") + messagebox.showerror("Error", f"Failed to load playlist: {e}") + + def show_video_placeholder(self, filename, duration): + """Show placeholder for video files""" + self.image_label.config(image='') + self.status_label.config(text="") # Clear any status text for cleaner display + + # Schedule next media + self.auto_advance_timer = self.root.after( + int(duration * 1000), + self.next_media + ) diff --git a/tkinter_requirements.txt b/tkinter_requirements.txt new file mode 100644 index 0000000..4a4caf5 --- /dev/null +++ b/tkinter_requirements.txt @@ -0,0 +1,14 @@ +# Tkinter Media Player Requirements - Raspberry Pi Compatible +# Core GUI and media handling (lighter alternatives) +opencv-python-headless>=4.8.0 +Pillow>=9.0.0 +pygame>=2.1.0 + +# Networking and data handling +requests>=2.28.0 + +# System utilities for Python 3.9+ +# pathlib2 not needed on modern Python versions + +# Optional: Basic image processing without heavy dependencies +# numpy - will be installed with opencv-python-headless \ No newline at end of file diff --git a/tkinter_requirements_minimal.txt b/tkinter_requirements_minimal.txt new file mode 100644 index 0000000..875c714 --- /dev/null +++ b/tkinter_requirements_minimal.txt @@ -0,0 +1,7 @@ +# Minimal Tkinter Media Player Requirements - Raspberry Pi Compatible +# Core dependencies only +requests>=2.28.0 + +# Optional but recommended if available via apt +# python3-pil (install via apt instead of pip) +# python3-tk (should already be installed with Python) \ No newline at end of file