исправь пожалуйста код, так чтоб прогресс бар работал, потому что при начале конвертации показывает 0% а когда конвертация успешно завершится покажет сразу 100%, нету загрузки прогресс бара, и нету времени сколько прошло и сколько осталось

import os
import subprocess
import threading
import tkinter as tk
from tkinter import ttk, messagebox
from datetime import datetime
import time


class StreamRecorderApp:
    def __init__(self, root):
        self.root = root
        self.root.title("TikTok Stream Recorder")
        self.active_threads = {}
        self.stop_flags = {}

        # Левый фрейм для консоли
        left_frame = tk.Frame(root)
        left_frame.grid(row=0, column=0, rowspan=6, padx=5, pady=5, sticky="ns")

        tk.Label(left_frame, text="Консоль:").pack(anchor="w")
        self.console = tk.Text(left_frame, height=20, width=40, state="normal", bg="black", fg="white")
        self.console.pack(fill="both", expand=True)

        # Добавление контекстного меню для консоли
        self.console.bind("<Button-3>", self.show_console_context_menu)

        # Правый фрейм для элементов управления
        right_frame = tk.Frame(root)
        right_frame.grid(row=0, column=1, padx=5, pady=5)

        # Поля ввода и кнопки
        tk.Label(right_frame, text="Имя пользователя:").grid(row=0, column=0, padx=5, pady=5)
        self.username_entry = tk.Entry(right_frame, width=20)
        self.username_entry.grid(row=0, column=1, padx=5, pady=5)
        self.username_entry.bind("<Button-3>", self.show_username_context_menu)
        
        # Новый фрейм для списка конвертаций
        conversion_frame = tk.Frame(root)
        conversion_frame.grid(row=0, column=2, rowspan=6, padx=5, pady=5, sticky="ns")

        tk.Label(conversion_frame, text="Текущие конвертации:").pack(anchor="w")
        self.conversion_listbox = tk.Text(conversion_frame, height=25, width=55, bg="white", fg="black", state="normal")
        self.conversion_listbox.pack(fill="both", expand=True)


        tk.Label(right_frame, text="Качество:").grid(row=1, column=0, padx=5, pady=5)
        self.quality_combobox = ttk.Combobox(right_frame, values=["ld", "sd", "hd", "uhd_60"], state="readonly", width=17)
        self.quality_combobox.grid(row=1, column=1, padx=5, pady=5)
        self.quality_combobox.set("uhd_60")

        tk.Button(right_frame, text="Начать запись", command=self.start_recording).grid(row=2, column=0, columnspan=2, pady=10)

        # Список активных записей
        tk.Label(right_frame, text="Активные записи:").grid(row=3, column=0, columnspan=2)
        self.record_listbox = tk.Listbox(right_frame, height=10, width=40)
        self.record_listbox.grid(row=4, column=0, columnspan=2, padx=5, pady=5)

        tk.Button(right_frame, text="Остановить запись", command=self.stop_selected_recording).grid(row=5, column=0, columnspan=2, pady=10)

        self.create_output_dirs()

    def log_message(self, message):
        """Вывод сообщения в консоль."""
        self.console.insert(tk.END, f"{message}\n")
        self.console.see(tk.END)

    def create_output_dirs(self):
        self.output_dir = os.path.join(os.getcwd(), "output")
        self.output_fixed_dir = os.path.join(os.getcwd(), "output_fixed")
        os.makedirs(self.output_dir, exist_ok=True)
        os.makedirs(self.output_fixed_dir, exist_ok=True)

    def format_time(self, seconds):
        hours, seconds = divmod(seconds, 3600)
        minutes, seconds = divmod(seconds, 60)
        return f"{hours:02}:{minutes:02}:{seconds:02}"

    def show_console_context_menu(self, event):
        menu = tk.Menu(self.console, tearoff=0)
        menu.add_command(label="Копировать", command=self.copy_console_text)
        menu.post(event.x_root, event.y_root)

    def copy_console_text(self):
        selected_text = self.console.get(tk.SEL_FIRST, tk.SEL_LAST)
        self.root.clipboard_clear()
        self.root.clipboard_append(selected_text)

    def show_username_context_menu(self, event):
        menu = tk.Menu(self.username_entry, tearoff=0)
        menu.add_command(label="Вставить", command=self.paste_username_text)
        menu.post(event.x_root, event.y_root)

    def paste_username_text(self):
        try:
            self.username_entry.insert(tk.END, self.root.clipboard_get())
        except Exception as e:
            print(f"Ошибка вставки: {e}")

    def record_stream(self, username, quality):
        url = f"https://www.tiktok.com/@{username}/live"
        current_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
        output_path = os.path.join(self.output_dir, f"{username}_{current_time}.mp4")
        fixed_output_path = os.path.join(self.output_fixed_dir, f"{username}_{current_time}_fixed.mp4")

        def run_recording():
            self.stop_flags[username] = False
            process = subprocess.Popen(
                ["streamlink", url, quality, "--output", output_path],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
            )
            self.log_message(f"Начата запись для {username}...")
            self.update_record_list(username, "00:00:00")

            start_time = time.time()
            while process.poll() is None:
                if self.stop_flags[username]:
                    process.terminate()
                    process.wait()
                    break

                elapsed_time = int(time.time() - start_time)
                formatted_time = self.format_time(elapsed_time)
                self.update_record_list(username, formatted_time)
                time.sleep(1)

            # Запись завершена, ждем остановки записи пользователем для конвертации
            if self.stop_flags[username]:
                self.log_message(f"Запись для {username} остановлена.")
                self.convert_video(output_path, fixed_output_path)

            del self.active_threads[username]
            self.stop_flags.pop(username, None)
            self.update_record_list(username, remove=True)

        thread = threading.Thread(target=run_recording)
        self.active_threads[username] = thread
        thread.start()

    def stop_selected_recording(self):
        selected_index = self.record_listbox.curselection()
        if not selected_index:
            messagebox.showwarning("Ошибка", "Выберите запись для остановки.")
            return

        username = self.record_listbox.get(selected_index).split(":")[0]
        
        if username in self.active_threads:
            if self.stop_flags.get(username, False):  # Проверка на наличие флага остановки
                self.log_message(f"Запись для {username} уже остановлена.")  # Логируем, если уже остановлено
                return  # Прерываем выполнение, если запись уже остановлена
            
            # Устанавливаем флаг остановки
            self.stop_flags[username] = True
            
            # Проверяем поток на завершение
            self.root.after(100, self.wait_for_thread_to_stop, username)

    def wait_for_thread_to_stop(self, username):
        """Проверяет завершение записи для пользователя."""
        thread = self.active_threads.get(username)
        
        if thread is None:  # Поток уже завершён, ничего не делаем
            self.stop_flags.pop(username, None)  # Убираем флаг остановки
            self.log_message(f"Поток записи для {username} завершён.")
            return
        
        if not thread.is_alive():  # Поток завершён
            self.active_threads.pop(username, None)  # Убираем поток
            self.stop_flags.pop(username, None)  # Убираем флаг
            self.log_message(f"Поток записи для {username} завершён.")
            return

        # Если поток всё ещё активен, повторяем проверку через 100ms
        self.root.after(100, self.wait_for_thread_to_stop, username)

    def parse_duration(self, line):
        parts = line.split(",")[0].split("Duration:")[1].strip()
        h, m, s = map(float, parts.split(":"))
        return int(h * 3600 + m * 60 + s)

    def parse_time(self, line):
        """Парсит строку stderr ffmpeg для извлечения времени обработки."""
        if "time=" not in line:
            return 0

        try:
            time_str = line.split("time=")[-1].split(" ")[0].strip()
            if time_str == "N/A":
                return 0  # Если время отсутствует, возвращаем 0
            h, m, s = map(float, time_str.split(":"))
            return int(h * 3600 + m * 60 + s)
        except (ValueError, IndexError):
            return 0  # Возвращаем 0, если формат строки не соответствует ожидаемому

    def add_to_convert_list(self, username):
        """Добавить запись для нового файла в список конвертаций."""
        self.conversion_listbox.insert(tk.END, f"{username}\n[                              ] 0%\n")
        self.conversion_listbox.insert(tk.END, "_" * 40 + "\n")
        self.conversion_listbox.see(tk.END)

    def remove_from_convert_list(self, username):
        """Удалить запись из списка."""
        lines = self.conversion_listbox.get("1.0", tk.END).splitlines()
        updated_lines = []
        for i in range(0, len(lines), 3):
            if username not in lines[i]:
                updated_lines.extend(lines[i:i + 3])

        self.conversion_listbox.delete("1.0", tk.END)
        self.conversion_listbox.insert(tk.END, "\n".join(updated_lines))
        self.conversion_listbox.see(tk.END)

    def update_convert_list(self, username, progress, total_duration, elapsed_time, completed=False):
        """Обновить прогресс конвертации."""
        lines = self.conversion_listbox.get("1.0", tk.END).splitlines()
        updated_lines = []
        for i in range(0, len(lines), 3):  # Каждая запись занимает 3 строки
            if username in lines[i]:
                if completed:
                    updated_lines.append(f"{username}")
                    updated_lines.append(f"[##############################] 100% (Готово)")
                else:
                    # Отображение прогресса
                    bar = "#" * (progress // 4) + " " * (25 - progress // 4)
                    remaining_time = total_duration - elapsed_time
                    updated_lines.append(f"{username}")
                    updated_lines.append(f"[{bar}] {progress}% ({self.format_time(elapsed_time)}/{self.format_time(remaining_time)})")
                updated_lines.append("_" * 40)
            else:
                updated_lines.extend(lines[i:i + 3])

        # Удаляем старые строки и вставляем обновленные
        self.conversion_listbox.delete("1.0", tk.END)
        self.conversion_listbox.insert(tk.END, "\n".join(updated_lines))
        self.conversion_listbox.see(tk.END)

    def convert_video(self, input_path, output_path):
        def run_conversion():
            username = os.path.basename(input_path).split("_")[0]  # Извлекаем имя пользователя
            self.add_to_convert_list(username)

            ffmpeg_path = os.path.join(os.getcwd(), "ffmpeg.exe")

            if not os.path.exists(ffmpeg_path):
                self.log_message("Ошибка: ffmpeg.exe не найден в корневой папке!")
                self.remove_from_convert_list(username)
                return

            if not os.path.exists(input_path):
                self.log_message(f"Ошибка: входной файл для конвертации не найден: {input_path}")
                self.remove_from_convert_list(username)
                return

            process = subprocess.Popen(
                [ffmpeg_path, "-i", input_path, "-c:v", "libx264", "-c:a", "aac", "-strict", "experimental", output_path],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True
            )

            total_duration = 0
            elapsed_time = 0

            for line in process.stderr:
                if "Duration" in line and total_duration == 0:
                    total_duration = self.parse_duration(line)  # Определяем общую длительность

                if "time=" in line:
                    elapsed_time = self.parse_time(line)  # Получаем текущее время обработки
                    if total_duration > 0:
                        progress = int((elapsed_time / total_duration) * 100)
                        self.update_convert_list(username, progress, total_duration, elapsed_time)

            process.wait()
            if process.returncode == 0:
                self.update_convert_list(username, 100, total_duration, total_duration, completed=True)
                os.remove(input_path)
                self.log_message(f"Видео успешно конвертировано: {output_path}")
            else:
                self.log_message("Ошибка конвертации!")
                self.remove_from_convert_list(username)

        threading.Thread(target=run_conversion).start()



    def start_recording(self):
        username = self.username_entry.get().strip()
        quality = self.quality_combobox.get().strip()

        if not username:
            messagebox.showwarning("Ошибка", "Введите имя пользователя.")
            return

        if username in self.active_threads:
            messagebox.showwarning("Ошибка", f"Запись для пользователя {username} уже идёт.")
            return

        self.update_record_list(username, "00:00:00")
        self.record_stream(username, quality)

    def update_record_list(self, username, formatted_time=None, remove=False):
        selected_index = self.record_listbox.curselection()

        for i in range(self.record_listbox.size()):
            item = self.record_listbox.get(i)
            if item.startswith(username):
                if remove:
                    self.record_listbox.delete(i)
                elif formatted_time:
                    self.record_listbox.delete(i)
                    self.record_listbox.insert(i, f"{username}: {formatted_time}")
                break
        else:
            if not remove:
                self.record_listbox.insert(tk.END, f"{username}: {formatted_time}")

        if selected_index:
            self.record_listbox.selection_set(selected_index)


if __name__ == "__main__":
    root = tk.Tk()
    app = StreamRecorderApp(root)
    root.mainloop()