```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()
```

Хорошо, преврати это все в сервер flask.

Преврати это все API на Flask.

Как это должно быть:
1) Сделай систему сессий обработки. Чтобы при подсоединении к сессии устанавливался канал websocket для передачи изображений в потоке, а также для получения результатов анализа модели.
2) Сделай простой сайт на Gradio(чтобы он работал через встроенный публичный адрес), где была бы кнопка подключения к сессии, ожидания ее(максимально 10), кнопки начала потока видео, остановки и окна для предпросмотра веб-камеры(если поток активен).

Сервер должен возвращать не измененное изображение через cv2, а массив с необходимыми данными и по ним, на фронтенде все будет отрисовываваться(включая вертикальный прогресс-бар и иную информацию, которая сейчас отрисовывается через cv2.
Можешь пожалуйста это еще и застилизовать красиво и минималистично. Спасибо большое!