From 99338b88c7ccb52aa901f03db1bfefd5d28e366f Mon Sep 17 00:00:00 2001 From: scheianu Date: Sat, 23 Aug 2025 19:03:10 +0300 Subject: [PATCH] final; --- tkinter_app/resources/log.txt | 550 +++++++++++ .../settings_screen.cpython-311.pyc | Bin 64384 -> 72718 bytes tkinter_app/src/tkinter_simple_player_old.py | 934 ------------------ 3 files changed, 550 insertions(+), 934 deletions(-) delete mode 100644 tkinter_app/src/tkinter_simple_player_old.py diff --git a/tkinter_app/resources/log.txt b/tkinter_app/resources/log.txt index 7d29791..4f381bf 100644 --- a/tkinter_app/resources/log.txt +++ b/tkinter_app/resources/log.txt @@ -2429,3 +2429,553 @@ [INFO] [SignageApp] No playlist updates available [INFO] [SignageApp] Successfully displayed image: Cindrel_1.jpg (Original: (4096, 3072), Screen: 1920x1080, Mode: fit, Offset: (240, 0)) [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=digi-signage.moto-adv.com, host=tv-terasa, quick=8887779, port=8880 +[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] Fetching playlist from URL: http://digi-signage.moto-adv.com/api/playlists with params: {'hostname': 'tv-terasa', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$UQzlEHNWudAB4P08Le3YJeWHrZHJkWL44kRQpZ53kt.fDLTcrPzGm', 'playlist': [{'duration': 10, 'file_name': 'Cindrel_1.jpg', 'url': 'http://digi-signage.moto-adv.com/media/Cindrel_1.jpg'}, {'duration': 10, 'file_name': 'trans_cindrel_4.jpg', 'url': 'http://digi-signage.moto-adv.com/media/trans_cindrel_4.jpg'}], 'playlist_version': 29} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 29. +[INFO] [SignageApp] No new playlist on the server. Local version: 29, Server version: 29 +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': 'Cindrel_1.jpg', 'url': 'static/resurse/Cindrel_1.jpg', 'duration': 10}, {'file_name': 'trans_cindrel_4.jpg', 'url': 'static/resurse/trans_cindrel_4.jpg', 'duration': 10}], 'version': 29} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Found fallback playlist with 2 items +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Initializing with settings: server=digi-signage.moto-adv.com, host=tv-terasa, port=8880 +[INFO] [SignageApp] Attempting to connect to server... +[INFO] [SignageApp] Fetching playlist from URL: http://digi-signage.moto-adv.com/api/playlists with params: {'hostname': 'tv-terasa', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$UQzlEHNWudAB4P08Le3YJeWHrZHJkWL44kRQpZ53kt.fDLTcrPzGm', 'playlist': [{'duration': 10, 'file_name': 'Cindrel_1.jpg', 'url': 'http://digi-signage.moto-adv.com/media/Cindrel_1.jpg'}, {'duration': 10, 'file_name': 'trans_cindrel_4.jpg', 'url': 'http://digi-signage.moto-adv.com/media/trans_cindrel_4.jpg'}], 'playlist_version': 29} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 29. +[INFO] [SignageApp] Server playlist found with 2 items, version 29 +[INFO] [SignageApp] python_functions: Starting media file download... +[INFO] [SignageApp] python_functions: File Cindrel_1.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File trans_cindrel_4.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 29. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': 'Cindrel_1.jpg', 'url': 'static/resurse/Cindrel_1.jpg', 'duration': 10}, {'file_name': 'trans_cindrel_4.jpg', 'url': 'static/resurse/trans_cindrel_4.jpg', 'duration': 10}], 'version': 29} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Successfully loaded 2 items from server +[INFO] [SignageApp] Playing media: Cindrel_1.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/Cindrel_1.jpg +2025-08-23 16:14:39 - STARTED: Cindrel_1.jpg +[INFO] [SignageApp] Successfully displayed image: Cindrel_1.jpg (Original: (4096, 3072), Screen: 1920x1018, Mode: fit, Offset: (281, 0)) +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] Playing media: trans_cindrel_4.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/trans_cindrel_4.jpg +2025-08-23 16:14:50 - STARTED: trans_cindrel_4.jpg +[INFO] [SignageApp] Successfully displayed image: trans_cindrel_4.jpg (Original: (4000, 3000), Screen: 1920x1018, Mode: fit, Offset: (281, 0)) +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] Playing media: Cindrel_1.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/Cindrel_1.jpg +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +2025-08-23 16:15:01 - STARTED: Cindrel_1.jpg +[INFO] [SignageApp] Fetching playlist from URL: http://digi-signage.moto-adv.com/api/playlists with params: {'hostname': 'tv-terasa', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$UQzlEHNWudAB4P08Le3YJeWHrZHJkWL44kRQpZ53kt.fDLTcrPzGm', 'playlist': [{'duration': 10, 'file_name': 'Cindrel_1.jpg', 'url': 'http://digi-signage.moto-adv.com/media/Cindrel_1.jpg'}, {'duration': 10, 'file_name': 'trans_cindrel_4.jpg', 'url': 'http://digi-signage.moto-adv.com/media/trans_cindrel_4.jpg'}], 'playlist_version': 29} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 29. +[INFO] [SignageApp] No playlist updates available +[INFO] [SignageApp] Successfully displayed image: Cindrel_1.jpg (Original: (4096, 3072), Screen: 1920x1018, Mode: fit, Offset: (281, 0)) +[INFO] [SignageApp] Playing media: trans_cindrel_4.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/trans_cindrel_4.jpg +2025-08-23 16:15:13 - STARTED: trans_cindrel_4.jpg +[INFO] [SignageApp] Successfully displayed image: trans_cindrel_4.jpg (Original: (4000, 3000), Screen: 1920x1018, Mode: fit, Offset: (281, 0)) +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] Playing media: Cindrel_1.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/Cindrel_1.jpg +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +2025-08-23 16:15:24 - STARTED: Cindrel_1.jpg +[INFO] [SignageApp] Fetching playlist from URL: http://digi-signage.moto-adv.com/api/playlists with params: {'hostname': 'tv-terasa', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$UQzlEHNWudAB4P08Le3YJeWHrZHJkWL44kRQpZ53kt.fDLTcrPzGm', 'playlist': [{'duration': 10, 'file_name': 'Cindrel_1.jpg', 'url': 'http://digi-signage.moto-adv.com/media/Cindrel_1.jpg'}, {'duration': 10, 'file_name': 'trans_cindrel_4.jpg', 'url': 'http://digi-signage.moto-adv.com/media/trans_cindrel_4.jpg'}], 'playlist_version': 29} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 29. +[INFO] [SignageApp] No playlist updates available +[INFO] [SignageApp] Successfully displayed image: Cindrel_1.jpg (Original: (4096, 3072), Screen: 1920x1018, Mode: fit, Offset: (281, 0)) +[INFO] [SignageApp] Media paused +[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=digi-signage.moto-adv.com, host=tv-terasa, quick=8887779, port=8880 +[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] Fetching playlist from URL: http://digi-signage.moto-adv.com/api/playlists with params: {'hostname': 'tv-terasa', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$UQzlEHNWudAB4P08Le3YJeWHrZHJkWL44kRQpZ53kt.fDLTcrPzGm', 'playlist': [{'duration': 10, 'file_name': 'Cindrel_1.jpg', 'url': 'http://digi-signage.moto-adv.com/media/Cindrel_1.jpg'}, {'duration': 10, 'file_name': 'trans_cindrel_4.jpg', 'url': 'http://digi-signage.moto-adv.com/media/trans_cindrel_4.jpg'}], 'playlist_version': 29} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 29. +[INFO] [SignageApp] No new playlist on the server. Local version: 29, Server version: 29 +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': 'Cindrel_1.jpg', 'url': 'static/resurse/Cindrel_1.jpg', 'duration': 10}, {'file_name': 'trans_cindrel_4.jpg', 'url': 'static/resurse/trans_cindrel_4.jpg', 'duration': 10}], 'version': 29} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Found fallback playlist with 2 items +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Initializing with settings: server=digi-signage.moto-adv.com, host=tv-terasa, port=8880 +[INFO] [SignageApp] Attempting to connect to server... +[INFO] [SignageApp] Fetching playlist from URL: http://digi-signage.moto-adv.com/api/playlists with params: {'hostname': 'tv-terasa', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$UQzlEHNWudAB4P08Le3YJeWHrZHJkWL44kRQpZ53kt.fDLTcrPzGm', 'playlist': [{'duration': 10, 'file_name': 'Cindrel_1.jpg', 'url': 'http://digi-signage.moto-adv.com/media/Cindrel_1.jpg'}, {'duration': 10, 'file_name': 'trans_cindrel_4.jpg', 'url': 'http://digi-signage.moto-adv.com/media/trans_cindrel_4.jpg'}], 'playlist_version': 29} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 29. +[INFO] [SignageApp] Server playlist found with 2 items, version 29 +[INFO] [SignageApp] python_functions: Starting media file download... +[INFO] [SignageApp] python_functions: File Cindrel_1.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File trans_cindrel_4.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 29. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': 'Cindrel_1.jpg', 'url': 'static/resurse/Cindrel_1.jpg', 'duration': 10}, {'file_name': 'trans_cindrel_4.jpg', 'url': 'static/resurse/trans_cindrel_4.jpg', 'duration': 10}], 'version': 29} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Successfully loaded 2 items from server +[INFO] [SignageApp] Playing media: Cindrel_1.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/Cindrel_1.jpg +2025-08-23 16:18:39 - STARTED: Cindrel_1.jpg +[INFO] [SignageApp] Successfully displayed image: Cindrel_1.jpg (Original: (4096, 3072), Screen: 1920x1080, Mode: fit, Offset: (240, 0)) +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] Media paused +[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=digi-signage.moto-adv.com, host=tv-terasa, quick=8887779, port=8880 +[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] Fetching playlist from URL: http://digi-signage.moto-adv.com/api/playlists with params: {'hostname': 'tv-terasa', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$UQzlEHNWudAB4P08Le3YJeWHrZHJkWL44kRQpZ53kt.fDLTcrPzGm', 'playlist': [{'duration': 10, 'file_name': 'Cindrel_1.jpg', 'url': 'http://digi-signage.moto-adv.com/media/Cindrel_1.jpg'}, {'duration': 10, 'file_name': 'trans_cindrel_4.jpg', 'url': 'http://digi-signage.moto-adv.com/media/trans_cindrel_4.jpg'}], 'playlist_version': 29} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 29. +[INFO] [SignageApp] No new playlist on the server. Local version: 29, Server version: 29 +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': 'Cindrel_1.jpg', 'url': 'static/resurse/Cindrel_1.jpg', 'duration': 10}, {'file_name': 'trans_cindrel_4.jpg', 'url': 'static/resurse/trans_cindrel_4.jpg', 'duration': 10}], 'version': 29} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Found fallback playlist with 2 items +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Initializing with settings: server=digi-signage.moto-adv.com, host=tv-terasa, port=8880 +[INFO] [SignageApp] Attempting to connect to server... +[INFO] [SignageApp] Fetching playlist from URL: http://digi-signage.moto-adv.com/api/playlists with params: {'hostname': 'tv-terasa', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$UQzlEHNWudAB4P08Le3YJeWHrZHJkWL44kRQpZ53kt.fDLTcrPzGm', 'playlist': [{'duration': 10, 'file_name': 'Cindrel_1.jpg', 'url': 'http://digi-signage.moto-adv.com/media/Cindrel_1.jpg'}, {'duration': 10, 'file_name': 'trans_cindrel_4.jpg', 'url': 'http://digi-signage.moto-adv.com/media/trans_cindrel_4.jpg'}], 'playlist_version': 29} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 29. +[INFO] [SignageApp] Server playlist found with 2 items, version 29 +[INFO] [SignageApp] python_functions: Starting media file download... +[INFO] [SignageApp] python_functions: File Cindrel_1.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File trans_cindrel_4.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 29. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': 'Cindrel_1.jpg', 'url': 'static/resurse/Cindrel_1.jpg', 'duration': 10}, {'file_name': 'trans_cindrel_4.jpg', 'url': 'static/resurse/trans_cindrel_4.jpg', 'duration': 10}], 'version': 29} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Successfully loaded 2 items from server +[INFO] [SignageApp] Playing media: Cindrel_1.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/Cindrel_1.jpg +2025-08-23 16:24:29 - STARTED: Cindrel_1.jpg +[INFO] [SignageApp] Successfully displayed image: Cindrel_1.jpg (Original: (4096, 3072), Screen: 1920x1080, Mode: fit, Offset: (240, 0)) +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] Media paused +[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=digi-signage.moto-adv.com, host=tv-terasa, quick=8887779, port=8880 +[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] Fetching playlist from URL: http://digi-signage.moto-adv.com/api/playlists with params: {'hostname': 'tv-terasa', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$UQzlEHNWudAB4P08Le3YJeWHrZHJkWL44kRQpZ53kt.fDLTcrPzGm', 'playlist': [{'duration': 10, 'file_name': 'Cindrel_1.jpg', 'url': 'http://digi-signage.moto-adv.com/media/Cindrel_1.jpg'}, {'duration': 10, 'file_name': 'trans_cindrel_4.jpg', 'url': 'http://digi-signage.moto-adv.com/media/trans_cindrel_4.jpg'}], 'playlist_version': 29} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 29. +[INFO] [SignageApp] No new playlist on the server. Local version: 29, Server version: 29 +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': 'Cindrel_1.jpg', 'url': 'static/resurse/Cindrel_1.jpg', 'duration': 10}, {'file_name': 'trans_cindrel_4.jpg', 'url': 'static/resurse/trans_cindrel_4.jpg', 'duration': 10}], 'version': 29} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Found fallback playlist with 2 items +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Initializing with settings: server=digi-signage.moto-adv.com, host=tv-terasa, port=8880 +[INFO] [SignageApp] Attempting to connect to server... +[INFO] [SignageApp] Fetching playlist from URL: http://digi-signage.moto-adv.com/api/playlists with params: {'hostname': 'tv-terasa', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$UQzlEHNWudAB4P08Le3YJeWHrZHJkWL44kRQpZ53kt.fDLTcrPzGm', 'playlist': [{'duration': 10, 'file_name': 'Cindrel_1.jpg', 'url': 'http://digi-signage.moto-adv.com/media/Cindrel_1.jpg'}, {'duration': 10, 'file_name': 'trans_cindrel_4.jpg', 'url': 'http://digi-signage.moto-adv.com/media/trans_cindrel_4.jpg'}], 'playlist_version': 29} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 29. +[INFO] [SignageApp] Server playlist found with 2 items, version 29 +[INFO] [SignageApp] python_functions: Starting media file download... +[INFO] [SignageApp] python_functions: File Cindrel_1.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File trans_cindrel_4.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 29. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': 'Cindrel_1.jpg', 'url': 'static/resurse/Cindrel_1.jpg', 'duration': 10}, {'file_name': 'trans_cindrel_4.jpg', 'url': 'static/resurse/trans_cindrel_4.jpg', 'duration': 10}], 'version': 29} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Successfully loaded 2 items from server +[INFO] [SignageApp] Playing media: Cindrel_1.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/Cindrel_1.jpg +2025-08-23 16:28:43 - STARTED: Cindrel_1.jpg +[INFO] [SignageApp] Successfully displayed image: Cindrel_1.jpg (Original: (4096, 3072), Screen: 1920x1080, Mode: fit, Offset: (240, 0)) +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] Playing media: trans_cindrel_4.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/trans_cindrel_4.jpg +2025-08-23 16:28:55 - STARTED: trans_cindrel_4.jpg +[INFO] [SignageApp] Successfully displayed image: trans_cindrel_4.jpg (Original: (4000, 3000), Screen: 1920x1080, Mode: fit, Offset: (240, 0)) +[INFO] [SignageApp] Media paused +[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] 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=digi-signage.moto-adv.com, host=tv-terasa, quick=8887779, port=8880 +[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] Fetching playlist from URL: http://digi-signage.moto-adv.com/api/playlists with params: {'hostname': 'tv-terasa', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$UQzlEHNWudAB4P08Le3YJeWHrZHJkWL44kRQpZ53kt.fDLTcrPzGm', 'playlist': [{'duration': 10, 'file_name': 'Cindrel_1.jpg', 'url': 'http://digi-signage.moto-adv.com/media/Cindrel_1.jpg'}, {'duration': 10, 'file_name': 'trans_cindrel_4.jpg', 'url': 'http://digi-signage.moto-adv.com/media/trans_cindrel_4.jpg'}], 'playlist_version': 29} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 29. +[INFO] [SignageApp] No new playlist on the server. Local version: 29, Server version: 29 +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': 'Cindrel_1.jpg', 'url': 'static/resurse/Cindrel_1.jpg', 'duration': 10}, {'file_name': 'trans_cindrel_4.jpg', 'url': 'static/resurse/trans_cindrel_4.jpg', 'duration': 10}], 'version': 29} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Found fallback playlist with 2 items +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Initializing with settings: server=digi-signage.moto-adv.com, host=tv-terasa, port=8880 +[INFO] [SignageApp] Attempting to connect to server... +[INFO] [SignageApp] Fetching playlist from URL: http://digi-signage.moto-adv.com/api/playlists with params: {'hostname': 'tv-terasa', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$UQzlEHNWudAB4P08Le3YJeWHrZHJkWL44kRQpZ53kt.fDLTcrPzGm', 'playlist': [{'duration': 10, 'file_name': 'Cindrel_1.jpg', 'url': 'http://digi-signage.moto-adv.com/media/Cindrel_1.jpg'}, {'duration': 10, 'file_name': 'trans_cindrel_4.jpg', 'url': 'http://digi-signage.moto-adv.com/media/trans_cindrel_4.jpg'}], 'playlist_version': 29} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 29. +[INFO] [SignageApp] Server playlist found with 2 items, version 29 +[INFO] [SignageApp] python_functions: Starting media file download... +[INFO] [SignageApp] python_functions: File Cindrel_1.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File trans_cindrel_4.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 29. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': 'Cindrel_1.jpg', 'url': 'static/resurse/Cindrel_1.jpg', 'duration': 10}, {'file_name': 'trans_cindrel_4.jpg', 'url': 'static/resurse/trans_cindrel_4.jpg', 'duration': 10}], 'version': 29} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Successfully loaded 2 items from server +[INFO] [SignageApp] Playing media: Cindrel_1.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/Cindrel_1.jpg +2025-08-23 16:41:13 - STARTED: Cindrel_1.jpg +[INFO] [SignageApp] Successfully displayed image: Cindrel_1.jpg (Original: (4096, 3072), Screen: 1920x1080, Mode: fit, Offset: (240, 0)) +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] Media paused +[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] 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=digi-signage.moto-adv.com, host=tv-terasa, quick=8887779, port=8880 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=digi-signage.moto-adv.com, host=tv-terasa, quick=8887779, port=8880 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=digi-signage.moto-adv.com, host=tv-terasa, quick=8887779, port=8880 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=digi-signage.moto-adv.com, host=tv-terasa, quick=8887779, port=8880 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=digi-signage.moto-adv.com, host=tv-terasa, quick=8887779, port=8880 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=digi-signage.moto-adv.com, host=tv-terasa, quick=8887779, port=8880 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=digi-signage.moto-adv.com, host=tv-terasa, quick=8887779, port=8880 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=digi-signage.moto-adv.com, host=tv-terasa, quick=8887779, port=8880 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=digi-signage.moto-adv.com, host=tv-terasa, quick=8887779, port=8880 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=digi-signage.moto-adv.com, host=tv-terasa, quick=8887779, port=8880 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=digi-signage.moto-adv.com, host=tv-terasa, quick=8887779, port=8880 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=digi-signage.moto-adv.com, host=tv-terasa, quick=8887779, port=8880 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=digi-signage.moto-adv.com, host=tv-terasa, quick=8887779, port=8880 +[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] Fetching playlist from URL: http://digi-signage.moto-adv.com/api/playlists with params: {'hostname': 'tv-terasa', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$UQzlEHNWudAB4P08Le3YJeWHrZHJkWL44kRQpZ53kt.fDLTcrPzGm', 'playlist': [{'duration': 10, 'file_name': 'Cindrel_1.jpg', 'url': 'http://digi-signage.moto-adv.com/media/Cindrel_1.jpg'}, {'duration': 10, 'file_name': 'trans_cindrel_4.jpg', 'url': 'http://digi-signage.moto-adv.com/media/trans_cindrel_4.jpg'}], 'playlist_version': 29} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 29. +[INFO] [SignageApp] No new playlist on the server. Local version: 29, Server version: 29 +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': 'Cindrel_1.jpg', 'url': 'static/resurse/Cindrel_1.jpg', 'duration': 10}, {'file_name': 'trans_cindrel_4.jpg', 'url': 'static/resurse/trans_cindrel_4.jpg', 'duration': 10}], 'version': 29} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Found fallback playlist with 2 items +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Initializing with settings: server=digi-signage.moto-adv.com, host=tv-terasa, port=8880 +[INFO] [SignageApp] Attempting to connect to server... +[INFO] [SignageApp] Fetching playlist from URL: http://digi-signage.moto-adv.com/api/playlists with params: {'hostname': 'tv-terasa', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$UQzlEHNWudAB4P08Le3YJeWHrZHJkWL44kRQpZ53kt.fDLTcrPzGm', 'playlist': [{'duration': 10, 'file_name': 'Cindrel_1.jpg', 'url': 'http://digi-signage.moto-adv.com/media/Cindrel_1.jpg'}, {'duration': 10, 'file_name': 'trans_cindrel_4.jpg', 'url': 'http://digi-signage.moto-adv.com/media/trans_cindrel_4.jpg'}], 'playlist_version': 29} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 29. +[INFO] [SignageApp] Server playlist found with 2 items, version 29 +[INFO] [SignageApp] python_functions: Starting media file download... +[INFO] [SignageApp] python_functions: File Cindrel_1.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File trans_cindrel_4.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 29. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': 'Cindrel_1.jpg', 'url': 'static/resurse/Cindrel_1.jpg', 'duration': 10}, {'file_name': 'trans_cindrel_4.jpg', 'url': 'static/resurse/trans_cindrel_4.jpg', 'duration': 10}], 'version': 29} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Successfully loaded 2 items from server +[INFO] [SignageApp] Playing media: Cindrel_1.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/Cindrel_1.jpg +2025-08-23 17:07:13 - STARTED: Cindrel_1.jpg +[INFO] [SignageApp] Successfully displayed image: Cindrel_1.jpg (Original: (4096, 3072), Screen: 1920x1080, Mode: fit, Offset: (240, 0)) +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] Playing media: trans_cindrel_4.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/trans_cindrel_4.jpg +2025-08-23 17:07:24 - STARTED: trans_cindrel_4.jpg +[INFO] [SignageApp] Successfully displayed image: trans_cindrel_4.jpg (Original: (4000, 3000), Screen: 1920x1080, Mode: fit, Offset: (240, 0)) +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] Playing media: Cindrel_1.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/Cindrel_1.jpg +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +2025-08-23 17:07:36 - STARTED: Cindrel_1.jpg +[INFO] [SignageApp] Fetching playlist from URL: http://digi-signage.moto-adv.com/api/playlists with params: {'hostname': 'tv-terasa', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$UQzlEHNWudAB4P08Le3YJeWHrZHJkWL44kRQpZ53kt.fDLTcrPzGm', 'playlist': [{'duration': 10, 'file_name': 'Cindrel_1.jpg', 'url': 'http://digi-signage.moto-adv.com/media/Cindrel_1.jpg'}, {'duration': 10, 'file_name': 'trans_cindrel_4.jpg', 'url': 'http://digi-signage.moto-adv.com/media/trans_cindrel_4.jpg'}], 'playlist_version': 29} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 29. +[INFO] [SignageApp] No playlist updates available +[INFO] [SignageApp] Successfully displayed image: Cindrel_1.jpg (Original: (4096, 3072), Screen: 1920x1080, Mode: fit, Offset: (240, 0)) +[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=digi-signage.moto-adv.com, host=tv-terasa, quick=8887779, port=8880 +[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] Fetching playlist from URL: http://digi-signage.moto-adv.com/api/playlists with params: {'hostname': 'tv-terasa', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$UQzlEHNWudAB4P08Le3YJeWHrZHJkWL44kRQpZ53kt.fDLTcrPzGm', 'playlist': [{'duration': 10, 'file_name': 'Cindrel_1.jpg', 'url': 'http://digi-signage.moto-adv.com/media/Cindrel_1.jpg'}, {'duration': 10, 'file_name': 'trans_cindrel_4.jpg', 'url': 'http://digi-signage.moto-adv.com/media/trans_cindrel_4.jpg'}], 'playlist_version': 29} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 29. +[INFO] [SignageApp] No new playlist on the server. Local version: 29, Server version: 29 +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': 'Cindrel_1.jpg', 'url': 'static/resurse/Cindrel_1.jpg', 'duration': 10}, {'file_name': 'trans_cindrel_4.jpg', 'url': 'static/resurse/trans_cindrel_4.jpg', 'duration': 10}], 'version': 29} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Found fallback playlist with 2 items +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Initializing with settings: server=digi-signage.moto-adv.com, host=tv-terasa, port=8880 +[INFO] [SignageApp] Attempting to connect to server... +[INFO] [SignageApp] Fetching playlist from URL: http://digi-signage.moto-adv.com/api/playlists with params: {'hostname': 'tv-terasa', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$UQzlEHNWudAB4P08Le3YJeWHrZHJkWL44kRQpZ53kt.fDLTcrPzGm', 'playlist': [{'duration': 10, 'file_name': 'Cindrel_1.jpg', 'url': 'http://digi-signage.moto-adv.com/media/Cindrel_1.jpg'}, {'duration': 10, 'file_name': 'trans_cindrel_4.jpg', 'url': 'http://digi-signage.moto-adv.com/media/trans_cindrel_4.jpg'}], 'playlist_version': 29} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 29. +[INFO] [SignageApp] Server playlist found with 2 items, version 29 +[INFO] [SignageApp] python_functions: Starting media file download... +[INFO] [SignageApp] python_functions: File Cindrel_1.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File trans_cindrel_4.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 29. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': 'Cindrel_1.jpg', 'url': 'static/resurse/Cindrel_1.jpg', 'duration': 10}, {'file_name': 'trans_cindrel_4.jpg', 'url': 'static/resurse/trans_cindrel_4.jpg', 'duration': 10}], 'version': 29} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Successfully loaded 2 items from server +[INFO] [SignageApp] Playing media: Cindrel_1.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/Cindrel_1.jpg +2025-08-23 17:08:21 - STARTED: Cindrel_1.jpg +[INFO] [SignageApp] Successfully displayed image: Cindrel_1.jpg (Original: (4096, 3072), Screen: 1920x1080, Mode: fit, Offset: (240, 0)) +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] Media paused +[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] 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=digi-signage.moto-adv.com, host=tv-terasa, quick=8887779, port=8880 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=digi-signage.moto-adv.com, host=tv-terasa, quick=8887779, port=8880 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=digi-signage.moto-adv.com, host=tv-terasa, quick=8887779, port=8880 +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] python_functions: Configuration loaded: server=digi-signage.moto-adv.com, host=tv-terasa, quick=8887779, port=8880 +[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] Fetching playlist from URL: http://digi-signage.moto-adv.com/api/playlists with params: {'hostname': 'tv-terasa', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$UQzlEHNWudAB4P08Le3YJeWHrZHJkWL44kRQpZ53kt.fDLTcrPzGm', 'playlist': [{'duration': 10, 'file_name': 'Cindrel_1.jpg', 'url': 'http://digi-signage.moto-adv.com/media/Cindrel_1.jpg'}, {'duration': 10, 'file_name': 'trans_cindrel_4.jpg', 'url': 'http://digi-signage.moto-adv.com/media/trans_cindrel_4.jpg'}], 'playlist_version': 29} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 29. +[INFO] [SignageApp] No new playlist on the server. Local version: 29, Server version: 29 +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': 'Cindrel_1.jpg', 'url': 'static/resurse/Cindrel_1.jpg', 'duration': 10}, {'file_name': 'trans_cindrel_4.jpg', 'url': 'static/resurse/trans_cindrel_4.jpg', 'duration': 10}], 'version': 29} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Found fallback playlist with 2 items +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Initializing with settings: server=digi-signage.moto-adv.com, host=tv-terasa, port=8880 +[INFO] [SignageApp] Attempting to connect to server... +[INFO] [SignageApp] Fetching playlist from URL: http://digi-signage.moto-adv.com/api/playlists with params: {'hostname': 'tv-terasa', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$UQzlEHNWudAB4P08Le3YJeWHrZHJkWL44kRQpZ53kt.fDLTcrPzGm', 'playlist': [{'duration': 10, 'file_name': 'Cindrel_1.jpg', 'url': 'http://digi-signage.moto-adv.com/media/Cindrel_1.jpg'}, {'duration': 10, 'file_name': 'trans_cindrel_4.jpg', 'url': 'http://digi-signage.moto-adv.com/media/trans_cindrel_4.jpg'}], 'playlist_version': 29} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 29. +[INFO] [SignageApp] Server playlist found with 2 items, version 29 +[INFO] [SignageApp] python_functions: Starting media file download... +[INFO] [SignageApp] python_functions: File Cindrel_1.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File trans_cindrel_4.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 29. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': 'Cindrel_1.jpg', 'url': 'static/resurse/Cindrel_1.jpg', 'duration': 10}, {'file_name': 'trans_cindrel_4.jpg', 'url': 'static/resurse/trans_cindrel_4.jpg', 'duration': 10}], 'version': 29} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Successfully loaded 2 items from server +[INFO] [SignageApp] Playing media: Cindrel_1.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/Cindrel_1.jpg +2025-08-23 17:14:27 - STARTED: Cindrel_1.jpg +[INFO] [SignageApp] Successfully displayed image: Cindrel_1.jpg (Original: (4096, 3072), Screen: 1920x1080, Mode: fit, Offset: (240, 0)) +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] Media paused +[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] 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=digi-signage.moto-adv.com, host=tv-terasa, quick=8887779, port=8880 +[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] Fetching playlist from URL: http://digi-signage.moto-adv.com/api/playlists with params: {'hostname': 'tv-terasa', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$UQzlEHNWudAB4P08Le3YJeWHrZHJkWL44kRQpZ53kt.fDLTcrPzGm', 'playlist': [{'duration': 10, 'file_name': 'Cindrel_1.jpg', 'url': 'http://digi-signage.moto-adv.com/media/Cindrel_1.jpg'}, {'duration': 10, 'file_name': 'trans_cindrel_4.jpg', 'url': 'http://digi-signage.moto-adv.com/media/trans_cindrel_4.jpg'}], 'playlist_version': 29} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 29. +[INFO] [SignageApp] No new playlist on the server. Local version: 29, Server version: 29 +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': 'Cindrel_1.jpg', 'url': 'static/resurse/Cindrel_1.jpg', 'duration': 10}, {'file_name': 'trans_cindrel_4.jpg', 'url': 'static/resurse/trans_cindrel_4.jpg', 'duration': 10}], 'version': 29} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Found fallback playlist with 2 items +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Initializing with settings: server=digi-signage.moto-adv.com, host=tv-terasa, port=8880 +[INFO] [SignageApp] Attempting to connect to server... +[INFO] [SignageApp] Fetching playlist from URL: http://digi-signage.moto-adv.com/api/playlists with params: {'hostname': 'tv-terasa', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$UQzlEHNWudAB4P08Le3YJeWHrZHJkWL44kRQpZ53kt.fDLTcrPzGm', 'playlist': [{'duration': 10, 'file_name': 'Cindrel_1.jpg', 'url': 'http://digi-signage.moto-adv.com/media/Cindrel_1.jpg'}, {'duration': 10, 'file_name': 'trans_cindrel_4.jpg', 'url': 'http://digi-signage.moto-adv.com/media/trans_cindrel_4.jpg'}], 'playlist_version': 29} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 29. +[INFO] [SignageApp] Server playlist found with 2 items, version 29 +[INFO] [SignageApp] python_functions: Starting media file download... +[INFO] [SignageApp] python_functions: File Cindrel_1.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File trans_cindrel_4.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 29. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': 'Cindrel_1.jpg', 'url': 'static/resurse/Cindrel_1.jpg', 'duration': 10}, {'file_name': 'trans_cindrel_4.jpg', 'url': 'static/resurse/trans_cindrel_4.jpg', 'duration': 10}], 'version': 29} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Successfully loaded 2 items from server +[INFO] [SignageApp] Playing media: Cindrel_1.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/Cindrel_1.jpg +2025-08-23 17:33:48 - STARTED: Cindrel_1.jpg +[INFO] [SignageApp] Successfully displayed image: Cindrel_1.jpg (Original: (4096, 3072), Screen: 1920x1080, Mode: fit, Offset: (240, 0)) +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] Media paused +[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] 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=digi-signage.moto-adv.com, host=tv-terasa, quick=8887779, port=8880 +[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] Fetching playlist from URL: http://digi-signage.moto-adv.com/api/playlists with params: {'hostname': 'tv-terasa', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$UQzlEHNWudAB4P08Le3YJeWHrZHJkWL44kRQpZ53kt.fDLTcrPzGm', 'playlist': [{'duration': 10, 'file_name': 'Cindrel_1.jpg', 'url': 'http://digi-signage.moto-adv.com/media/Cindrel_1.jpg'}, {'duration': 10, 'file_name': 'trans_cindrel_4.jpg', 'url': 'http://digi-signage.moto-adv.com/media/trans_cindrel_4.jpg'}], 'playlist_version': 29} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 29. +[INFO] [SignageApp] No new playlist on the server. Local version: 29, Server version: 29 +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': 'Cindrel_1.jpg', 'url': 'static/resurse/Cindrel_1.jpg', 'duration': 10}, {'file_name': 'trans_cindrel_4.jpg', 'url': 'static/resurse/trans_cindrel_4.jpg', 'duration': 10}], 'version': 29} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Found fallback playlist with 2 items +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Initializing with settings: server=digi-signage.moto-adv.com, host=tv-terasa, port=8880 +[INFO] [SignageApp] Attempting to connect to server... +[INFO] [SignageApp] Fetching playlist from URL: http://digi-signage.moto-adv.com/api/playlists with params: {'hostname': 'tv-terasa', 'quickconnect_code': '8887779'} +[INFO] [SignageApp] Server response: {'hashed_quickconnect': '$2b$12$UQzlEHNWudAB4P08Le3YJeWHrZHJkWL44kRQpZ53kt.fDLTcrPzGm', 'playlist': [{'duration': 10, 'file_name': 'Cindrel_1.jpg', 'url': 'http://digi-signage.moto-adv.com/media/Cindrel_1.jpg'}, {'duration': 10, 'file_name': 'trans_cindrel_4.jpg', 'url': 'http://digi-signage.moto-adv.com/media/trans_cindrel_4.jpg'}], 'playlist_version': 29} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 29. +[INFO] [SignageApp] Server playlist found with 2 items, version 29 +[INFO] [SignageApp] python_functions: Starting media file download... +[INFO] [SignageApp] python_functions: File Cindrel_1.jpg already exists. Skipping download. +[INFO] [SignageApp] python_functions: File trans_cindrel_4.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 29. +[INFO] [SignageApp] python_functions: Starting load_local_playlist function. +[INFO] [SignageApp] python_functions: Local playlist loaded: {'playlist': [{'file_name': 'Cindrel_1.jpg', 'url': 'static/resurse/Cindrel_1.jpg', 'duration': 10}, {'file_name': 'trans_cindrel_4.jpg', 'url': 'static/resurse/trans_cindrel_4.jpg', 'duration': 10}], 'version': 29} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Successfully loaded 2 items from server +[INFO] [SignageApp] Playing media: Cindrel_1.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/Cindrel_1.jpg +2025-08-23 17:43:13 - STARTED: Cindrel_1.jpg +[INFO] [SignageApp] Successfully displayed image: Cindrel_1.jpg (Original: (4096, 3072), Screen: 1920x1018, Mode: fit, Offset: (281, 0)) +[INFO] [SignageApp] Starting Simple Tkinter Media Player +[INFO] [SignageApp] Media paused +[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] Application exit requested diff --git a/tkinter_app/src/__pycache__/settings_screen.cpython-311.pyc b/tkinter_app/src/__pycache__/settings_screen.cpython-311.pyc index bdd2def05b7b05c78c0e799df83d69e5c965b5ed..00636b5f1909c76eac0f6f13c7e47b083ebc0bb4 100644 GIT binary patch delta 12541 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 delta 6367 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', 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 touch-optimized control panel with larger buttons""" - # Create control frame with larger size for touch - self.control_frame = tk.Frame( - self.root, - bg='#1a1a1a', # Dark background - bd=2, - relief=tk.RAISED, - padx=15, - pady=15 - ) - self.control_frame.place(relx=0.98, rely=0.98, anchor='se') - - # Touch-optimized button configuration - button_config = { - 'bg': '#333333', - 'fg': 'white', - 'activebackground': '#555555', - 'activeforeground': 'white', - 'relief': tk.FLAT, - 'borderwidth': 0, - 'width': 10, # Larger for touch - 'height': 3, # Larger for touch - 'font': ('Segoe UI', 10, 'bold'), # Larger font - 'cursor': 'hand2' - } - - # 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=5) - - # 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, - bg='#27ae60', # Green for play/pause - activebackground='#35d974', - **{k: v for k, v in button_config.items() if k not in ['bg', 'activebackground']} - ) - self.play_pause_btn.grid(row=0, column=1, padx=5) - - # 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=5) - - # Settings button - self.settings_btn = tk.Button( - self.control_frame, - text="⚙️ Settings", - command=self.open_settings, - bg='#9b59b6', # Purple for settings - activebackground='#bb8fce', - **{k: v for k, v in button_config.items() if k not in ['bg', 'activebackground']} - ) - self.settings_btn.grid(row=0, column=3, padx=5) - - # Exit button with special styling - self.exit_btn = tk.Button( - self.control_frame, - text="❌ EXIT", - command=self.show_exit_dialog, - bg='#e74c3c', # Red background - fg='white', - activebackground='#ec7063', - activeforeground='white', - relief=tk.FLAT, - borderwidth=0, - width=8, - height=3, - font=('Segoe UI', 10, 'bold'), - cursor='hand2' - ) - self.exit_btn.grid(row=0, column=4, padx=5) - - # Add touch feedback to all buttons - for button in [self.prev_btn, self.play_pause_btn, self.next_btn, - self.settings_btn, self.exit_btn]: - self.add_touch_feedback_to_control_button(button) - - def scale_image_to_screen(self, img, screen_width, screen_height, mode='fit'): - """ - Scale image to screen with different modes: - - 'fit': Maintain aspect ratio, add black bars if needed (letterbox/pillarbox) - - 'fill': Maintain aspect ratio, crop if needed to fill entire screen - - 'stretch': Ignore aspect ratio, stretch to fill entire screen - """ - img_width, img_height = img.size - - if mode == 'stretch': - # Stretch to fill entire screen, ignoring aspect ratio - return img.resize((screen_width, screen_height), Image.LANCZOS), (0, 0) - - elif mode == 'fill': - # Maintain aspect ratio, crop to fill entire screen - screen_ratio = screen_width / screen_height - img_ratio = img_width / img_height - - if img_ratio > screen_ratio: - # Image is wider - scale by height and crop width - new_height = screen_height - new_width = int(screen_height * img_ratio) - x_offset = (screen_width - new_width) // 2 - y_offset = 0 - else: - # Image is taller - scale by width and crop height - new_width = screen_width - new_height = int(screen_width / img_ratio) - x_offset = 0 - y_offset = (screen_height - new_height) // 2 - - # Resize and crop - img_resized = img.resize((new_width, new_height), Image.LANCZOS) - - # Create final image and paste (this will crop automatically) - final_img = Image.new('RGB', (screen_width, screen_height), 'black') - - # Calculate crop area if image is larger than screen - if new_width > screen_width: - crop_x = (new_width - screen_width) // 2 - img_resized = img_resized.crop((crop_x, 0, crop_x + screen_width, new_height)) - x_offset = 0 - if new_height > screen_height: - crop_y = (new_height - screen_height) // 2 - img_resized = img_resized.crop((0, crop_y, new_width, crop_y + screen_height)) - y_offset = 0 - - final_img.paste(img_resized, (x_offset, y_offset)) - return final_img, (x_offset, y_offset) - - else: # mode == 'fit' (default) - # Maintain aspect ratio, add black bars if needed - screen_ratio = screen_width / screen_height - img_ratio = img_width / img_height - - if img_ratio > screen_ratio: - # Image is wider than screen - fit to width - new_width = screen_width - new_height = int(screen_width / img_ratio) - else: - # Image is taller than screen - fit to height - new_height = screen_height - new_width = int(screen_height * img_ratio) - - # Resize image - img_resized = img.resize((new_width, new_height), Image.LANCZOS) - - # Create black background and center the image - final_img = Image.new('RGB', (screen_width, screen_height), 'black') - x_offset = (screen_width - new_width) // 2 - y_offset = (screen_height - new_height) // 2 - final_img.paste(img_resized, (x_offset, y_offset)) - - return final_img, (x_offset, y_offset) - - def add_touch_feedback_to_control_button(self, button): - """Add touch feedback effects to control panel buttons""" - original_bg = button.cget('bg') - - def on_press(e): - button.configure(relief=tk.SUNKEN) - - def on_release(e): - button.configure(relief=tk.FLAT) - - def on_enter(e): - button.configure(relief=tk.RAISED) - - def on_leave(e): - button.configure(relief=tk.FLAT) - - button.bind("", on_press) - button.bind("", on_release) - button.bind("", on_enter) - button.bind("", on_leave) - - 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." - ) - self.status_label.place(relx=0.5, rely=0.5, anchor='center') - - def show_error_message(self, message): - """Show error message""" - self.image_label.config(image='') - self.status_label.config(text=f"Error: {message}") - self.status_label.place(relx=0.5, rely=0.5, anchor='center') - - 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 using system VLC as a subprocess for robust hardware acceleration and stability.""" - self.status_label.place_forget() - def run_vlc_subprocess(): - try: - Logger.info(f"Starting system VLC subprocess for video: {file_path}") - # Build VLC command - vlc_cmd = [ - 'cvlc', - '--fullscreen', - '--no-osd', - '--no-video-title-show', - '--play-and-exit', - '--quiet', - file_path - ] - proc = subprocess.Popen(vlc_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) - proc.wait() - Logger.info(f"VLC subprocess finished: {file_path}") - except Exception as e: - Logger.error(f"VLC subprocess error: {e}") - finally: - self.root.after_idle(lambda: setattr(self, 'auto_advance_timer', self.root.after(1000, self.next_media))) - threading.Thread(target=run_vlc_subprocess, daemon=True).start() - - def _update_video_frame(self, photo): - """Update video frame from main thread""" - try: - self.image_label.config(image=photo) - self.image_label.image = photo # Keep reference - except Exception as e: - Logger.error(f"Error updating video frame: {e}") - - def _show_video_error(self, error_msg): - """Show video error from main thread""" - try: - self.status_label.config(text=f"Video Error:\n{error_msg}") - self.status_label.place(relx=0.5, rely=0.5, anchor='center') - self.auto_advance_timer = self.root.after(5000, self.next_media) - except Exception as e: - Logger.error(f"Error showing video error: {e}") - self.auto_advance_timer = self.root.after(5000, self.next_media) - - 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 in full screen, properly fitted to screen size""" - try: - # Hide status label and clear any previous text - self.status_label.place_forget() - self.status_label.config(text="") - - if PIL_AVAILABLE: - # Use PIL for better image handling - img = Image.open(file_path) - original_size = img.size - - # Get actual screen dimensions - screen_width = self.root.winfo_width() - screen_height = self.root.winfo_height() - - # Ensure we have valid screen dimensions - if screen_width <= 1 or screen_height <= 1: - screen_width = 1920 # Default fallback - screen_height = 1080 - - # Scale image using the scaling helper - final_img, offset = self.scale_image_to_screen(img, screen_width, screen_height, self.scaling_mode) - - # Convert to PhotoImage - photo = ImageTk.PhotoImage(final_img) - - # Clear previous image and display new one - self.image_label.config(image=photo) - self.image_label.image = photo # Keep reference - - Logger.info(f"Successfully displayed image: {os.path.basename(file_path)} " - f"(Original: {original_size}, Screen: {screen_width}x{screen_height}, " - f"Mode: {self.scaling_mode}, Offset: {offset})") - 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)") - self.status_label.place(relx=0.5, rely=0.5, anchor='center') - Logger.warning("PIL not available - showing text placeholder for image") - - # 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.image_label.config(image='') - self.status_label.config(text=f"Image Error:\n{os.path.basename(file_path)}\n{str(e)}") - self.status_label.place(relx=0.5, rely=0.5, anchor='center') - 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() - elif key == '1': - self.set_scaling_mode('fit') - elif key == '2': - self.set_scaling_mode('fill') - elif key == '3': - self.set_scaling_mode('stretch') - elif event.state & 0x4: # Ctrl key pressed - if key == 's': - self.open_settings() - - self.show_controls() - self.schedule_hide_controls() - - def set_scaling_mode(self, mode): - """Change the scaling mode and refresh current media""" - old_mode = self.scaling_mode - self.scaling_mode = mode - Logger.info(f"Scaling mode changed from '{old_mode}' to '{mode}'") - - # Show temporary notification - self.status_label.config(text=f"Scaling Mode: {mode.title()}\n" - f"1=Fit 2=Fill 3=Stretch") - self.status_label.place(relx=0.5, rely=0.05, anchor='center') - - # Hide notification after 2 seconds - self.root.after(2000, lambda: self.status_label.place_forget()) - - # Refresh current media with new scaling - if self.playlist and 0 <= self.current_index < len(self.playlist): - self.cancel_timers() - self.play_current_media() - - 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 - - # Pause media playback when opening settings - if not self.is_paused: - self.toggle_play_pause() - - self.settings_window = SettingsWindow(self.root, self) - - # Add a callback to resume playback when the settings window is closed - def on_settings_close(): - if self.is_paused: - self.toggle_play_pause() - - self.settings_window.protocol("WM_DELETE_WINDOW", on_settings_close) - - 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 using helper method - self.center_dialog_on_screen(exit_dialog, 400, 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 center_dialog_on_screen(self, dialog, width, height): - """Center a dialog window on screen regardless of screen size""" - dialog.update_idletasks() # Ensure geometry is calculated - screen_width = dialog.winfo_screenwidth() - screen_height = dialog.winfo_screenheight() - - # Calculate center position - center_x = int((screen_width - width) / 2) - center_y = int((screen_height - height) / 2) - - # Ensure the dialog doesn't go off-screen on smaller displays - center_x = max(0, min(center_x, screen_width - width)) - center_y = max(0, min(center_y, screen_height - height)) - - dialog.geometry(f"{width}x{height}+{center_x}+{center_y}") - - # Bring to front and focus - dialog.lift() - dialog.focus_force() - - return center_x, center_y - - 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}") - -