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()