Nahrát soubory do „/“
This commit is contained in:
BIN
background.png
Normal file
BIN
background.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.4 MiB |
37
server.js
Normal file
37
server.js
Normal 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
243
spotify_widget.py
Normal 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
BIN
sweb(node+katalog).zip
Normal file
Binary file not shown.
Reference in New Issue
Block a user