Nahrát soubory do „/“

This commit is contained in:
2026-09-23 16:28:04 +00:00
parent 0d386b08b6
commit 8d23d0170f
4 changed files with 280 additions and 0 deletions

BIN
background.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB

37
server.js Normal file
View File

@@ -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}...`);
});

243
spotify_widget.py Normal file
View File

@@ -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("<Escape>", 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()

BIN
sweb(node+katalog).zip Normal file

Binary file not shown.