import os import re import sys import time import math import random import datetime import threading import subprocess import requests from io import BytesIO import tkinter as tk from PIL import Image, ImageTk, ImageDraw import pythoncom # Pokus o automatickou instalaci pycaw, pokud chybí try: from pycaw.pycaw import AudioUtilities HAS_PYCAW = True except ImportError: print("📦 Instaluji potřebnou knihovnu 'pycaw' pro okamžité ztlumení Windows...") try: subprocess.check_call([sys.executable, "-m", "pip", "install", "pycaw", "comtypes"], creationflags=getattr(subprocess, 'CREATE_NO_WINDOW', 0)) from pycaw.pycaw import AudioUtilities HAS_PYCAW = True print("✅ Knihovna pycaw byla úspěšně nainstalována!") except Exception as e: print(f"⚠️ Automatická instalace pycaw se nezdařila: {e}") HAS_PYCAW = False # Pokus o import winsound (standardně na Windows) try: import winsound HAS_WINSOUND = True except ImportError: HAS_WINSOUND = False # Pokus o import pygame try: import pygame pygame.mixer.init() HAS_PYGAME = True except Exception: HAS_PYGAME = False SERVER_URL = "http://localhost:8080/endpoint" CYCLE_INTERVAL_MS = 10000 # Doba zobrazení produktu (10 sekund) FRAME_INTERVAL_MS = 120 # Rychlost 3D rotace fotek (120 ms) PARTICLE_COUNT = 60 # Počet bokeh částic SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) KATALOG_DIR = os.path.join(SCRIPT_DIR, "katalog") if not os.path.exists(KATALOG_DIR): KATALOG_DIR = os.path.join(SCRIPT_DIR, "documents", "katalog") # Složka se zvuky pro reklamy SOUNDS_DIR = os.path.join(SCRIPT_DIR, "zvuky_reklama") if not os.path.exists(SOUNDS_DIR): os.makedirs(SOUNDS_DIR, exist_ok=True) def natural_sort_key(s): """ Řazení souborů podle čísel (image1, image2, ..., image10) """ return [int(text) if text.isdigit() else text.lower() for text in re.split(r'(\d+)', s)] def set_spotify_mute(mute=True): """ Bleskově ztlumí VŠECHNY aplikace ve Windows kromě samotného Pythonu """ pythoncom.CoInitialize() if HAS_PYCAW: try: sessions = AudioUtilities.GetAllSessions() for session in sessions: try: pname = session.Process.name().lower() if session.Process else "" if not any(keep in pname for keep in ["python", "wmplayer"]): volume = session.SimpleAudioVolume volume.SetMute(1 if mute else 0, None) except Exception: pass return except Exception as e: print(f"Pycaw chyba: {e}") nircmd_path = os.path.join(SCRIPT_DIR, "nircmd.exe") if os.path.exists(nircmd_path): try: val = "1" if mute else "0" for proc in ["spotify.exe", "chrome.exe", "msedge.exe", "brave.exe", "firefox.exe", "opera.exe"]: subprocess.run( [nircmd_path, "muteappvolume", proc, val], creationflags=getattr(subprocess, 'CREATE_NO_WINDOW', 0) ) except Exception as e: print(f"NirCmd chyba: {e}") class AdvancedParticle: """ Třída pro vícevrstvé animované bokeh částice """ def __init__(self, w=1920, h=1080): self.w = w self.h = h self.reset(random_y=True) def reset(self, random_y=False): self.x = random.uniform(0, self.w) self.y = random.uniform(0, self.h) if random_y else self.h + random.uniform(20, 80) self.p_type = random.choice(['bokeh_large', 'bokeh_medium', 'spark']) if self.p_type == 'bokeh_large': self.base_radius = random.uniform(40, 90) self.speed_y = random.uniform(0.2, 0.6) self.sway_amp = random.uniform(1.0, 3.0) self.sway_freq = random.uniform(0.005, 0.015) self.color_outer = random.choice(["#2a0845", "#3c096c", "#1e0034", "#480ca8", "#38040e"]) self.color_inner = random.choice(["#5a189a", "#7b2cbf", "#9d4edd", "#7209b7", "#9e2a2b"]) elif self.p_type == 'bokeh_medium': self.base_radius = random.uniform(18, 38) self.speed_y = random.uniform(0.5, 1.2) self.sway_amp = random.uniform(1.5, 4.0) self.sway_freq = random.uniform(0.01, 0.025) self.color_outer = random.choice(["#5a189a", "#7209b7", "#b5179e", "#480ca8"]) self.color_inner = random.choice(["#e0aaff", "#f72585", "#c77dff", "#ffb703"]) else: # spark self.base_radius = random.uniform(2, 5) self.speed_y = random.uniform(0.8, 2.2) self.sway_amp = random.uniform(0.5, 2.0) self.sway_freq = random.uniform(0.02, 0.05) self.color_outer = "#ffffff" self.color_inner = random.choice(["#ffffff", "#ffee83", "#4cc9f0", "#ffb703"]) self.radius = self.base_radius self.pulse_phase = random.uniform(0, 2 * math.pi) self.pulse_speed = random.uniform(0.015, 0.04) self.phase = random.uniform(0, 2 * math.pi) def update(self, w, h): self.w = w self.h = h self.y -= self.speed_y self.phase += self.sway_freq self.x += math.sin(self.phase) * self.sway_amp self.pulse_phase += self.pulse_speed pulse_factor = 1.0 + 0.22 * math.sin(self.pulse_phase) self.radius = self.base_radius * pulse_factor if self.y < -self.radius * 2 or self.x < -self.radius or self.x > self.w + self.radius: self.reset(random_y=False) class FullscreenJarmarkDisplay: def __init__(self, root): self.root = root self.root.title("Jarmark Display Kiosk") self.root.attributes("-fullscreen", True) self.w = 1920 self.h = 1080 # Klávesové zkratky self.root.bind("", lambda e: self.root.destroy()) self.root.bind("", lambda e: self.next_product()) self.root.bind("", lambda e: self.prev_product()) self.root.bind("", lambda e: self.toggle_pause()) self.root.bind("", lambda e: self.reload_catalog_action()) self.root.bind("", lambda e: self.reload_catalog_action()) # Plátno s čistým tmavým pozadím self.canvas = tk.Canvas(root, bg="#07010f", highlightthickness=0) self.canvas.pack(fill=tk.BOTH, expand=True) self.canvas.bind("", self.on_resize) # Částice self.particles = [AdvancedParticle(self.w, self.h) for _ in range(PARTICLE_COUNT)] self.particle_draw_ids = [] self.init_particles_canvas() # Canvas Prvky - Název produktu se stínem pro skvělou čitelnost bez rámčku self.title_shadow_id = self.canvas.create_text(0, 0, text="", fill="#000000", font=("Impact", 42, "bold"), anchor="center") self.title_text_id = self.canvas.create_text(0, 0, text="", fill="#ffffff", font=("Impact", 42, "bold"), anchor="center") self.badge_bg_id = self.canvas.create_rectangle(0, 0, 0, 0, fill="", outline="", state="hidden") self.badge_text_id = self.canvas.create_text(0, 0, text="", fill="white", font=("Segoe UI", 13, "bold"), state="hidden") # Produkt fotka (zcela bez rámečku, volně na pozadí) self.prod_image_id = self.canvas.create_image(0, 0, anchor="center") # Cenovka vpravo self.price_bg_id = self.canvas.create_rectangle(0, 0, 0, 0, fill="#120521", outline="#9d4edd", width=2) self.price_shadow_id = self.canvas.create_text(0, 0, text="", fill="#000000", font=("Segoe UI", 38, "bold"), anchor="center") self.price_text_id = self.canvas.create_text(0, 0, text="", fill="#ffb703", font=("Segoe UI", 38, "bold"), anchor="center") # Hodiny & Datum self.clock_time_id = self.canvas.create_text(0, 0, text="", fill="#ffffff", font=("Segoe UI", 26, "bold"), anchor="e") self.clock_date_id = self.canvas.create_text(0, 0, text="", fill="#9d4edd", font=("Segoe UI", 13, "bold"), anchor="e") # Indikátor pauzy self.pause_indicator_id = self.canvas.create_text(0, 0, text="", fill="#ff0055", font=("Segoe UI", 13, "bold"), anchor="w") # Spotify Karta (ovládaná vaším rozšířením, vlevo dole) self.card_x1, self.card_y1 = 0, 0 self.card_w, self.card_h = 360, 150 self.card_bg_photo = None self.card_bg_id = self.canvas.create_image(0, 0, anchor="nw") self.cover_img_id = self.canvas.create_image(0, 0, anchor="center") self.spotify_title_id = self.canvas.create_text(0, 0, text="Načítám...", fill="white", font=("Segoe UI", 13, "bold"), anchor="w") self.spotify_subtitle_id = self.canvas.create_text(0, 0, text="Čekám na Spotify", fill="#c77dff", font=("Segoe UI", 10), anchor="w") self.time_curr_id = self.canvas.create_text(0, 0, text="0:00", fill="#aaaaaa", font=("Segoe UI", 9), anchor="w") self.time_total_id = self.canvas.create_text(0, 0, text="0:00", fill="#aaaaaa", font=("Segoe UI", 9), anchor="e") self.pbar_bg = self.canvas.create_line(0, 0, 0, 0, fill="#3c096c", width=5, capstyle=tk.ROUND) self.pbar_fg = self.canvas.create_line(0, 0, 0, 0, fill="#f72585", width=5, capstyle=tk.ROUND) # Stavové proměnné self.running = True self.is_paused = False self.is_ad_active = False self.ad_sound_process = None self.current_img_url = None self.photo_cover = None self.raw_cover_img = None self.current_card_rgba = (25, 10, 40, 230) self.current_prod_photo = None self.catalog_items = [] self.active_item = None self.current_catalog_index = 0 self.current_frame_index = 0 # Spuštění procesů self.load_catalog() self.cycle_product() self.animate_product_3d() self.animate_particles() self.update_clock() threading.Thread(target=self.poll_data, daemon=True).start() def trigger_ad_sound(self): print("🔇 Ztlumuji vše kromě Pythonu...") set_spotify_mute(True) if not os.path.exists(SOUNDS_DIR): return valid_exts = ('.wav', '.mp3', '.wma', '.ogg') sounds = [os.path.join(SOUNDS_DIR, f) for f in os.listdir(SOUNDS_DIR) if f.lower().endswith(valid_exts)] if not sounds: return chosen_sound = random.choice(sounds) def _play(): try: if HAS_PYGAME: pygame.mixer.music.load(chosen_sound) pygame.mixer.music.play() elif chosen_sound.lower().endswith('.wav') and HAS_WINSOUND: winsound.PlaySound(chosen_sound, winsound.SND_FILENAME | winsound.SND_ASYNC) else: ps_script = f"$m = New-Object -ComObject WMPlayer.OCX; $m.URL = '{chosen_sound}'; $m.controls.play(); Start-Sleep -s 60" self.ad_sound_process = subprocess.Popen( ["powershell", "-Command", ps_script], creationflags=getattr(subprocess, 'CREATE_NO_WINDOW', 0) ) except Exception as e: print(f"Chyba při přehrávání zvuku: {e}") threading.Thread(target=_play, daemon=True).start() def stop_ad_sound(self): print("🔊 REKLAMA SKONČILA: Vracím hlasitost zpět.") try: if HAS_PYGAME and pygame.mixer.music.get_busy(): pygame.mixer.music.stop() if HAS_WINSOUND: winsound.PlaySound(None, winsound.SND_PURGE) if self.ad_sound_process: self.ad_sound_process.kill() self.ad_sound_process = None except Exception as e: print(f"Chyba při zastavování: {e}") set_spotify_mute(False) def on_resize(self, event): if event.width <= 10 or event.height <= 10: return if self.w == event.width and self.h == event.height: return self.w = event.width self.h = event.height self.reposition_all() def reposition_all(self): w, h = self.w, self.h # 1. Název produktu + stín font_title_sz = max(24, int(h * 0.055)) self.canvas.itemconfig(self.title_text_id, font=("Impact", font_title_sz, "bold")) self.canvas.itemconfig(self.title_shadow_id, font=("Impact", font_title_sz, "bold")) self.canvas.coords(self.title_text_id, w * 0.5, h * 0.09) self.canvas.coords(self.title_shadow_id, w * 0.5 + 2, h * 0.09 + 2) # 2. Produkt self.canvas.coords(self.prod_image_id, w * 0.5, h * 0.48) self.update_product_frame() # 3. Cenovka vpravo font_price_sz = max(22, int(h * 0.050)) price_w, price_h = int(w * 0.20), int(h * 0.10) px1, py1 = int(w * 0.74), int(h * 0.46) px2, py2 = px1 + price_w, py1 + price_h self.canvas.coords(self.price_bg_id, px1, py1, px2, py2) self.canvas.coords(self.price_shadow_id, px1 + price_w/2 + 2, py1 + price_h/2 + 2) self.canvas.coords(self.price_text_id, px1 + price_w/2, py1 + price_h/2) self.canvas.itemconfig(self.price_shadow_id, font=("Segoe UI", font_price_sz, "bold")) self.canvas.itemconfig(self.price_text_id, font=("Segoe UI", font_price_sz, "bold")) # 4. Hodiny & Datum font_time_sz = max(18, int(h * 0.032)) font_date_sz = max(11, int(h * 0.016)) self.canvas.itemconfig(self.clock_time_id, font=("Segoe UI", font_time_sz, "bold")) self.canvas.itemconfig(self.clock_date_id, font=("Segoe UI", font_date_sz, "bold")) self.canvas.coords(self.clock_time_id, w * 0.97, h * 0.04) self.canvas.coords(self.clock_date_id, w * 0.97, h * 0.08) # 5. Indikátor pauzy self.canvas.coords(self.pause_indicator_id, w * 0.03, h * 0.04) # 6. Spotify Karta vlevo dole self.card_w = max(320, int(w * 0.23)) self.card_h = max(135, int(h * 0.16)) self.card_x1 = int(w * 0.03) self.card_y1 = int(h - self.card_h - (h * 0.03)) self.canvas.coords(self.card_bg_id, self.card_x1, self.card_y1) self.update_card_background(self.current_card_rgba) cover_size = int(self.card_h * 0.58) cover_cx = self.card_x1 + int(self.card_h * 0.42) cover_cy = self.card_y1 + int(self.card_h * 0.50) self.canvas.coords(self.cover_img_id, cover_cx, cover_cy) if self.raw_cover_img: resized_cover = self.raw_cover_img.resize((cover_size, cover_size), Image.Resampling.LANCZOS) self.photo_cover = ImageTk.PhotoImage(resized_cover) self.canvas.itemconfig(self.cover_img_id, image=self.photo_cover) text_x = self.card_x1 + int(self.card_h * 0.82) font_sp_title = max(11, int(self.card_h * 0.11)) font_sp_sub = max(9, int(self.card_h * 0.085)) self.canvas.itemconfig(self.spotify_title_id, font=("Segoe UI", font_sp_title, "bold")) self.canvas.itemconfig(self.spotify_subtitle_id, font=("Segoe UI", font_sp_sub, "bold")) self.canvas.coords(self.spotify_title_id, text_x, self.card_y1 + int(self.card_h * 0.22)) self.canvas.coords(self.spotify_subtitle_id, text_x, self.card_y1 + int(self.card_h * 0.40)) bar_x1 = text_x bar_x2 = self.card_x1 + self.card_w - int(self.card_h * 0.12) bar_y = self.card_y1 + int(self.card_h * 0.68) self.canvas.coords(self.pbar_bg, bar_x1, bar_y, bar_x2, bar_y) self.canvas.coords(self.time_curr_id, bar_x1, bar_y + int(self.card_h * 0.18)) self.canvas.coords(self.time_total_id, bar_x2, bar_y + int(self.card_h * 0.18)) if self.active_item: self.show_product_by_index(self.current_catalog_index - 1) def update_clock(self): now = datetime.datetime.now() time_str = now.strftime("%H:%M:%S") days_cz = ["Pondělí", "Úterý", "Středa", "Čtvrtek", "Pátek", "Sobota", "Neděle"] months_cz = ["ledna", "února", "března", "dubna", "května", "června", "července", "srpna", "září", "října", "listopadu", "prosince"] day_name = days_cz[now.weekday()] month_name = months_cz[now.month - 1] date_str = f"{day_name}, {now.day}. {month_name} {now.year}" self.canvas.itemconfig(self.clock_time_id, text=time_str) self.canvas.itemconfig(self.clock_date_id, text=date_str) self.root.after(1000, self.update_clock) def init_particles_canvas(self): for p in self.particles: if p.p_type in ['bokeh_large', 'bokeh_medium']: outer_id = self.canvas.create_oval(p.x - p.radius, p.y - p.radius, p.x + p.radius, p.y + p.radius, fill=p.color_outer, outline="") r_in = p.radius * 0.45 inner_id = self.canvas.create_oval(p.x - r_in, p.y - r_in, p.x + r_in, p.y + r_in, fill=p.color_inner, outline="") self.particle_draw_ids.append((outer_id, inner_id)) else: draw_id = self.canvas.create_oval(p.x - p.radius, p.y - p.radius, p.x + p.radius, p.y + p.radius, fill=p.color_inner, outline="") self.particle_draw_ids.append((draw_id, None)) def animate_particles(self): for p, draw_ids in zip(self.particles, self.particle_draw_ids): p.update(self.w, self.h) outer_id, inner_id = draw_ids if inner_id is not None: self.canvas.coords(outer_id, p.x - p.radius, p.y - p.radius, p.x + p.radius, p.y + p.radius) r_in = p.radius * 0.45 self.canvas.coords(inner_id, p.x - r_in, p.y - r_in, p.x + r_in, p.y + r_in) else: self.canvas.coords(outer_id, p.x - p.radius, p.y - p.radius, p.x + p.radius, p.y + p.radius) self.root.after(35, self.animate_particles) def toggle_pause(self): self.is_paused = not self.is_paused text = "⏸ PAUZA" if self.is_paused else "" self.canvas.itemconfig(self.pause_indicator_id, text=text) def next_product(self): if self.catalog_items: self.show_product_by_index(self.current_catalog_index) self.current_catalog_index = (self.current_catalog_index + 1) % len(self.catalog_items) def prev_product(self): if self.catalog_items: prev_idx = (self.current_catalog_index - 2) % len(self.catalog_items) self.show_product_by_index(prev_idx) self.current_catalog_index = (prev_idx + 1) % len(self.catalog_items) def reload_catalog_action(self): self.load_catalog() if self.catalog_items: self.show_product_by_index(0) self.current_catalog_index = 1 % len(self.catalog_items) def update_card_background(self, rgba_color): self.current_card_rgba = rgba_color img = Image.new("RGBA", (self.card_w, self.card_h), (0, 0, 0, 0)) draw = ImageDraw.Draw(img) r, g, b, a = rgba_color border_color = (min(r + 80, 255), min(g + 80, 255), min(b + 80, 255), 200) draw.rounded_rectangle([0, 0, self.card_w - 1, self.card_h - 1], radius=16, fill=rgba_color, outline=border_color, width=2) self.card_bg_photo = ImageTk.PhotoImage(img) self.canvas.itemconfig(self.card_bg_id, image=self.card_bg_photo) def load_catalog(self): global KATALOG_DIR if not os.path.exists(KATALOG_DIR): KATALOG_DIR = os.path.join(SCRIPT_DIR, "katalog") if not os.path.exists(KATALOG_DIR): KATALOG_DIR = os.path.join(SCRIPT_DIR, "documents", "katalog") self.catalog_items = [] if not os.path.exists(KATALOG_DIR): return valid_extensions = ('.png', '.jpg', '.jpeg', '.webp') for folder_name in sorted(os.listdir(KATALOG_DIR)): folder_path = os.path.join(KATALOG_DIR, folder_name) if os.path.isdir(folder_path): info_path = os.path.join(folder_path, "info.txt") title = folder_name price = "" badge = "" if os.path.exists(info_path): try: with open(info_path, "r", encoding="utf-8") as f: lines = [line.strip() for line in f.readlines() if line.strip()] if len(lines) >= 1: title = lines[0] if len(lines) >= 2: price = lines[1] if len(lines) >= 3: badge = lines[2] except Exception as e: print(f"Chyba při čtení {info_path}: {e}") images = [f for f in os.listdir(folder_path) if f.lower().endswith(valid_extensions)] images.sort(key=natural_sort_key) image_paths = [os.path.join(folder_path, img) for img in images] self.catalog_items.append({ "title": title, "price": price, "badge": badge, "image_paths": image_paths }) def cycle_product(self): if not self.is_paused: if not self.catalog_items: self.load_catalog() if self.catalog_items: self.show_product_by_index(self.current_catalog_index) self.current_catalog_index = (self.current_catalog_index + 1) % len(self.catalog_items) self.root.after(CYCLE_INTERVAL_MS, self.cycle_product) def show_product_by_index(self, index): if not self.catalog_items: return idx = index % len(self.catalog_items) self.active_item = self.catalog_items[idx] self.canvas.itemconfig(self.title_text_id, text=self.active_item["title"]) self.canvas.itemconfig(self.title_shadow_id, text=self.active_item["title"]) price_val = self.active_item["price"] self.canvas.itemconfig(self.price_text_id, text=price_val) self.canvas.itemconfig(self.price_shadow_id, text=price_val) if price_val: self.canvas.itemconfig(self.price_bg_id, state="normal") self.canvas.itemconfig(self.price_shadow_id, state="normal") self.canvas.itemconfig(self.price_text_id, state="normal") else: self.canvas.itemconfig(self.price_bg_id, state="hidden") self.canvas.itemconfig(self.price_shadow_id, state="hidden") self.canvas.itemconfig(self.price_text_id, state="hidden") badge_val = self.active_item["badge"] if badge_val: badge_upper = badge_val.upper() if any(w in badge_upper for w in ["VYPRODÁNO", "NENÍ", "OUT"]): bg_color, text_color = "#e63946", "#ffffff" elif any(w in badge_upper for w in ["AKCE", "SLEVA", "SALE", "%"]): bg_color, text_color = "#f77f00", "#ffffff" elif any(w in badge_upper for w in ["NOVINKA", "NEW", "TOP"]): bg_color, text_color = "#2a9d8f", "#ffffff" else: bg_color, text_color = "#7209b7", "#ffffff" self.canvas.itemconfig(self.badge_text_id, text=f" {badge_val.upper()} ", fill=text_color, state="normal") badge_y = self.h * 0.14 self.canvas.coords(self.badge_text_id, self.w * 0.5, badge_y) bbox = self.canvas.bbox(self.badge_text_id) if bbox: x1, y1, x2, y2 = bbox self.canvas.coords(self.badge_bg_id, x1 - 12, y1 - 5, x2 + 12, y2 + 5) self.canvas.itemconfig(self.badge_bg_id, fill=bg_color, outline="#ffffff", width=1, state="normal") else: self.canvas.itemconfig(self.badge_text_id, state="hidden") self.canvas.itemconfig(self.badge_bg_id, state="hidden") self.current_frame_index = 0 self.update_product_frame() def animate_product_3d(self): if self.active_item: paths = self.active_item["image_paths"] if len(paths) > 1: self.current_frame_index = (self.current_frame_index + 1) % len(paths) self.update_product_frame() self.root.after(FRAME_INTERVAL_MS, self.animate_product_3d) def update_product_frame(self): if not self.active_item: return paths = self.active_item["image_paths"] if paths: img_path = paths[self.current_frame_index % len(paths)] try: raw_img = Image.open(img_path).convert("RGBA") max_w = int(self.w * 0.42) max_h = int(self.h * 0.52) raw_img.thumbnail((max_w, max_h), Image.Resampling.LANCZOS) self.current_prod_photo = ImageTk.PhotoImage(raw_img) self.canvas.itemconfig(self.prod_image_id, image=self.current_prod_photo) except Exception as e: print(f"Chyba při načítání snímku {img_path}: {e}") self.canvas.itemconfig(self.prod_image_id, image="") else: self.canvas.itemconfig(self.prod_image_id, image="") def poll_data(self): pythoncom.CoInitialize() while self.running: try: res = requests.get(SERVER_URL, timeout=1) if res.status_code == 200: data = res.json() artist = str(data.get("artist", "")).strip() title = str(data.get("title", "")).strip() progress = str(data.get("progressTime", "0:00/0:00")) img_url = data.get("coverImg", None) is_ad_flag = bool(data.get("isAd", False) or data.get("is_ad", False)) # Sloučíme VŠECHNY hodnoty z JSONu do jednoho řetězce (pro jistotu, kdyby rozšíření poslalo text v jiném poli) all_text_values = " ".join([str(v).lower() for v in data.values()]) # Kompletní a rozšířený seznam frází pro spolehlivou detekci reklam ad_phrases = [ "advertisement", "reklama", "přetvoř svůj poslech", "pretvor svoj poslech", "tvoje uši si zaslouží", "spotify premium", "sponzorované", "sponzorovano", "spotify partner", "partner", "commercial", "spot", "poslech", "hudba", "atmosféra", "atmosfera", "1 z 2", "2 z 2", "1 z 1", "2 z 1", "zaslouží", "zaslouzi", "premium", "propagace", "z 2", "z 1" ] # Detekce shody buď podle frází kdekoli v datech, nebo podle speciálního vzoru Spotify + čítač (např. 2 z 2) matched_phrase = next((p for p in ad_phrases if p in all_text_values), None) is_spotify_ad_pattern = "spotify" in all_text_values and ("z 2" in all_text_values or "z 1" in all_text_values or "reklama" in all_text_values) is_ad_detected = is_ad_flag or (matched_phrase is not None) or is_spotify_ad_pattern # Debug výpis do konzole, abyste viděl, co přesně přišlo if is_ad_detected: print(f"[REKLAMA DETEKOVÁNA] Zastavuji zvuk. (Shoda s frází: '{matched_phrase}' / vzor Spotify | Všechna data: {all_text_values})") if not self.is_ad_active: self.is_ad_active = True self.trigger_ad_sound() else: if self.is_ad_active: print(f"[KONEC REKLAMY] Vracím hlasitost. | Všechna data: {all_text_values}") self.is_ad_active = False self.stop_ad_sound() self.root.after(0, self.update_gui, artist, title, progress, img_url, self.is_ad_active) except Exception: pass time.sleep(0.4) def parse_time_to_seconds(self, time_str): try: parts = time_str.split(':') return int(parts[0]) * 60 + int(parts[1]) except Exception: return 0 def update_gui(self, artist, title, progress, img_url, is_ad=False): if is_ad: title_disp = "📢 REKLAMA" artist_disp = "Spotify Partner" else: title_disp = title if len(title) <= 22 else title[:20] + "..." artist_disp = artist if len(artist) <= 26 else artist[:24] + "..." self.canvas.itemconfig(self.spotify_title_id, text=title_disp) self.canvas.itemconfig(self.spotify_subtitle_id, text=artist_disp) if "/" in progress: curr, dur = progress.split("/") self.canvas.itemconfig(self.time_curr_id, text=curr) self.canvas.itemconfig(self.time_total_id, text=dur) c_sec = self.parse_time_to_seconds(curr) d_sec = self.parse_time_to_seconds(dur) percent = (c_sec / d_sec) if d_sec > 0 else 0 text_x = self.card_x1 + int(self.card_h * 0.82) bar_x1 = text_x bar_x2 = self.card_x1 + self.card_w - int(self.card_h * 0.12) bar_y = self.card_y1 + int(self.card_h * 0.68) max_w = bar_x2 - bar_x1 fill_x = bar_x1 + int(max_w * percent) self.canvas.coords(self.pbar_fg, bar_x1, bar_y, fill_x, bar_y) if img_url and img_url != self.current_img_url: self.current_img_url = img_url threading.Thread(target=self.download_cover, args=(img_url,), daemon=True).start() def download_cover(self, url): try: res = requests.get(url, timeout=3) if res.status_code == 200: self.raw_cover_img = Image.open(BytesIO(res.content)).convert("RGBA") small_img = self.raw_cover_img.resize((1, 1), Image.Resampling.BILINEAR) dom_color = small_img.getpixel((0, 0)) dark_r = int(dom_color[0] * 0.35) dark_g = int(dom_color[1] * 0.35) dark_b = int(dom_color[2] * 0.35) card_rgba = (dark_r, dark_g, dark_b, 225) cover_size = int(self.card_h * 0.58) cover_img = self.raw_cover_img.resize((cover_size, cover_size), Image.Resampling.LANCZOS) self.photo_cover = ImageTk.PhotoImage(cover_img) self.root.after(0, lambda: [ self.canvas.itemconfig(self.cover_img_id, image=self.photo_cover), self.update_card_background(card_rgba) ]) except Exception as e: print(f"Chyba při načítání obalu alba: {e}") if __name__ == "__main__": root = tk.Tk() app = FullscreenJarmarkDisplay(root) root.mainloop()