From 90df6ff7653b8ab47774af1cd9eafd53b98a802d 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: Thu, 13 Nov 2025 17:25:18 +0300 Subject: [PATCH] =?UTF-8?q?1.=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=20=D0=B2=20gitigove=20.env=202.=20/vmix=20-=20=D0=B3=D0=B5?= =?UTF-8?q?=D0=BD=D0=B5=D1=80=D0=B8=D1=80=D1=83=D0=B5=D1=82=D1=81=D1=8F=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=20vMix=20=D1=81=20=D0=BD?= =?UTF-8?q?=D1=83=D0=B6=D0=BD=D1=8B=D0=BC=D0=B8=20=D0=BB=D0=B8=D0=BD=D0=BA?= =?UTF-8?q?=D0=B0=D0=BC=D0=B8=20=D0=B4=D0=BB=D1=8F=20=D0=B2=D0=B8=D1=80?= =?UTF-8?q?=D1=82=D1=83=D0=B0=D0=BB=D1=8C=D0=BD=D1=8B=D1=85=20=D0=BC=D0=B0?= =?UTF-8?q?=D1=88=D0=B8=D0=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +- get_data.py | 120 +++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 101 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index a21c49b..5820b83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /__pycache__ /TestJson /logs/* -*.venv \ No newline at end of file +*.venv +*.env \ No newline at end of file diff --git a/get_data.py b/get_data.py index f899551..734abb9 100644 --- a/get_data.py +++ b/get_data.py @@ -1,22 +1,20 @@ -from fastapi import FastAPI -from fastapi.responses import Response, HTMLResponse -from fastapi import HTTPException -from fastapi import Request +from fastapi import FastAPI, HTTPException, Request +from fastapi.responses import Response, HTMLResponse, FileResponse, StreamingResponse +from typing import Dict, Any from contextlib import asynccontextmanager -import requests -import threading -import time -import queue +import requests, uvicorn, json +import threading, queue import argparse -import uvicorn -import os import pandas as pd -import json from datetime import datetime, time as dtime, timedelta from fastapi.responses import Response import logging import logging.config -import platform +from dotenv import load_dotenv +from pprint import pprint +import nasio +import io, os, platform, time +import xml.etree.ElementTree as ET parser = argparse.ArgumentParser() parser = argparse.ArgumentParser() @@ -30,10 +28,8 @@ MYHOST = platform.node() if not os.path.exists("logs"): os.makedirs("logs") -telegram_bot_token = "7639240596:AAH0YtdQoWZSC-_R_EW4wKAHHNLIA0F_ARY" -# telegram_chat_id = 228977654 -# telegram_chat_id = -4803699526 -telegram_chat_id = -1003388354193 +telegram_bot_token = os.getenv("TELEGRAM_TOKEN") +telegram_chat_id = os.getenv("TELEGRAM_CHAT_ID") log_config = { "version": 1, "handlers": { @@ -79,11 +75,19 @@ logging.config.dictConfig(log_config) logger = logging.getLogger(__name__) logger.handlers[2].formatter.use_emoji = True +pprint(f"Локальный файл окружения ={load_dotenv(verbose=True)}") LEAGUE = args.league TEAM = args.team LANG = args.lang -HOST = "https://pro.russiabasket.org" +HOST = os.getenv("API_BASE_URL") +SYNO_PATH = f'{os.getenv("SYNO_PATH")}MATCH INFO.xlsx' +SYNO_URL = os.getenv("SYNO_URL") +SYNO_USERNAME = os.getenv("SYNO_USERNAME") +SYNO_PASSWORD = os.getenv("SYNO_PASSWORD") +SYNO_PATH_МVMIX = os.getenv("SYNO_PATH_МVMIX") + + STATUS = False GAME_ID = None SEASON = None @@ -2773,7 +2777,6 @@ async def play_by_play(): team1_name = data["team1"]["name"] team2_name = data["team2"]["name"] - team1_startnum = [ i["startNum"] for i in next( @@ -2799,8 +2802,11 @@ async def play_by_play(): last_event = data_pbp[-1] if "play" not in df_data_pbp: return maybe_clear_for_vmix([]) - - if "live-status" in latest_data and latest_data["live-status"]["data"] != "Not Found": + + if ( + "live-status" in latest_data + and latest_data["live-status"]["data"] != "Not Found" + ): json_quarter = latest_data["live-status"]["data"]["result"]["period"] json_second = latest_data["live-status"]["data"]["result"]["second"] else: @@ -2899,9 +2905,81 @@ async def play_by_play(): return maybe_clear_for_vmix(payload) +def change_vmix_datasource_urls(xml_data, new_base_url: str) -> bytes: + """ + Ищет все и меняет внутри на new_base_url + endpoint. + """ + + # 1. Приводим вход к bytes + if isinstance(xml_data, (bytes, bytearray)): + raw_bytes = bytes(xml_data) + elif isinstance(xml_data, str): + raw_bytes = xml_data.encode("utf-8") + elif isinstance(xml_data, io.IOBase) or hasattr(xml_data, "read"): + # nasio.load_bio, скорее всего, возвращает BytesIO + raw_bytes = xml_data.read() + try: + xml_data.seek(0) + except Exception: + pass + else: + raise TypeError(f"Unsupported xml_data type: {type(xml_data)}") + + # 2. Декодируем + text = raw_bytes.decode("utf-8", errors="replace") + + # 3. Парсим XML + root = ET.fromstring(text) + + # 4. Меняем URL + for ds in root.findall(".//datasource[@friendlyName='JSON']"): + for inst in ds.findall(".//instance"): + url_tag = inst.find(".//state/xml/url") + if url_tag is not None and url_tag.text: + old_url = url_tag.text.strip() + + # аккуратно выделяем endpoint + # http://127.0.0.1:8000/team1 -> /team1 + after_scheme = old_url.split("://", 1)[-1] + after_host = ( + after_scheme.split("/", 1)[-1] if "/" in after_scheme else "" + ) + endpoint = "/" + after_host if after_host else "" + + new_url = new_base_url.rstrip("/") + endpoint + url_tag.text = new_url + + # 5. Сериализуем обратно в bytes + new_xml = ET.tostring(root, encoding="utf-8", method="xml") + return new_xml + + @app.get("/vmix") async def vmix_project(): - pass + vmix_bio = nasio.load_bio( + user=SYNO_USERNAME, + password=SYNO_PASSWORD, + nas_ip=SYNO_URL, + nas_port="443", + path=SYNO_PATH_МVMIX, + ) + system_name = platform.system() + if system_name == "Windows": + pass + else: + # ❗ На Linux/Synology/Docker — заменяем URL + edited_vmix = change_vmix_datasource_urls( + vmix_bio, f"https://{MYHOST}.tvstart.ru" + ) + # 2. гарантируем, что это bytes + if isinstance(edited_vmix, str): + edited_vmix = edited_vmix.encode("utf-8") + + return StreamingResponse( + io.BytesIO(edited_vmix), + media_type="application/octet-stream", + headers={"Content-Disposition": f'attachment; filename="VTB_{MYHOST}.vmix"'}, + ) if __name__ == "__main__":