diff --git a/signage_player/__pycache__/get_playlists.cpython-311.pyc b/signage_player/__pycache__/get_playlists.cpython-311.pyc new file mode 100644 index 0000000..5f5622c Binary files /dev/null and b/signage_player/__pycache__/get_playlists.cpython-311.pyc differ diff --git a/signage_player/__pycache__/logging_config.cpython-311.pyc b/signage_player/__pycache__/logging_config.cpython-311.pyc new file mode 100644 index 0000000..815cd39 Binary files /dev/null and b/signage_player/__pycache__/logging_config.cpython-311.pyc differ diff --git a/signage_player/get_playlists.py b/signage_player/get_playlists.py new file mode 100644 index 0000000..7c78fea --- /dev/null +++ b/signage_player/get_playlists.py @@ -0,0 +1,138 @@ +import os +import json +import requests +import bcrypt +import re +from logging_config import Logger + +def is_playlist_up_to_date(local_playlist_path, config): + """ + Compare the version of the local playlist with the server playlist. + Returns True if up-to-date, False otherwise. + """ + import json + if not os.path.exists(local_playlist_path): + Logger.info(f"Local playlist file not found: {local_playlist_path}") + return False + with open(local_playlist_path, 'r') as f: + local_data = json.load(f) + local_version = local_data.get('version', 0) + server_data = fetch_server_playlist(config) + server_version = server_data.get('version', 0) + Logger.info(f"Local playlist version: {local_version}, Server playlist version: {server_version}") + return local_version == server_version + +def fetch_server_playlist(config): + """Fetch the updated playlist from the server using a config dict.""" + server = config.get("server_ip", "") + host = config.get("screen_name", "") + quick = config.get("quickconnect_key", "") + port = config.get("port", "") + try: + ip_pattern = r'^\d+\.\d+\.\d+\.\d+$' + if re.match(ip_pattern, server): + server_url = f'http://{server}:{port}/api/playlists' + else: + server_url = f'http://{server}/api/playlists' + params = { + 'hostname': host, + 'quickconnect_code': quick + } + Logger.info(f"Fetching playlist from URL: {server_url} with params: {params}") + response = requests.get(server_url, params=params) + if response.status_code == 200: + response_data = response.json() + Logger.info(f"Server response: {response_data}") + playlist = response_data.get('playlist', []) + version = response_data.get('playlist_version', None) + hashed_quickconnect = response_data.get('hashed_quickconnect', None) + if version is not None and hashed_quickconnect is not None: + if bcrypt.checkpw(quick.encode('utf-8'), hashed_quickconnect.encode('utf-8')): + Logger.info("Fetched updated playlist from server.") + return {'playlist': playlist, 'version': version} + else: + Logger.error("Quickconnect code validation failed.") + else: + Logger.error("Failed to retrieve playlist or hashed quickconnect from the response.") + else: + Logger.error(f"Failed to fetch playlist. Status Code: {response.status_code}") + except requests.exceptions.RequestException as e: + Logger.error(f"Failed to fetch playlist: {e}") + return {'playlist': [], 'version': 0} + +def save_playlist_with_version(playlist_data, playlist_dir): + version = playlist_data.get('version', 0) + playlist_file = os.path.join(playlist_dir, f'server_playlist_v{version}.json') + with open(playlist_file, 'w') as f: + json.dump(playlist_data, f, indent=2) + print(f"Playlist saved to {playlist_file}") + return playlist_file + + +def download_media_files(playlist, media_dir): + """Download media files from the server and save them to media_dir.""" + if not os.path.exists(media_dir): + os.makedirs(media_dir) + Logger.info(f"Created directory {media_dir} for media files.") + + updated_playlist = [] + for media in playlist: + file_name = media.get('file_name', '') + file_url = media.get('url', '') + duration = media.get('duration', 10) + local_path = os.path.join(media_dir, file_name) + Logger.info(f"Preparing to download {file_name} from {file_url}...") + if os.path.exists(local_path): + Logger.info(f"File {file_name} already exists. Skipping download.") + else: + try: + response = requests.get(file_url, timeout=10) + if response.status_code == 200: + with open(local_path, 'wb') as file: + file.write(response.content) + Logger.info(f"Successfully downloaded {file_name} to {local_path}") + else: + Logger.error(f"Failed to download {file_name}. Status Code: {response.status_code}") + continue + except requests.exceptions.RequestException as e: + Logger.error(f"Error downloading {file_name}: {e}") + continue + updated_media = { + 'file_name': file_name, + 'url': os.path.relpath(local_path, os.path.dirname(media_dir)), + 'duration': duration + } + updated_playlist.append(updated_media) + return updated_playlist + +def update_playlist_if_needed(local_playlist_path, config, media_dir, playlist_dir): + """ + Fetch the server playlist once, compare versions, and update if needed. + Returns True if updated, False if already up to date. + """ + import json + server_data = fetch_server_playlist(config) + server_version = server_data.get('version', 0) + if not os.path.exists(local_playlist_path): + local_version = 0 + else: + with open(local_playlist_path, 'r') as f: + local_data = json.load(f) + local_version = local_data.get('version', 0) + Logger.info(f"Local playlist version: {local_version}, Server playlist version: {server_version}") + if local_version != server_version: + if server_data and server_data.get('playlist'): + updated_playlist = download_media_files(server_data['playlist'], media_dir) + server_data['playlist'] = updated_playlist + save_playlist_with_version(server_data, playlist_dir) + return True + else: + Logger.warning("No playlist data fetched from server or playlist is empty.") + return False + else: + Logger.info("Local playlist is already up to date.") + return False + + + + diff --git a/signage_player/logging_config.py b/signage_player/logging_config.py new file mode 100644 index 0000000..0ace510 --- /dev/null +++ b/signage_player/logging_config.py @@ -0,0 +1,19 @@ +import logging +import os + +# Path to the log file (in main_data for new app) +LOG_FILE_PATH = os.path.join(os.path.dirname(__file__), 'main_data', 'log.txt') + +Logger = logging.getLogger('SignageApp') +Logger.setLevel(logging.INFO) + +file_handler = logging.FileHandler(LOG_FILE_PATH, mode='a') +file_handler.setLevel(logging.INFO) +formatter = logging.Formatter('[%(levelname)s] [%(name)s] %(message)s') +file_handler.setFormatter(formatter) +Logger.addHandler(file_handler) + +stream_handler = logging.StreamHandler() +stream_handler.setLevel(logging.INFO) +stream_handler.setFormatter(formatter) +Logger.addHandler(stream_handler) diff --git a/signage_player/main.py b/signage_player/main.py new file mode 100644 index 0000000..f20f1df --- /dev/null +++ b/signage_player/main.py @@ -0,0 +1,42 @@ +import threading +import time +import os +import json +from get_playlists import update_playlist_if_needed + +CONFIG_PATH = os.path.join(os.path.dirname(__file__), 'main_data', 'app_config.txt') +MEDIA_DATA_PATH = os.path.join(os.path.dirname(__file__), 'static_data', 'media') +PLAYLIST_DIR = os.path.join(os.path.dirname(__file__), 'static_data', 'playlist') +LOCAL_PLAYLIST_PATH = os.path.join(PLAYLIST_DIR, 'server_playlist.json') + +def playlist_update_loop(refresh_time, stop_event, config, local_playlist_path, media_dir, playlist_dir): + while not stop_event.is_set(): + updated = update_playlist_if_needed(local_playlist_path, config, media_dir, playlist_dir) + if updated: + print(f"[REFRESH] Playlist updated from server at {time.strftime('%X')}") + else: + print(f"[REFRESH] Playlist already up to date at {time.strftime('%X')}") + time.sleep(refresh_time) + +def main(): + with open(CONFIG_PATH, 'r') as f: + config = json.load(f) + refresh_time = int(config.get('refresh_time', 5)) * 60 # minutes + stop_event = threading.Event() + update_thread = threading.Thread( + target=playlist_update_loop, + args=(refresh_time, stop_event, config, LOCAL_PLAYLIST_PATH, MEDIA_DATA_PATH, PLAYLIST_DIR), + daemon=True + ) + update_thread.start() + print("Playlist update thread started. Press Ctrl+C to exit.") + try: + while True: + time.sleep(1) + except KeyboardInterrupt: + print("Exiting...") + stop_event.set() + update_thread.join() + +if __name__ == '__main__': + main() diff --git a/signage_player/main_data/app_config.txt b/signage_player/main_data/app_config.txt new file mode 100644 index 0000000..6639942 --- /dev/null +++ b/signage_player/main_data/app_config.txt @@ -0,0 +1,10 @@ +{ + "screen_orientation": "Landscape", + "screen_name": "tv-terasa", + "quickconnect_key": "8887779", + "server_ip": "digi-signage.moto-adv.com", + "port": "8880", + "screen_w": "1920", + "screen_h": "1080", + "refresh_time": "5" +} diff --git a/signage_player/main_data/log.txt b/signage_player/main_data/log.txt new file mode 100644 index 0000000..8f385ae --- /dev/null +++ b/signage_player/main_data/log.txt @@ -0,0 +1,22 @@ +[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'}, {'duration': 14, 'file_name': '101394-video-1080.mp4', 'url': 'http://digi-signage.moto-adv.com/media/101394-video-1080.mp4'}], 'playlist_version': 30} +[INFO] [SignageApp] Fetched updated playlist from 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'}, {'duration': 14, 'file_name': '101394-video-1080.mp4', 'url': 'http://digi-signage.moto-adv.com/media/101394-video-1080.mp4'}], 'playlist_version': 30} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] Preparing to download Cindrel_1.jpg from http://digi-signage.moto-adv.com/media/Cindrel_1.jpg... +[INFO] [SignageApp] Successfully downloaded Cindrel_1.jpg to /home/pi/Desktop/tkinter_player/signage_player/static_data/media/Cindrel_1.jpg +[INFO] [SignageApp] Preparing to download trans_cindrel_4.jpg from http://digi-signage.moto-adv.com/media/trans_cindrel_4.jpg... +[INFO] [SignageApp] Successfully downloaded trans_cindrel_4.jpg to /home/pi/Desktop/tkinter_player/signage_player/static_data/media/trans_cindrel_4.jpg +[INFO] [SignageApp] Preparing to download 101394-video-1080.mp4 from http://digi-signage.moto-adv.com/media/101394-video-1080.mp4... +[ERROR] [SignageApp] Failed to download 101394-video-1080.mp4. Status Code: 404 +[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'}, {'duration': 14, 'file_name': '101394-video-1080.mp4', 'url': 'http://digi-signage.moto-adv.com/media/101394-video-1080.mp4'}], 'playlist_version': 30} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] Local playlist version: 0, Server playlist version: 30 +[INFO] [SignageApp] Preparing to download Cindrel_1.jpg from http://digi-signage.moto-adv.com/media/Cindrel_1.jpg... +[INFO] [SignageApp] Successfully downloaded Cindrel_1.jpg to /home/pi/Desktop/tkinter_player/signage_player/static_data/media/Cindrel_1.jpg +[INFO] [SignageApp] Preparing to download trans_cindrel_4.jpg from http://digi-signage.moto-adv.com/media/trans_cindrel_4.jpg... +[INFO] [SignageApp] Successfully downloaded trans_cindrel_4.jpg to /home/pi/Desktop/tkinter_player/signage_player/static_data/media/trans_cindrel_4.jpg +[INFO] [SignageApp] Preparing to download 101394-video-1080.mp4 from http://digi-signage.moto-adv.com/media/101394-video-1080.mp4... +[ERROR] [SignageApp] Failed to download 101394-video-1080.mp4. Status Code: 404 diff --git a/signage_player/player.py b/signage_player/player.py new file mode 100644 index 0000000..57cf958 --- /dev/null +++ b/signage_player/player.py @@ -0,0 +1,158 @@ +import os +import json +import tkinter as tk +from PIL import Image, ImageTk + +CONFIG_PATH = os.path.join(os.path.dirname(__file__), 'main_data', 'app_config.txt') +PLAYLIST_DIR = os.path.join(os.path.dirname(__file__), 'static_data', 'playlist') +MEDIA_DATA_PATH = os.path.join(os.path.dirname(__file__), 'static_data', 'media') + +class SimpleTkPlayer: + def __init__(self, root, playlist): + self.root = root + self.playlist = playlist + self.current_index = 0 + self.paused = False + self.pause_timer = None + self.label = tk.Label(root, bg='black') + self.label.pack(fill=tk.BOTH, expand=True) + self.create_controls() + self.hide_controls() + self.root.bind('', self.on_activity) + self.root.bind('', self.on_activity) + self.show_current_media() + self.root.after(100, self.next_media_loop) + + def create_controls(self): + self.controls_frame = tk.Frame(self.root, bg='#222') + self.controls_frame.place(relx=0.98, rely=0.98, anchor='se') + self.prev_btn = tk.Button(self.controls_frame, text='⏮ Prev', command=self.prev_media, width=8) + self.prev_btn.grid(row=0, column=0, padx=2) + self.pause_btn = tk.Button(self.controls_frame, text='⏸ Pause', command=self.toggle_pause, width=8) + self.pause_btn.grid(row=0, column=1, padx=2) + self.next_btn = tk.Button(self.controls_frame, text='Next ⏭', command=self.next_media, width=8) + self.next_btn.grid(row=0, column=2, padx=2) + self.settings_btn = tk.Button(self.controls_frame, text='⚙ Settings', command=self.open_settings, width=10) + self.settings_btn.grid(row=0, column=3, padx=2) + self.exit_btn = tk.Button(self.controls_frame, text='⏻ Exit', command=self.exit_app, width=8, fg='red') + self.exit_btn.grid(row=0, column=4, padx=2) + + def show_controls(self): + self.controls_frame.place(relx=0.98, rely=0.98, anchor='se') + self.controls_frame.lift() + self.schedule_hide_controls() + + def hide_controls(self): + self.controls_frame.place_forget() + + def schedule_hide_controls(self): + if hasattr(self, 'hide_controls_timer') and self.hide_controls_timer: + self.root.after_cancel(self.hide_controls_timer) + self.hide_controls_timer = self.root.after(5000, self.hide_controls) + + def on_activity(self, event=None): + self.show_controls() + + def prev_media(self): + self.current_index = (self.current_index - 1) % len(self.playlist) + self.show_current_media() + + def next_media(self): + self.current_index = (self.current_index + 1) % len(self.playlist) + self.show_current_media() + + def toggle_pause(self): + if not self.paused: + self.paused = True + self.pause_btn.config(text='▶ Resume') + self.pause_timer = self.root.after(30000, self.resume_play) + else: + self.resume_play() + + def resume_play(self): + self.paused = False + self.pause_btn.config(text='⏸ Pause') + if self.pause_timer: + self.root.after_cancel(self.pause_timer) + self.pause_timer = None + + def show_current_media(self): + if not self.playlist: + self.label.config(text="No media available", fg='white', font=('Arial', 32)) + return + media = self.playlist[self.current_index] + file_path = os.path.join(MEDIA_DATA_PATH, media['file_name']) + if file_path.lower().endswith(('.jpg', '.jpeg', '.png', '.bmp', '.gif')): + try: + img = Image.open(file_path) + # Fit to screen without crop or stretch + screen_w = self.root.winfo_screenwidth() + screen_h = self.root.winfo_screenheight() + img_w, img_h = img.size + scale = min(screen_w / img_w, screen_h / img_h) + new_w = int(img_w * scale) + new_h = int(img_h * scale) + img = img.resize((new_w, new_h), Image.LANCZOS) + bg = Image.new('RGB', (screen_w, screen_h), 'black') + x = (screen_w - new_w) // 2 + y = (screen_h - new_h) // 2 + bg.paste(img, (x, y)) + photo = ImageTk.PhotoImage(bg) + self.label.config(image=photo, text='') + self.label.image = photo + except Exception as e: + self.label.config(text=f"Image error: {e}", fg='red') + else: + self.label.config(text=f"Unsupported: {media['file_name']}", fg='yellow') + + def next_media_loop(self): + if not self.playlist or self.paused: + self.root.after(1000, self.next_media_loop) + return + duration = self.playlist[self.current_index].get('duration', 10) + self.current_index = (self.current_index + 1) % len(self.playlist) + self.show_current_media() + self.root.after(duration * 1000, self.next_media_loop) + + def exit_app(self): + self.root.destroy() + + def open_settings(self): + if self.paused is not True: + self.paused = True + self.pause_btn.config(text='▶ Resume') + settings_win = tk.Toplevel(self.root) + settings_win.title('Settings') + settings_win.geometry('400x300+100+100') + settings_win.transient(self.root) + settings_win.grab_set() + tk.Label(settings_win, text='Settings', font=('Arial', 18)).pack(pady=10) + # Example setting: close button + tk.Button(settings_win, text='Close', command=settings_win.destroy).pack(pady=20) + def on_close(): + settings_win.grab_release() + settings_win.destroy() + self.resume_play() + settings_win.protocol('WM_DELETE_WINDOW', on_close) + settings_win.bind('', lambda e: self.resume_play() if not settings_win.winfo_exists() else None) + +def load_latest_playlist(): + files = [f for f in os.listdir(PLAYLIST_DIR) if f.startswith('server_playlist_v') and f.endswith('.json')] + if not files: + return [] + files.sort(key=lambda x: int(x.split('_v')[-1].split('.json')[0]), reverse=True) + with open(os.path.join(PLAYLIST_DIR, files[0]), 'r') as f: + data = json.load(f) + return data.get('playlist', []) + +def main(): + root = tk.Tk() + root.title("Simple Tkinter Player") + root.configure(bg='black') + root.attributes('-fullscreen', True) + playlist = load_latest_playlist() + player = SimpleTkPlayer(root, playlist) + root.mainloop() + +if __name__ == '__main__': + main() diff --git a/signage_player/static_data/media/Cindrel_1.jpg b/signage_player/static_data/media/Cindrel_1.jpg new file mode 100644 index 0000000..2579223 Binary files /dev/null and b/signage_player/static_data/media/Cindrel_1.jpg differ diff --git a/signage_player/static_data/media/trans_cindrel_4.jpg b/signage_player/static_data/media/trans_cindrel_4.jpg new file mode 100644 index 0000000..6897121 Binary files /dev/null and b/signage_player/static_data/media/trans_cindrel_4.jpg differ diff --git a/signage_player/static_data/playlist/server_playlist_v30.json b/signage_player/static_data/playlist/server_playlist_v30.json new file mode 100644 index 0000000..285da5d --- /dev/null +++ b/signage_player/static_data/playlist/server_playlist_v30.json @@ -0,0 +1,15 @@ +{ + "playlist": [ + { + "file_name": "Cindrel_1.jpg", + "url": "media/Cindrel_1.jpg", + "duration": 10 + }, + { + "file_name": "trans_cindrel_4.jpg", + "url": "media/trans_cindrel_4.jpg", + "duration": 10 + } + ], + "version": 30 +} \ No newline at end of file diff --git a/tkinter_app/resources/app_config.txt b/tkinter_app/resources/app_config.txt index 32c4736..386e08f 100644 --- a/tkinter_app/resources/app_config.txt +++ b/tkinter_app/resources/app_config.txt @@ -6,5 +6,5 @@ "port": "8880", "screen_w": "1920", "screen_h": "1080", - "playlist_version": 29 + "playlist_version": 30 } \ No newline at end of file diff --git a/tkinter_app/resources/local_playlist.json b/tkinter_app/resources/local_playlist.json index 6c049f5..386b8a2 100644 --- a/tkinter_app/resources/local_playlist.json +++ b/tkinter_app/resources/local_playlist.json @@ -11,5 +11,5 @@ "duration": 10 } ], - "version": 29 + "version": 30 } \ No newline at end of file diff --git a/tkinter_app/resources/log.txt b/tkinter_app/resources/log.txt index 22db532..6a2bfbd 100644 --- a/tkinter_app/resources/log.txt +++ b/tkinter_app/resources/log.txt @@ -5458,3 +5458,245 @@ [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-24 00:06:56 - 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] 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_local_playlist function. +[INFO] [SignageApp] [MAIN] About to show splash screen: /home/pi/Desktop/tkinter_player/tkinter_app/resources/intro1.mp4 +[INFO] [SignageApp] [SplashScreen] Running splash as standalone VLC subprocess: /home/pi/Desktop/tkinter_player/tkinter_app/resources/intro1.mp4 +[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] [SplashScreen] Launching: cvlc --fullscreen --no-osd --no-video-title-show --play-and-exit --quiet /home/pi/Desktop/tkinter_player/tkinter_app/resources/intro1.mp4 +[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] [SplashScreen] VLC splash finished. +[INFO] [SignageApp] [MAIN] splash.show() called, entering mainloop... +[INFO] [SignageApp] [MAIN] Splash finished, waiting for playlist... +[INFO] [SignageApp] [MAIN] Playlist loaded: [{'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}] +[INFO] [SignageApp] Playing media: Cindrel_1.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/Cindrel_1.jpg +2025-08-24 00:08:44 - STARTED: Cindrel_1.jpg +[INFO] [SignageApp] Successfully displayed image: Cindrel_1.jpg (Original: (4096, 3072), Screen: 1920x1080, Mode: fit, Offset: (240, 0)) +[INFO] [SignageApp] [MAIN] Player UI and playback started. +[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-24 00:08:55 - 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] 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_local_playlist function. +[INFO] [SignageApp] [MAIN] About to show splash screen: /home/pi/Desktop/tkinter_player/tkinter_app/resources/intro1.mp4 +[INFO] [SignageApp] [SplashScreen] Running splash as standalone VLC subprocess: /home/pi/Desktop/tkinter_player/tkinter_app/resources/intro1.mp4 +[INFO] [SignageApp] [SplashScreen] Launching: cvlc --fullscreen --no-osd --no-video-title-show --play-and-exit --quiet /home/pi/Desktop/tkinter_player/tkinter_app/resources/intro1.mp4 +[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'}, {'duration': 14, 'file_name': '101394-video-1080.mp4', 'url': 'http://digi-signage.moto-adv.com/media/101394-video-1080.mp4'}], 'playlist_version': 30} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 30. +[INFO] [SignageApp] Server playlist found with 3 items, version 30 +[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. +[ERROR] [SignageApp] python_functions: Failed to download 101394-video-1080.mp4. Status Code: 404 +[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 30. +[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': 30} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Successfully loaded 2 items from server +[INFO] [SignageApp] [SplashScreen] VLC splash finished. +[INFO] [SignageApp] [MAIN] splash.show() called, entering mainloop... +[INFO] [SignageApp] [MAIN] Splash finished, waiting for playlist... +[INFO] [SignageApp] [MAIN] Playlist loaded: [{'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}] +[INFO] [SignageApp] Playing media: Cindrel_1.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/Cindrel_1.jpg +2025-08-24 17:50:25 - STARTED: Cindrel_1.jpg +[INFO] [SignageApp] Successfully displayed image: Cindrel_1.jpg (Original: (4096, 3072), Screen: 1920x1080, Mode: fit, Offset: (240, 0)) +[INFO] [SignageApp] [MAIN] Player UI and playback started. +[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-24 17:50:36 - 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] Playing media: Cindrel_1.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/Cindrel_1.jpg +2025-08-24 17:50:47 - STARTED: Cindrel_1.jpg +[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-24 17:50:58 - 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] Playing media: Cindrel_1.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/Cindrel_1.jpg +2025-08-24 17:51:09 - STARTED: Cindrel_1.jpg +[INFO] [SignageApp] Successfully displayed image: Cindrel_1.jpg (Original: (4096, 3072), Screen: 1920x1018, Mode: fit, Offset: (281, 0)) +[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_local_playlist function. +[INFO] [SignageApp] [MAIN] About to show splash screen: /home/pi/Desktop/tkinter_player/tkinter_app/resources/intro1.mp4 +[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': 30} +[INFO] [SignageApp] [SplashScreen] Running splash as standalone VLC subprocess: /home/pi/Desktop/tkinter_player/tkinter_app/resources/intro1.mp4 +[INFO] [SignageApp] [SplashScreen] Launching: cvlc --fullscreen --no-osd --no-video-title-show --play-and-exit --quiet /home/pi/Desktop/tkinter_player/tkinter_app/resources/intro1.mp4 +[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'}, {'duration': 14, 'file_name': '101394-video-1080.mp4', 'url': 'http://digi-signage.moto-adv.com/media/101394-video-1080.mp4'}], 'playlist_version': 30} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 30. +[INFO] [SignageApp] Server playlist found with 3 items, version 30 +[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. +[ERROR] [SignageApp] python_functions: Failed to download 101394-video-1080.mp4. Status Code: 404 +[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 30. +[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': 30} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Successfully loaded 2 items from server +[INFO] [SignageApp] [SplashScreen] VLC splash finished. +[INFO] [SignageApp] [MAIN] splash.show() called, entering mainloop... +[INFO] [SignageApp] [MAIN] Splash finished, waiting for playlist... +[INFO] [SignageApp] [MAIN] Playlist loaded: [{'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}] +[INFO] [SignageApp] Playing media: Cindrel_1.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/Cindrel_1.jpg +2025-08-24 17:52:11 - STARTED: Cindrel_1.jpg +[INFO] [SignageApp] Successfully displayed image: Cindrel_1.jpg (Original: (4096, 3072), Screen: 1920x1080, Mode: fit, Offset: (240, 0)) +[INFO] [SignageApp] [MAIN] Player UI and playback started. +[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-24 17:52:23 - 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] 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_local_playlist function. +[INFO] [SignageApp] [MAIN] About to show splash screen: /home/pi/Desktop/tkinter_player/tkinter_app/resources/intro1.mp4 +[INFO] [SignageApp] [SplashScreen] Running splash as standalone VLC subprocess: /home/pi/Desktop/tkinter_player/tkinter_app/resources/intro1.mp4 +[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': 30} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] [SplashScreen] Launching: cvlc --fullscreen --no-osd --no-video-title-show --play-and-exit --quiet /home/pi/Desktop/tkinter_player/tkinter_app/resources/intro1.mp4 +[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'}, {'duration': 14, 'file_name': '101394-video-1080.mp4', 'url': 'http://digi-signage.moto-adv.com/media/101394-video-1080.mp4'}], 'playlist_version': 30} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 30. +[INFO] [SignageApp] Server playlist found with 3 items, version 30 +[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. +[ERROR] [SignageApp] python_functions: Failed to download 101394-video-1080.mp4. Status Code: 404 +[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 30. +[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': 30} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Successfully loaded 2 items from server +[INFO] [SignageApp] [SplashScreen] VLC splash finished. +[INFO] [SignageApp] [MAIN] splash.show() called, entering mainloop... +[INFO] [SignageApp] [MAIN] Splash finished, waiting for playlist... +[INFO] [SignageApp] [MAIN] Playlist loaded: [{'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}] +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Playing media: Cindrel_1.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/Cindrel_1.jpg +2025-08-24 17:57: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] [MAIN] Player UI and playback started. +[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-24 17:57:50 - 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] Playing media: Cindrel_1.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/Cindrel_1.jpg +2025-08-24 17:58:02 - STARTED: Cindrel_1.jpg +[INFO] [SignageApp] Successfully displayed image: Cindrel_1.jpg (Original: (4096, 3072), Screen: 1920x1080, Mode: fit, Offset: (240, 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-24 17:58:13 - 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] 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_local_playlist function. +[INFO] [SignageApp] [MAIN] About to show splash screen: /home/pi/Desktop/tkinter_player/tkinter_app/resources/intro1.mp4 +[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': 30} +[INFO] [SignageApp] [SplashScreen] Running splash as standalone VLC subprocess: /home/pi/Desktop/tkinter_player/tkinter_app/resources/intro1.mp4 +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] [SplashScreen] Launching: cvlc --fullscreen --no-osd --no-video-title-show --play-and-exit --quiet /home/pi/Desktop/tkinter_player/tkinter_app/resources/intro1.mp4 +[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'}, {'duration': 14, 'file_name': '101394-video-1080.mp4', 'url': 'http://digi-signage.moto-adv.com/media/101394-video-1080.mp4'}], 'playlist_version': 30} +[INFO] [SignageApp] Fetched updated playlist from server. +[INFO] [SignageApp] python_functions: Updated playlist version in app_config.txt to 30. +[INFO] [SignageApp] Server playlist found with 3 items, version 30 +[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. +[ERROR] [SignageApp] python_functions: Failed to download 101394-video-1080.mp4. Status Code: 404 +[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 30. +[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': 30} +[INFO] [SignageApp] python_functions: Finished load_local_playlist function successfully. +[INFO] [SignageApp] Successfully loaded 2 items from server +[INFO] [SignageApp] [SplashScreen] VLC splash finished. +[INFO] [SignageApp] [MAIN] splash.show() called, entering mainloop... +[INFO] [SignageApp] [MAIN] Splash finished, waiting for playlist... +[INFO] [SignageApp] [MAIN] Playlist loaded: [{'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}] +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[INFO] [SignageApp] Playing media: Cindrel_1.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/Cindrel_1.jpg +2025-08-24 18:10:56 - STARTED: Cindrel_1.jpg +[INFO] [SignageApp] Successfully displayed image: Cindrel_1.jpg (Original: (4096, 3072), Screen: 1920x1080, Mode: fit, Offset: (240, 0)) +[INFO] [SignageApp] [MAIN] Player UI and playback started. +[INFO] [SignageApp] python_functions: Starting load_config function. +[INFO] [SignageApp] python_functions: Configuration file loaded successfully. +[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-24 18:11:09 - 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] Playing media: Cindrel_1.jpg from /home/pi/Desktop/tkinter_player/tkinter_app/src/static/resurse/Cindrel_1.jpg +2025-08-24 18:11:20 - STARTED: Cindrel_1.jpg +[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-24 18:11:25 - STARTED: trans_cindrel_4.jpg +[INFO] [SignageApp] Successfully displayed image: trans_cindrel_4.jpg (Original: (4000, 3000), Screen: 1920x1018, Mode: fit, Offset: (281, 0)) + \ No newline at end of file diff --git a/tkinter_app/src/__pycache__/player_ui.cpython-311.pyc b/tkinter_app/src/__pycache__/player_ui.cpython-311.pyc index 180ce84..6053d66 100644 Binary files a/tkinter_app/src/__pycache__/player_ui.cpython-311.pyc and b/tkinter_app/src/__pycache__/player_ui.cpython-311.pyc differ diff --git a/tkinter_app/src/main.py b/tkinter_app/src/main.py index e573c59..96408c9 100644 --- a/tkinter_app/src/main.py +++ b/tkinter_app/src/main.py @@ -16,6 +16,7 @@ from media_playback_controller import MediaPlaybackController from splash_screen import SplashScreen from playlist_manager import PlaylistManager from threading import Thread +from settings_screen import SettingsWindow if __name__ == "__main__": import tkinter as tk @@ -43,8 +44,23 @@ if __name__ == "__main__": playlist = playlist_manager.wait_for_playlist() Logger.info(f"[MAIN] Playlist loaded: {playlist}") Thread(target=preload_first_media, args=(playlist,), daemon=True).start() - ui = PlayerUI(root) - playback = MediaPlaybackController(app=None, ui=ui) + # Create playback first so we can reference its methods in callbacks + playback = MediaPlaybackController(app=None, ui=None) # UI will be set after + def open_settings(): + SettingsWindow(root, None) + def show_exit(): + root.quit() + control_callbacks = { + 'prev': playback.previous_media, + 'play_pause': playback.toggle_play_pause, + 'next': playback.next_media, + 'settings': open_settings, + 'exit': show_exit + } + ui = PlayerUI(root, control_callbacks=control_callbacks) + ui.setup_window() # Ensure fullscreen and geometry + ui.bind_show_controls_on_activity() # Bind activity to show controls + playback.ui = ui # Set UI reference now that it's created playback.set_playlist(playlist or []) playback.play_current_media() Logger.info("[MAIN] Player UI and playback started.") diff --git a/tkinter_app/src/player_ui.py b/tkinter_app/src/player_ui.py index fa13be1..6d9b815 100644 --- a/tkinter_app/src/player_ui.py +++ b/tkinter_app/src/player_ui.py @@ -14,6 +14,25 @@ class PlayerUI: self.exit_btn = None self.settings_btn = None self.hide_controls_timer = None + # Set fullscreen and geometry before packing widgets + self.root.title("Simple Signage Player") + self.root.configure(bg='black') + try: + config = None + try: + from python_functions import load_config + config = load_config() + except Exception: + pass + width = int(config.get('screen_w', 1920)) if config else 1920 + height = int(config.get('screen_h', 1080)) if config else 1080 + self.scaling_mode = config.get('scaling_mode', 'fit') if config else 'fit' + except: + width, height = 1920, 1080 + self.scaling_mode = 'fit' + self.root.geometry(f"{width}x{height}") + self.root.attributes('-fullscreen', True) + self.root.focus_set() self.setup_ui(control_callbacks) def setup_ui(self, control_callbacks=None): @@ -160,3 +179,11 @@ class PlayerUI: y_offset = (screen_height - new_height) // 2 final_img.paste(img_resized, (x_offset, y_offset)) return final_img, (x_offset, y_offset) + + def bind_show_controls_on_activity(self): + def on_activity(event=None): + self.show_controls() + self.schedule_hide_controls() + self.root.bind('', on_activity) + self.root.bind('', on_activity) + self.root.bind('', on_activity)