From 43c3c6e02de84f56f6e8616abe29ee102fdf8456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=AE=D1=80=D0=B8=D0=B9=20=D0=A7=D0=B5=D1=80=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Tue, 18 Nov 2025 12:41:01 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BF=D0=BE=D0=BF=D1=80=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=BA=D0=B0=D1=80=D1=82=D1=83=20=D0=B1=D1=80=D0=BE?= =?UTF-8?q?=D1=81=D0=BA=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- get_data.py | 144 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 93 insertions(+), 51 deletions(-) diff --git a/get_data.py b/get_data.py index 037c22a..6d8e99a 100644 --- a/get_data.py +++ b/get_data.py @@ -93,14 +93,41 @@ SYNO_PASSWORD = os.getenv("SYNO_PASSWORD") SYNO_PATH_VMIX = os.getenv("SYNO_PATH_VMIX") SYNO_FONT_PATH = os.getenv("SYNO_FONT_PATH") +_syno_font_path = nasio.load_bio( + user=SYNO_USERNAME, + password=SYNO_PASSWORD, + nas_ip=SYNO_URL, + nas_port="443", + path=os.getenv("SYNO_FONT_PATH"), +) +if isinstance(_syno_font_path, BytesIO): + _syno_font_path = _syno_font_path.getvalue() +SYNO_FONT_PATH = _syno_font_path # bytes или None +# ---- ИКОНКА ПРОМАХА ---- +_syno_miss_raw = nasio.load_bio( + user=SYNO_USERNAME, + password=SYNO_PASSWORD, + nas_ip=SYNO_URL, + nas_port="443", + path=os.getenv("SYNO_MISS"), +) +if isinstance(_syno_miss_raw, BytesIO): + _syno_miss_raw = _syno_miss_raw.getvalue() +SYNO_MISS = _syno_miss_raw # bytes или None + +# ---- ИКОНКА ПОПАДАНИЯ ---- +_syno_goal_raw = nasio.load_bio( + user=SYNO_USERNAME, + password=SYNO_PASSWORD, + nas_ip=SYNO_URL, + nas_port="443", + path=os.getenv("SYNO_GOAL"), +) +if isinstance(_syno_goal_raw, BytesIO): + _syno_goal_raw = _syno_goal_raw.getvalue() +SYNO_GOAL = _syno_goal_raw # bytes или None SHOTMAP_SUBDIR = "shotmaps" -# SHOTMAP_DIR = ( -# os.path.join(SYNO_PATH_VMIX, SHOTMAP_SUBDIR) -# if SYNO_PATH_VMIX -# else os.path.join(os.getcwd(), SHOTMAP_SUBDIR) -# ) -# os.makedirs(SHOTMAP_DIR, exist_ok=True) SHOTMAP_DIR = os.path.join(os.getcwd(), "shotmaps") os.makedirs(SHOTMAP_DIR, exist_ok=True) @@ -3312,10 +3339,9 @@ async def games_online(): return todays_games - def get_image(points, bib, count_point): """ - points: список кортежей (x, y, is_made) + points: список кортежей (x, y, is_made, sec, period) x, y — координаты из API, где (0,0) = центр кольца is_made — True (play 2,3) или False (play 5,6) bib: startNum/номер игрока @@ -3325,54 +3351,40 @@ def get_image(points, bib, count_point): if not points: return "" - # # --- подложка --- - # try: - # base_image = Image.open(COURT_IMAGE_PATH).convert("RGBA") - # except Exception as e: - # logger.warning( - # f"[shotmap] не удалось открыть COURT_IMAGE_PATH={COURT_IMAGE_PATH}: {e}" - # ) base_image = Image.new("RGBA", (1500, 2800), (0, 0, 0, 0)) - - width, height = base_image.size # ожидаем 1500 x 2800 + width, height = base_image.size draw = ImageDraw.Draw(base_image) - # === ДИАПАЗОН КООРДИНАТ ИЗ API === + # === Диапазон координат === COURT_WIDTH_UNITS = 150.0 # по X COURT_LENGTH_UNITS = 280.0 # по Y - # масштаб: пиксели на 1 API-юнит scale_x = height / COURT_LENGTH_UNITS scale_y = width / COURT_WIDTH_UNITS - # === ЦЕНТР КОЛЬЦА В PILLOW-КООРДИНАТАХ (ОТ ВЕРХНЕГО ЛЕВОГО УГЛА) === HOOP_X_PX = 750 HOOP_Y_PX = 157.5 def to_px(x, y): - """ - (0,0) из API → (HOOP_X_PX, HOOP_Y_PX) на картинке. - x > 0 — вправо, y > 0 — вниз (вглубь площадки). - Если нужно, чтобы y шёл вверх — поменяй + на -. - """ px = int(HOOP_X_PX - x * scale_x) py = int(HOOP_Y_PX + y * scale_y) return px, py - point_radius = 10 + point_radius = 30 - # try: - nasio_font = nasio.load_bio( - user=SYNO_USERNAME, - password=SYNO_PASSWORD, - nas_ip=SYNO_URL, - nas_port="443", - path=SYNO_FONT_PATH, - ) - if nasio_font: - font = ImageFont.truetype(nasio_font, 18) - # except Exception: - # font = ImageFont.load_default() + # --- шрифт --- + font = ImageFont.load_default() + try: + nasio_font = SYNO_FONT_PATH + if nasio_font: + if isinstance(nasio_font, BytesIO): + nasio_font = nasio_font.getvalue() + if isinstance(nasio_font, (bytes, bytearray)): + font = ImageFont.truetype(BytesIO(nasio_font), 18) + else: + font = ImageFont.truetype(nasio_font, 18) + except Exception as e: + logger.warning(f"[shotmap] не удалось загрузить шрифт: {e}") for x_raw, y_raw, is_made, sec, period in points: try: @@ -3383,21 +3395,51 @@ def get_image(points, bib, count_point): px, py = to_px(x, y) - # кружок - bbox = ( - px - point_radius, - py - point_radius, - px + point_radius, - py + point_radius, - ) - color = (0, 255, 0, 255) if is_made else (255, 0, 0, 255) - draw.ellipse(bbox, fill=color) + # --- выбираем bytes иконки --- + icon_bytes = SYNO_GOAL if is_made else SYNO_MISS + used_icon = False - # подпись координат (для отладки) - label = f"{x:.2f}; {y:.2f}" - text_x = px + point_radius + 4 - text_y = py - point_radius - 4 + if isinstance(icon_bytes, (bytes, bytearray)) and icon_bytes: + try: + buf = BytesIO(icon_bytes) + icon = Image.open(buf).convert("RGBA") + + size = point_radius * 2 + icon = icon.resize((size, size), Image.LANCZOS) + + base_image.paste(icon, (px - point_radius, py - point_radius), icon) + used_icon = True + except Exception as e: + logger.warning( + f"[shotmap] не удалось открыть иконку из bytes " + f"(is_made={is_made}, len={len(icon_bytes)}): {e}" + ) + else: + logger.warning( + f"[shotmap] icon_bytes пустой или не bytes " + f"(is_made={is_made}, type={type(icon_bytes)})" + ) + + # --- Fallback: кружок, если картинка не нарисовалась --- + if not used_icon: + bbox = ( + px - point_radius, + py - point_radius, + px + point_radius, + py + point_radius, + ) + color = (0, 255, 0, 255) if is_made else (255, 0, 0, 255) + draw.ellipse(bbox, fill=color) + + # --- подпись Q{period} по центру --- label = f"Q{period}" + bbox = draw.textbbox((0, 0), label, font=font) + text_w = bbox[2] - bbox[0] + text_h = bbox[3] - bbox[1] + + # центрируем относительно точки (px, py) + text_x = px - text_w // 2 + text_y = py - text_h // 2 # тень draw.text((text_x + 1, text_y + 1), label, font=font, fill=(0, 0, 0, 255))