diff --git a/get_data_new.py b/get_data_new.py index 322b78b..cb3640d 100644 --- a/get_data_new.py +++ b/get_data_new.py @@ -361,38 +361,36 @@ def get_interval_by_name(name: str) -> int: def run_live_loop( - parent_session_unused, league: str, season: str, game_id: int, lang: str, game_meta: dict, + stop_event: threading.Event, ): """ - Поток, который дергает онлайн API матча. - По завершении матча говорит рендеру остановиться. + Запускает два рабочих цикла: + - poll_game_live (опрашивает API матча) + - render_loop (собирает ui_state.json) + Управляет их остановкой. """ logger.info( f"[LIVE_THREAD] start live loop for game_id={game_id} (league={league}, season={season})" ) - # создаём свою сессию, чтобы не делить session между потоками + # отдельная сессия только для лайва session = create_session() - # общий stop_event для live и render - stop_event = threading.Event() - - # запускаем рендер-поток + # поток рендера render_thread = threading.Thread( target=render_loop, - args=(stop_event,), # только stop_event, out_name используем дефолт "ui_state" - daemon=True, + args=(stop_event,), # дефолт out_name="game" в твоём render_loop можно оставить + daemon=False, ) render_thread.start() logger.info("[LIVE_THREAD] render thread spawned") try: - # крутим опрос API до конца матча poll_game_live( session=session, league=league, @@ -400,12 +398,15 @@ def run_live_loop( game_id=game_id, lang=lang, game_meta=game_meta, + stop_event=stop_event, ) except Exception as e: logger.exception(f"[LIVE_THREAD] crash in live loop for game_id={game_id}: {e}") finally: - # матч кончился -> выключаем рендер + # какое бы ни было завершение, просим рендер остановиться stop_event.set() + logger.info(f"[LIVE_THREAD] stopping render thread for game_id={game_id}") + render_thread.join() logger.info(f"[LIVE_THREAD] stop live loop for game_id={game_id}") @@ -418,30 +419,20 @@ def poll_game_live( game_meta: dict, stop_event: threading.Event, ): - """ - Онлайн-цикл: - - "game" раз в 600 сек (pregame-fullstats можно вернуть позже) - - "live-status", "box-score", "play-by-play" раз в 1 сек - - Цикл выходит, когда матч перестаёт быть live ИЛИ когда сработал stop_event. - """ - - slow_endpoints = ["game"] + slow_endpoints = ["game"] # "pregame-fullstats" можно добавить обратно fast_endpoints = ["live-status", "box-score", "play-by-play"] - last_call = {ep: 0 for ep in slow_endpoints + fast_endpoints} + last_call = {name: 0 for name in slow_endpoints + fast_endpoints} - # пул потоков живет весь матч with ThreadPoolExecutor(max_workers=5) as executor: while True: - # внешняя принудительная остановка + # внешний стоп с клавиатуры / по команде if stop_event.is_set(): - logger.info(f"[LIVE_LOOP] stop_event set -> break live poll for game {game_id}") + logger.info(f"[POLL] stop_event set -> break live poll for game {game_id}") break now = time.time() - # какие ручки надо дёрнуть прямо сейчас to_run = [] for ep in fast_endpoints + slow_endpoints: interval = get_interval_by_name(ep) @@ -464,7 +455,6 @@ def poll_game_live( ) game_finished = False - for fut in as_completed(futures): try: ep_name, data = fut.result() @@ -486,7 +476,6 @@ def poll_game_live( except Exception as e: logger.exception(f"poll endpoint error: {e}") - # страховка по календарю (game_meta мог устареть, но лучше чем ничего) if not is_game_live(game_meta): logger.info( f"Game {game_id} no longer live by calendar meta -> stop loop" @@ -497,10 +486,14 @@ def poll_game_live( break time.sleep(0.2) - logger.debug("live poll tick ok") + + # ещё одна точка выхода даже если статус не изменился + if stop_event.is_set(): + logger.info(f"[POLL] stop_event set after sleep -> break live poll for game {game_id}") + break -def get_data_API(session, league: str, team: str, lang: str): +def get_data_API(session, league: str, team: str, lang: str, stop_event: threading.Event): json_seasons = fetch_api_data( session, "seasons", host=HOST, league=league, lang=lang ) @@ -510,7 +503,6 @@ def get_data_API(session, league: str, team: str, lang: str): season = json_seasons[0]["season"] - # standings и calendar просто кешируем в файлы fetch_api_data( session, "standings", host=HOST, league=league, season=season, lang=lang ) @@ -523,71 +515,35 @@ def get_data_API(session, league: str, team: str, lang: str): today_game, last_played = get_game_id(json_calendar, team) - # если есть завершённая последняя игра — просто сохраним статический срез и выходим if last_played and not today_game: game_id = last_played["game"]["id"] logger.info(f"Последний завершённый матч id={game_id}") fetch_api_data(session, "game", host=HOST, game_id=game_id, lang=lang) return - # если есть матч сегодня if today_game: game_id = today_game["game"]["id"] logger.info(f"Онлайн матч id={game_id}") - # базовые данные прямо сейчас fetch_api_data(session, "game", host=HOST, game_id=game_id, lang=lang) - # если матч реально идёт -> запускаем live-петлю и рендер if is_game_live(today_game["game"]): - # отдельная сессия для live-пула (как раньше) - live_session = create_session() - - # единый stop_event для всего матча - stop_event = threading.Event() - - # поток рендера - render_thread = threading.Thread( - target=render_loop, - args=(stop_event, "ui_state"), # имя файла можешь менять + t = threading.Thread( + target=run_live_loop, + args=(league, season, game_id, lang, today_game["game"], stop_event), daemon=False, ) - render_thread.start() - logger.info("[MAIN] render thread spawned") + t.start() + logger.info("live thread spawned, waiting for it to finish...") - # поток live-пулинга API - def live_worker(): - try: - poll_game_live( - session=live_session, - league=league, - season=season, - game_id=game_id, - lang=lang, - game_meta=today_game["game"], - stop_event=stop_event, - ) - except Exception as e: - logger.exception(f"[LIVE_THREAD] crash in live loop: {e}") - - live_thread = threading.Thread( - target=live_worker, - daemon=False, - ) - live_thread.start() - logger.info("[MAIN] live thread spawned") - - # дожидаемся окончания live_thread (то есть завершения матча или ошибки) - live_thread.join() - logger.info("[MAIN] live thread finished") - - # говорим рендеру остановиться - stop_event.set() - - # ждём корректного завершения рендера - render_thread.join() - logger.info("[MAIN] render thread finished") + try: + t.join() + except KeyboardInterrupt: + logger.info("KeyboardInterrupt while waiting live thread -> stop_event") + stop_event.set() + t.join() + logger.info("live thread finished") return logger.info("Для этой команды игр сегодня нет и нет завершённой последней игры.")