diff --git a/background.png b/background.png new file mode 100644 index 0000000..12b2c74 Binary files /dev/null and b/background.png differ diff --git a/server.js b/server.js new file mode 100644 index 0000000..56c39d0 --- /dev/null +++ b/server.js @@ -0,0 +1,37 @@ +const express = require('express'); +const cors = require('cors'); +const app = express(); +const PORT = 8080; + +app.use(cors()); +app.use(express.json()); + +// Sem si budeme ukládat poslední přijatou skladbu +let currentSongData = { + artist: "Čekám na Spotify...", + title: "Spusťte hudbu", + coverImg: null, + progressTime: "0:00/0:00", + ad: "false" +}; + +// 1. Endpoint, kam Chrome rozšíření posílá data (POST) +app.post('/endpoint', (req, res) => { + if (req.body && req.body.artist) { + currentSongData = req.body; // Uložíme data do paměti + console.log("--- New Song Received ---"); + console.log(`Interpret: ${currentSongData.artist}`); + console.log(`Skladba: ${currentSongData.title}`); + console.log(`Čas: ${currentSongData.progressTime} | Reklama: ${currentSongData.ad}`); + } + res.sendStatus(200); +}); + +// 2. Endpoint, ze kterého si Python widget čte data (GET) +app.get('/endpoint', (req, res) => { + res.json(currentSongData); +}); + +app.listen(PORT, '0.0.0.0', () => { + console.log(`Server běží a poslouchá na portu ${PORT}...`); +}); \ No newline at end of file diff --git a/spotify_widget.py b/spotify_widget.py new file mode 100644 index 0000000..8beb308 --- /dev/null +++ b/spotify_widget.py @@ -0,0 +1,243 @@ +import tkinter as tk +import requests +import threading +import time +import os +import random +from io import BytesIO +from PIL import Image, ImageTk + +SERVER_URL = "http://localhost:8080/endpoint" +CYCLE_INTERVAL_MS = 10000 # Změna produktu každých 10 sekund + +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") + +class FullscreenJarmarkDisplay: + def __init__(self, root): + self.root = root + self.root.title("RP + 1602 Display Jarmark") + + self.root.attributes("-fullscreen", True) + self.root.bind("", lambda e: self.root.destroy()) + + self.canvas = tk.Canvas(root, width=1920, height=1080, bg="#0d031b", highlightthickness=0) + self.canvas.pack(fill=tk.BOTH, expand=True) + + self.load_background() + + # Název produktu nahoře + self.title_text_id = self.canvas.create_text(960, 120, text="", fill="white", font=("Impact", 75, "bold")) + + # Obrázek produktu uprostřed + self.prod_image_id = self.canvas.create_image(920, 510, anchor="center") + + # --- DESIGN CENOVKY (Vpravo) --- + # 1. Podkladový badge/karta pro cenu + self.price_bg_id = self.canvas.create_rectangle(1380, 450, 1840, 570, fill="#150826", outline="#9d4edd", width=3) + + # 2. Stín ceny (3D efekt) + self.price_shadow_id = self.canvas.create_text(1613, 513, text="", fill="#000000", font=("Trebuchet MS", 62, "bold"), anchor="center") + + # 3. Hlavní text ceny (Zářivě zlatá) + self.price_text_id = self.canvas.create_text(1610, 510, text="", fill="#FFD700", font=("Trebuchet MS", 62, "bold"), anchor="center") + + + # Spodní lišta přehrávače + self.canvas.create_rectangle(0, 910, 1920, 1080, fill="#000000", outline="") + + self.cover_img_id = self.canvas.create_image(80, 995, anchor="center") + + self.spotify_title_id = self.canvas.create_text(160, 955, text="Načítám...", fill="white", font=("Arial", 22, "bold"), anchor="w") + self.spotify_subtitle_id = self.canvas.create_text(160, 990, text="Hudobné video • Čekám na Spotify", fill="#bbbbbb", font=("Arial", 16), anchor="w") + + self.time_curr_id = self.canvas.create_text(160, 1040, text="0:00", fill="#aaaaaa", font=("Arial", 14), anchor="w") + self.time_total_id = self.canvas.create_text(1860, 1040, text="0:00", fill="#aaaaaa", font=("Arial", 14), anchor="e") + + self.pbar_bg = self.canvas.create_line(220, 1040, 1800, 1040, fill="#444444", width=5) + self.pbar_fg = self.canvas.create_line(220, 1040, 220, 1040, fill="#ffffff", width=5) + + self.running = True + self.current_img_url = None + self.photo_cover = None + self.current_prod_photo = None + + self.catalog_items = [] + self.current_catalog_index = 0 + + self.load_catalog() + self.cycle_product() + + threading.Thread(target=self.poll_data, daemon=True).start() + + def load_background(self): + bg_candidates = [ + "backgroung.png", + "background.png", + "background.jpg", + "backgroung.jpg" + ] + + bg_path = None + for filename in bg_candidates: + full_path = os.path.join(SCRIPT_DIR, filename) + if os.path.exists(full_path): + bg_path = full_path + break + + if bg_path: + try: + bg_img = Image.open(bg_path).convert("RGBA").resize((1920, 1080), Image.Resampling.LANCZOS) + self.bg_photo = ImageTk.PhotoImage(bg_img) + self.canvas.create_image(0, 0, image=self.bg_photo, anchor="nw") + except Exception as e: + print(f"Chyba při načítání pozadí {bg_path}: {e}") + else: + colors = ["#2b0845", "#410b66", "#581287", "#731aa8", "#8b24ca"] + random.seed(42) + for _ in range(40): + x = random.randint(0, 1920) + y = random.randint(0, 1080) + r = random.randint(40, 150) + c = random.choice(colors) + self.canvas.create_oval(x-r, y-r, x+r, y+r, fill=c, outline="") + + 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): + print(f"Adresář 'katalog' neexistuje na cestě: {KATALOG_DIR}") + return + + 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") + image_path = os.path.join(folder_path, "image.png") + + title = folder_name + price = "" + + 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] + except Exception as e: + print(f"Chyba při čtení {info_path}: {e}") + + self.catalog_items.append({ + "title": title, + "price": price, + "image_path": image_path if os.path.exists(image_path) else None + }) + + def cycle_product(self): + if self.current_catalog_index == 0: + self.load_catalog() + + if self.catalog_items: + self.current_catalog_index %= len(self.catalog_items) + item = self.catalog_items[self.current_catalog_index] + + self.canvas.itemconfig(self.title_text_id, text=item["title"]) + + # Nastavení ceny i stínu ceny + price_val = item["price"] + self.canvas.itemconfig(self.price_text_id, text=price_val) + self.canvas.itemconfig(self.price_shadow_id, text=price_val) + + # Pokud cenovka nemá text, schováme podkladový obdélník + if price_val: + self.canvas.itemconfig(self.price_bg_id, state="normal") + else: + self.canvas.itemconfig(self.price_bg_id, state="hidden") + + if item["image_path"]: + try: + raw_img = Image.open(item["image_path"]).convert("RGBA") + raw_img.thumbnail((700, 460), 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í obrázku {item['image_path']}: {e}") + self.canvas.itemconfig(self.prod_image_id, image="") + else: + self.canvas.itemconfig(self.prod_image_id, image="") + + self.current_catalog_index = (self.current_catalog_index + 1) % len(self.catalog_items) + + self.root.after(CYCLE_INTERVAL_MS, self.cycle_product) + + def poll_data(self): + while self.running: + try: + res = requests.get(SERVER_URL, timeout=1) + if res.status_code == 200: + data = res.json() + artist = data.get("artist", "Neznámý interpret") + title = data.get("title", "Čekám na skladbu") + progress = data.get("progressTime", "0:00/0:00") + img_url = data.get("coverImg", None) + + self.root.after(0, self.update_gui, artist, title, progress, img_url) + except Exception: + pass + time.sleep(0.8) + + 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): + self.canvas.itemconfig(self.spotify_title_id, text=title) + self.canvas.itemconfig(self.spotify_subtitle_id, text=f"Hudobné video • {artist}") + + 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 + + start_x = 220 + max_w = 1800 - start_x + fill_x = start_x + int(max_w * percent) + self.canvas.coords(self.pbar_fg, start_x, 1040, fill_x, 1040) + + 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: + raw_img = Image.open(BytesIO(res.content)).convert("RGBA") + raw_img = raw_img.resize((100, 100), Image.Resampling.LANCZOS) + + self.photo_cover = ImageTk.PhotoImage(raw_img) + self.root.after(0, lambda: self.canvas.itemconfig(self.cover_img_id, image=self.photo_cover)) + except Exception: + pass + +if __name__ == "__main__": + root = tk.Tk() + app = FullscreenJarmarkDisplay(root) + root.mainloop() \ No newline at end of file diff --git a/sweb(node+katalog).zip b/sweb(node+katalog).zip new file mode 100644 index 0000000..4a64f3e Binary files /dev/null and b/sweb(node+katalog).zip differ