Привет, я разрабатываю нейросеть на python, которая бы помогала тренироваться человеку.
Она бы смотрела в реальном времени через камеру то, как он тренеруется, после чего оанализировала корректность упражнений и подсчитывала подходы.

Вот код, который относительно рабтал с видео:
```py
# 1. Установка необходимых библиотек
!pip install mediapipe opencv-python-headless

# 2. Импорт необходимых библиотек
import cv2
import mediapipe as mp
import numpy as np
import math
from collections import deque
from google.colab import files
import tempfile
import os
from IPython.display import HTML
from base64 import b64encode
import zipfile
import io

# 3. Загрузка и распаковка папки DeepFit и видеофайла
print("Пожалуйста, загрузите ZIP-архив папки DeepFit и видеофайл (например, input_video.mp4).")
uploaded = files.upload()

# Предполагается, что были загружены:
# - deepfit.zip (содержит папку DeepFit)
# - input_video.mp4

# Разархивируем DeepFit
for file in uploaded.keys():
    if file.endswith('.zip'):
        print(f"Распаковка {file}...")
        with zipfile.ZipFile(io.BytesIO(uploaded[file]), 'r') as zip_ref:
            zip_ref.extractall('.')
        print("Распаковка завершена.")

# Поиск видеофайла среди загруженных файлов
video_extensions = ['.mp4', '.avi', '.mov', '.mkv']
video_files = [filename for filename in uploaded.keys() if any(filename.lower().endswith(ext) for ext in video_extensions)]

if not video_files:
    raise ValueError("Не найдено загруженных видеофайлов. Пожалуйста, загрузите видеофайл с расширением .mp4, .avi, .mov или .mkv")
else:
    video_path = video_files[0]
    print(f"Используется видеофайл: {video_path}")

# Проверка наличия необходимого содержимого в папке DeepFit
required_files = ['DeepFitClassifier.py', 'deepfit_classifier_v3.tflite']
for req_file in required_files:
    deepfit_path = os.path.join('DeepFit', req_file)
    if not os.path.exists(deepfit_path):
        raise FileNotFoundError(f"Не найден файл: {deepfit_path}. Пожалуйста, убедитесь, что все необходимые файлы находятся в папке DeepFit.")

# Добавление папки DeepFit в системный путь для импорта
import sys
sys.path.append('DeepFit')

# Импорт DeepFitClassifier после загрузки и распаковки
from DeepFitClassifier import DeepFitClassifier

# 4. Определение всех необходимых функций и параметров

# Mapping dictionary to map keypoints from Mediapipe to our Classifier model
lm_dict = {
    0: 0, 1: 10, 2: 12, 3: 14, 4: 16, 5: 11, 6: 13, 7: 15, 8: 24, 9: 26,
    10: 28, 11: 23, 12: 25, 13: 27, 14: 5, 15: 2, 16: 8, 17: 7,
}

def set_pose_parameters():
    mode = False
    complexity = 1
    smooth_landmarks = True
    enable_segmentation = False
    smooth_segmentation = True
    detectionCon = 0.5
    trackCon = 0.5
    mpPose = mp.solutions.pose
    return mode, complexity, smooth_landmarks, enable_segmentation, smooth_segmentation, detectionCon, trackCon, mpPose

def get_pose(img, results, draw=True):
    if results.pose_landmarks:
        if draw:
            mpDraw = mp.solutions.drawing_utils
            mpDraw.draw_landmarks(img, results.pose_landmarks, mpPose.POSE_CONNECTIONS)
    return img

def get_position(img, results, height, width, draw=True):
    landmark_list = []
    if results.pose_landmarks:
        for id, landmark in enumerate(results.pose_landmarks.landmark):
            # finding height, width of the image printed
            landmark_pixel_x, landmark_pixel_y = int(landmark.x * width), int(landmark.y * height)
            landmark_list.append([id, landmark_pixel_x, landmark_pixel_y])
            if draw:
                cv2.circle(img, (landmark_pixel_x, landmark_pixel_y), 5, (255, 0, 0), cv2.FILLED)
    return landmark_list

def get_angle(img, landmark_list, point1, point2, point3, draw=True):
    # Retrieve landmark coordinates from point identifiers
    try:
        x1, y1 = landmark_list[point1][1:]
        x2, y2 = landmark_list[point2][1:]
        x3, y3 = landmark_list[point3][1:]
    except IndexError:
        return 0  # Возвращать 0, если ключевая точка отсутствует

    angle = math.degrees(math.atan2(y3 - y2, x3 - x2) -
                         math.atan2(y1 - y2, x1 - x2))

    # Handling angle edge cases: Obtuse and negative angles
    if angle < 0:
        angle += 360
        if angle > 180:
            angle = 360 - angle
    elif angle > 180:
        angle = 360 - angle

    if draw:
        # Drawing lines between the three points
        cv2.line(img, (x1, y1), (x2, y2), (255,255,255), 3)
        cv2.line(img, (x3, y3), (x2, y2), (255,255,255), 3)

        # Drawing circles at intersection points of lines
        cv2.circle(img, (x1, y1), 5, (75,0,130), cv2.FILLED)
        cv2.circle(img, (x1, y1), 15, (75,0,130), 2)
        cv2.circle(img, (x2, y2), 5, (75,0,130), cv2.FILLED)
        cv2.circle(img, (x2, y2), 15, (75,0,130), 2)
        cv2.circle(img, (x3, y3), 5, (75,0,130), cv2.FILLED)
        cv2.circle(img, (x3, y3), 15, (75,0,130), 2)

        # Show angles between lines
        cv2.putText(img, str(int(angle)), (x2-50, y2+50),
                    cv2.FONT_HERSHEY_PLAIN, 2, (0,0,255), 2)
    return angle

def convert_mediapipe_keypoints_for_model(lm_dict, landmark_list):
    inp_pushup = []
    for index in range(0, 36):
        if index < 18:
            try:
                inp_pushup.append(round(landmark_list[lm_dict[index]][1], 3))
            except IndexError:
                inp_pushup.append(0.0)
        else:
            try:
                inp_pushup.append(round(landmark_list[lm_dict[index - 18]][2], 3))
            except IndexError:
                inp_pushup.append(0.0)
    return inp_pushup

# Предположим, что вы хотите использовать классификатор DeepFit для определения типа упражнения
# Если этот функционал не нужен, можно проигнорировать соответствующие части кода

def set_video_feed_variables(video_path):
    # Используем загруженный видеофайл
    cap = cv2.VideoCapture(video_path)
    if not cap.isOpened():
        raise IOError(f"Не удалось открыть видеофайл: {video_path}")
    count = 0
    direction = 0
    form = 0
    feedback = "Bad Form. Correct Posture."
    frame_queue = deque(maxlen=250)
    clf = DeepFitClassifier('DeepFit/deepfit_classifier_v3.tflite')  # Убедитесь, что путь корректный
    return cap, count, direction, form, feedback, frame_queue, clf

def main():
    # Настройка параметров позы
    mode, complexity, smooth_landmarks, enable_segmentation, smooth_segmentation, detectionCon, trackCon, mpPose = set_pose_parameters()
    pose = mpPose.Pose(mode, complexity, smooth_landmarks,
                       enable_segmentation, smooth_segmentation,
                       detectionCon, trackCon)

    # Настройка видеофида
    cap, count, direction, form, feedback, frame_queue, clf = set_video_feed_variables(video_path)

    # Настройка параметров видео для сохранения результата
    fourcc = cv2.VideoWriter_fourcc(*'mp4v')
    output_filename = 'output_video.mp4'
    fps = cap.get(cv2.CAP_PROP_FPS)
    if fps == 0:
        fps = 30  # Значение по умолчанию, если не удалось получить FPS
        print("Не удалось определить FPS видео. Используется значение по умолчанию: 30")
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    out = cv2.VideoWriter(output_filename, fourcc, fps, (width, height))

    frame_count = 0
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    print(f"Total frames in input video: {total_frames}")

    while cap.isOpened():
        # Получение кадра из видео
        ret, img = cap.read()
        if not ret:
            break
        frame_count += 1
        print(f"Processing frame {frame_count} of {total_frames}", end='\r')

        # Конвертация из BGR (используемого cv2) в RGB (используемого Mediapipe)
        results = pose.process(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))

        # Получение позы и рисование ключевых точек
        img = get_pose(img, results, False)

        # Получение списка ключевых точек
        landmark_list = get_position(img, results, height, width, False)

        # Если ключевые точки существуют, получить соответствующие углы и выполнить подсчёт повторений
        if len(landmark_list) != 0:
            elbow_angle = get_angle(img, landmark_list, 11, 13, 15)
            shoulder_angle = get_angle(img, landmark_list, 13, 11, 23)
            hip_angle = get_angle(img, landmark_list, 11, 23, 25)

            # Интерполяция угла локтя для прогресс-бара и процента успешности
            pushup_success_percentage = np.interp(elbow_angle, (90, 160), (0, 100))
            pushup_progress_bar = np.interp(elbow_angle, (90, 160), (380, 50))

            # Проверка начальной формы
            if elbow_angle > 160 and shoulder_angle > 40 and hip_angle > 160:
                form = 1

            # Полный цикл выполнения отжиманий
            if form == 1:
                if pushup_success_percentage == 0:
                    if elbow_angle <= 90 and hip_angle > 160:
                        feedback = "Go Up"
                        if direction == 0:
                            count += 0.5
                            direction = 1
                    else:
                        feedback = "Bad Form. Correct Posture."

                if pushup_success_percentage == 100:
                    if elbow_angle > 160 and shoulder_angle > 40 and hip_angle > 160:
                        feedback = "Go Down"
                        if direction == 1:
                            count += 0.5
                            direction = 0
                    else:
                        feedback = "Bad Form. Correct Posture."

            print(f"Reps: {int(count)}")

            # Рисование прогресс-бара отжиманий
            if form == 1:
                cv2.rectangle(img, (580, 50), (600, 380), (0, 255, 0), 3)
                cv2.rectangle(img, (580, int(pushup_progress_bar)), (600, 380), (0, 255, 0), cv2.FILLED)
                cv2.putText(img, f'{int(pushup_success_percentage)}%', (565, 430), cv2.FONT_HERSHEY_PLAIN, 2,
                            (255, 0, 0), 2)

            # Отображение счёта отжиманий
            cv2.rectangle(img, (0, 380), (100, 480), (0, 255, 0), cv2.FILLED)
            cv2.putText(img, str(int(count)), (25, 455), cv2.FONT_HERSHEY_PLAIN, 5,
                        (255, 0, 0), 5)

            # Отображение обратной связи
            cv2.rectangle(img, (500, 0), (640, 40), (255, 255, 255), cv2.FILLED)
            cv2.putText(img, feedback, (500, 40 ), cv2.FONT_HERSHEY_PLAIN, 2,
                        (0, 255, 0), 2)

        # Запись аннотированного кадра в выходной файл
        out.write(img)

    # Завершение обработки видео
    cap.release()
    out.release()
    print("\nProcessing complete.")

    # Функция для отображения видео в Colab
    def display_video(video_path):
        mp4 = open(video_path, 'rb').read()
        data_url = "data:video/mp4;base64," + b64encode(mp4).decode()
        return HTML(f'''
            <video width="800" height="600" controls>
                <source src="{data_url}" type="video/mp4">
            </video>
        ''')

    # Проверка наличия выходного файла
    if os.path.exists(output_filename):
        # Отображение выходного видео
        display_video(output_filename)
    else:
        print(f"Не удалось создать выходной файл видео: {output_filename}")

if __name__ == "__main__":
    main()
```

Однако, я хочу превратить это все в сервер, который бы принимал на вход изображения и возвращал все необходимое, чтобы отрисовывать всб информацию на фронтенде, однако, что-то не так:
- Отрисовка скелета не выровнена с изображением камеры, наблюдается offset
- Он совершенно не подсчитывает количество подходов
- Он должен писать, хорошо ли делается упражнение или плохо.


Вот текущий код сервера:
```py
# app.py

import os
import threading
import time
import uuid
import math
import base64  # Добавлено для кодирования изображений

import cv2
import mediapipe as mp
import numpy as np
from collections import deque
from flask import Flask, request, jsonify
from flask_socketio import SocketIO, emit, join_room, leave_room
import gradio as gr
import requests  # Перемещено в начало
from io import BytesIO  # Добавлено для обработки изображений

# Импорт DeepFitClassifier
from DeepFit.DeepFitClassifier import DeepFitClassifier  # Убедитесь, что путь корректный

# Инициализация Flask и SocketIO
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app, cors_allowed_origins="*")

# Максимальное количество одновременных сессий
MAX_SESSIONS = 10

# Хранилище сессий
sessions = {}
sessions_lock = threading.Lock()

# Маркировка для Mediapipe
mpDraw = mp.solutions.drawing_utils
mpPose = mp.solutions.pose

# Функции для обработки видео
def set_pose_parameters():
    mode = False
    complexity = 1
    smooth_landmarks = True
    enable_segmentation = False
    smooth_segmentation = True
    detectionCon = 0.5
    trackCon = 0.5
    pose = mpPose.Pose(mode, complexity, smooth_landmarks,
                       enable_segmentation, smooth_segmentation,
                       detectionCon, trackCon)
    return pose

def get_position(results, height, width, img=None, draw=False):
    landmark_list = []
    if results.pose_landmarks:
        for id, landmark in enumerate(results.pose_landmarks.landmark):
            landmark_pixel_x, landmark_pixel_y = int(landmark.x * width), int(landmark.y * height)
            landmark_list.append([id, landmark_pixel_x, landmark_pixel_y])
            if draw and img is not None:
                cv2.circle(img, (landmark_pixel_x, landmark_pixel_y), 5, (255, 0, 0), cv2.FILLED)
    return landmark_list

def get_angle(landmark_list, point1, point2, point3):
    try:
        x1, y1 = landmark_list[point1][1:]
        x2, y2 = landmark_list[point2][1:]
        x3, y3 = landmark_list[point3][1:]
    except IndexError:
        return 0  # Возвращать 0, если ключевая точка отсутствует

    angle = math.degrees(math.atan2(y3 - y2, x3 - x2) -
                         math.atan2(y1 - y2, x1 - x2))

    # Обработка крайних случаев углов
    if angle < 0:
        angle += 360
        if angle > 180:
            angle = 360 - angle
    elif angle > 180:
        angle = 360 - angle

    return int(angle)

# Обработка видео для конкретной сессии
def video_processing(session_id, video_path=None, use_webcam=False):
    pose = set_pose_parameters()
    clf = DeepFitClassifier('DeepFit/deepfit_classifier_v3.tflite')  # Убедитесь, что путь корректный

    if use_webcam:
        cap = cv2.VideoCapture(0)
        if not cap.isOpened():
            socketio.emit('error', {'message': 'Не удалось открыть веб-камеру.'}, room=session_id)
            return
    else:
        if not video_path or not os.path.exists(video_path):
            socketio.emit('error', {'message': 'Неверный путь к видеофайлу.'}, room=session_id)
            return
        cap = cv2.VideoCapture(video_path)

    count = 0
    direction = 0
    form = 0
    feedback = "Bad Form. Correct Posture."
    frame_queue = deque(maxlen=250)

    while cap.isOpened():
        ret, img = cap.read()
        if not ret:
            break

        # Проверка активности сессии
        with sessions_lock:
            active = sessions.get(session_id, {}).get('active', False)
        if not active:
            break

        height, width = img.shape[:2]
        results = pose.process(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
        # Рисование ключевых точек на изображении
        landmark_list = get_position(results, height, width, img=img, draw=True)

        data = {}
        if len(landmark_list) != 0:
            elbow_angle = get_angle(landmark_list, 11, 13, 15)
            shoulder_angle = get_angle(landmark_list, 13, 11, 23)
            hip_angle = get_angle(landmark_list, 11, 23, 25)

            pushup_success_percentage = int(np.interp(elbow_angle, (90, 160), (0, 100)))
            pushup_progress_bar = int(np.interp(elbow_angle, (90, 160), (380, 50)))

            # Проверка начальной формы
            if elbow_angle > 160 and shoulder_angle > 40 and hip_angle > 160:
                form = 1

            # Полный цикл выполнения отжиманий
            if form == 1:
                if pushup_success_percentage == 0:
                    if elbow_angle <= 90 and hip_angle > 160:
                        feedback = "Go Up"
                        if direction == 0:
                            count += 0.5
                            direction = 1
                    else:
                        feedback = "Bad Form. Correct Posture."

                if pushup_success_percentage == 100:
                    if elbow_angle > 160 and shoulder_angle > 40 and hip_angle > 160:
                        feedback = "Go Down"
                        if direction == 1:
                            count += 0.5
                            direction = 0
                    else:
                        feedback = "Bad Form. Correct Posture."

            # Формирование данных для отправки клиенту
            data = {
                'count': int(count),
                'feedback': feedback,
                'pushup_success_percentage': int(pushup_success_percentage),
                'pushup_progress_bar': int(pushup_progress_bar),
            }

        # Кодирование изображения в base64
        _, buffer = cv2.imencode('.jpg', img)
        image_base64 = base64.b64encode(buffer).decode('utf-8')
        data['image'] = image_base64

        # Обновление данных сессии
        with sessions_lock:
            if session_id in sessions:
                sessions[session_id]['data'] = data

        # Отправка данных через SocketIO
        socketio.emit('video_frame', data, room=session_id)

        # Ограничение FPS (например, 30 FPS)
        socketio.sleep(1/30)

    cap.release()
    pose.close()
    socketio.emit('processing_complete', {'message': 'Processing complete.'}, room=session_id)

    # Очистка данных сессии
    with sessions_lock:
        if session_id in sessions:
            sessions.pop(session_id, None)

# Обработчики SocketIO событий
@socketio.on('connect')
def handle_connect():
    print('Client connected')

@socketio.on('disconnect')
def handle_disconnect():
    print('Client disconnected')
    # Очистка сессии при отключении
    for session_id, session in list(sessions.items()):
        if session['sid'] == request.sid:
            with sessions_lock:
                sessions.pop(session_id, None)
            break

@socketio.on('join')
def handle_join(data):
    session_id = data.get('session_id')
    if not session_id:
        # Создание нового session_id
        session_id = str(uuid.uuid4())
        with sessions_lock:
            if len(sessions) >= MAX_SESSIONS:
                emit('error', {'message': 'Максимальное количество сессий достигнуто.'})
                return
            sessions[session_id] = {
                'sid': request.sid,
                'active': False,
                'data': {}
            }
    join_room(session_id)
    emit('joined', {'session_id': session_id})
    print(f'Client joined session: {session_id}')

@socketio.on('start_video')
def handle_start_video(data):
    session_id = data.get('session_id')
    use_webcam = data.get('use_webcam', False)

    with sessions_lock:
        if session_id not in sessions:
            emit('error', {'message': 'Session ID не найден.'})
            return
        if sessions[session_id]['active']:
            emit('error', {'message': 'Видео уже обрабатывается.'})
            return
        sessions[session_id]['active'] = True

    # Запуск обработки видео в отдельном потоке
    thread = threading.Thread(target=video_processing, args=(session_id, None, use_webcam))
    thread.start()

@socketio.on('stop_video')
def handle_stop_video(data):
    session_id = data.get('session_id')
    with sessions_lock:
        if session_id not in sessions:
            emit('error', {'message': 'Session ID не найден.'})
            return
        sessions[session_id]['active'] = False
    emit('stopped', {'message': 'Видео остановлено.'})

# API эндпоинты для Gradio
@app.route('/api/get_latest/<session_id>', methods=['GET'])
def get_latest(session_id):
    with sessions_lock:
        session = sessions.get(session_id)
        if not session:
            return jsonify({'error': 'Session ID не найден.'}), 404
        data = session.get('data', {})
    return jsonify(data)

@app.route('/api/join', methods=['POST'])
def api_join():
    print("JOIN!!!!!!")
    with sessions_lock:
        if len(sessions) >= MAX_SESSIONS:
            return jsonify({'error': 'Максимальное количество сессий достигнуто.'}), 400
        session_id = str(uuid.uuid4())
        sessions[session_id] = {
            'sid': None,
            'active': False,
            'data': {}
        }
    return jsonify({'session_id': session_id})

@app.route('/api/start_video', methods=['POST'])
def api_start_video():
    data = request.json
    session_id = data.get('session_id')
    use_webcam = data.get('use_webcam', False)

    with sessions_lock:
        if session_id not in sessions:
            return jsonify({'error': 'Session ID не найден.'}), 404
        if sessions[session_id]['active']:
            return jsonify({'error': 'Видео уже обрабатывается.'}), 400
        sessions[session_id]['active'] = True

    # Запуск обработки видео в отдельном потоке
    thread = threading.Thread(target=video_processing, args=(session_id, None, use_webcam))
    thread.start()

    return jsonify({'message': 'Видео начато.'})

@app.route('/api/stop_video', methods=['POST'])
def api_stop_video():
    data = request.json
    session_id = data.get('session_id')

    with sessions_lock:
        if session_id not in sessions:
            return jsonify({'error': 'Session ID не найден.'}), 404
        sessions[session_id]['active'] = False

    return jsonify({'message': 'Видео остановлено.'})

# Gradio интерфейс
def gradio_interface():
    with gr.Blocks(title="Push-Up Tracker") as demo:
        gr.Markdown("## Push-Up Tracker")

        with gr.Row():
            connect_btn = gr.Button("Подключиться к сессии")
            session_id_text = gr.Textbox(label="Session ID", interactive=False)

        with gr.Row():
            start_btn = gr.Button("Начать поток видео (Веб-камера)")
            stop_btn = gr.Button("Остановить поток видео")

        with gr.Row():
            reps_display = gr.Number(label="Количество отжиманий", interactive=False)
            feedback_display = gr.Textbox(label="Обратная связь", interactive=False)

        with gr.Row():
            percentage_display = gr.Number(label="Успешность отжиманий (%)", interactive=False)
            progress_display = gr.HTML(label="Прогресс-бар")

        with gr.Row():
            video_display = gr.Image(label="Видео", interactive=False)  # Добавлен компонент для видео

        def connect():
            try:
                response = requests.post("http://localhost:5000/api/join")
                if response.status_code == 200:
                    session_id = response.json().get('session_id')
                    return session_id
                else:
                    return "Ошибка подключения."
            except Exception as e:
                return f"Ошибка подключения: {e}"

        def start_video(session_id):
            if not session_id or session_id.startswith("Ошибка"):
                return "Нет активной сессии."
            try:
                response = requests.post("http://localhost:5000/api/start_video", json={
                    'session_id': session_id,
                    'use_webcam': True
                })
                if response.status_code == 200:
                    return "Видео начато."
                else:
                    return response.json().get('error', 'Ошибка при запуске видео.')
            except Exception as e:
                return f"Ошибка при запуске видео: {e}"

        def stop_video(session_id):
            if not session_id or session_id.startswith("Ошибка"):
                return "Нет активной сессии."
            try:
                response = requests.post("http://localhost:5000/api/stop_video", json={
                    'session_id': session_id
                })
                if response.status_code == 200:
                    return "Видео остановлено."
                else:
                    return response.json().get('error', 'Ошибка при остановке видео.')
            except Exception as e:
                return f"Ошибка при остановке видео: {e}"

        def update_data(session_id):
            if not session_id or session_id.startswith("Ошибка"):
                return 0, "", 0, "", None
            try:
                response = requests.get(f"http://localhost:5000/api/get_latest/{session_id}")
                if response.status_code == 200:
                    data = response.json()
                    count = data.get('count', 0)
                    feedback = data.get('feedback', "")
                    percentage = data.get('pushup_success_percentage', 0)
                    progress = f"""
                    <div style="position: relative; width: 50px; height: 400px; border: 1px solid #000;">
                        <div style="position: absolute; bottom: 0; width: 100%; height: {percentage * 4}px; background-color: green;"></div>
                    </div>
                    """
                    image_base64 = data.get('image')
                    if image_base64:
                        image_bytes = base64.b64decode(image_base64)
                        image = image_bytes
                    else:
                        image = None
                    return count, feedback, percentage, progress, image
                else:
                    return 0, "Нет данных.", 0, "", None
            except Exception as e:
                return 0, f"Ошибка обновления данных: {e}", 0, "", None

        def live_update(session_id):
            while True:
                yield update_data(session_id)
                time.sleep(0.1)  # Уменьшено время ожидания для более быстрых обновлений

        connect_btn.click(fn=connect, outputs=session_id_text)
        start_btn.click(fn=start_video, inputs=session_id_text, outputs=None)
        stop_btn.click(fn=stop_video, inputs=session_id_text, outputs=None)

        demo.load(live_update, session_id_text, [reps_display, feedback_display, percentage_display, progress_display, video_display])

    demo.launch(share=True)

# Запуск Flask и Gradio в отдельных потоках
if __name__ == '__main__':
    # Запуск Flask сервера в отдельном демоническом потоке
    flask_thread = threading.Thread(target=lambda: socketio.run(app, host='0.0.0.0', port=5000, use_reloader=False))
    flask_thread.daemon = True  # Устанавливаем поток как демонический
    flask_thread.start()  # Запускаем Flask сервер

    # Дожидаемся запуска Flask сервера
    time.sleep(2)

    # Запуск Gradio интерфейса в основном потоке
    gradio_interface()

    # Основной поток блокируется, пока Gradio работает
    while True:
        time.sleep(1)
```
Можешь просмотреть что тут не так?