working version. something wrong with icons

This commit is contained in:
2025-10-16 14:24:49 +03:00
parent 414cb4d38e
commit add2e15d5f
12 changed files with 371 additions and 104 deletions
+288 -60
View File
@@ -1,8 +1,11 @@
import os
import queue
import sys
import json
import threading
import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext
from PIL import Image, ImageTk
import customtkinter as ctk
import torch
@@ -10,11 +13,12 @@ import torch
from transcription.audio_transcription import AudioTranscription
from transcription.device_configuration import DeviceConfiguration
from transcription.torch_checker import check_torch
from ui.ui_log_handler import setup_ui_logger
from ui.tooltip import ToolTip
from ui.ui_log_handler import setup_ui_logger
from utils.requests_to_api import LLMrequest
WINDOW_WIDTH = 900
WINDOW_HEIGHT = 650
WINDOW_WIDTH = 1000
WINDOW_HEIGHT = 725
class TranscriberApp(ctk.CTk):
@@ -25,16 +29,60 @@ class TranscriberApp(ctk.CTk):
ctk.set_appearance_mode("System")
ctk.set_default_color_theme("blue")
# TODO: fix this stuff
base_path = getattr(sys, "_MEIPASS", os.path.dirname(os.path.abspath(__file__)))
icon_path = os.path.join(base_path, "assets", "logo.png")
applied_method = None
try:
self.iconbitmap(icon_path)
applied_method = "iconbitmap"
except Exception as e:
print("iconbitmap did not work:", e)
if applied_method is None:
try:
img = tk.PhotoImage(file=icon_path)
self.iconphoto(False, img)
applied_method = "iconphoto"
except Exception as e:
print("iconphoto did not work:", e)
if applied_method is None:
try:
pil = Image.open(icon_path)
photo = ImageTk.PhotoImage(pil)
self.tk.call('wm', 'iconphoto', self._w, photo)
applied_method = "tk.call"
except Exception as e:
print("wm iconphoto via tk.call did not work:", e)
if applied_method:
print(f"Applied method: {applied_method}")
else:
print("Can't set an icon.")
# states
self.progress_queue = queue.Queue()
self.transcribe_thread = None
self.stop_flag = threading.Event()
# user variables
self.model_var = tk.StringVar(value="openai/whisper-large-v2")
# USER VARIABLES
# transcription model settings
self.model_var = tk.StringVar(value="openai/whisper-large-v3-turbo")
self.batch_var = tk.StringVar(value="32")
self.dtype_var = tk.StringVar(value="torch.float16")
self.chunk_var = tk.StringVar(value="30")
self.dtype_var = tk.StringVar(value="torch.float16")
self.transcription_lang_var = tk.StringVar(value="ru")
# llm settings
self.conspect_transcription_lang_var = tk.StringVar(value="Russian")
self.api_key_var = tk.StringVar(value="")
self.base_url_var = tk.StringVar(value="")
self.api_model_var = tk.StringVar(value="")
# checkboxes
self.create_conspect = tk.BooleanVar(value=False)
self.remove_transcription = tk.BooleanVar(value=False)
# settings device options
device_opts = []
@@ -62,7 +110,7 @@ class TranscriberApp(ctk.CTk):
# logger
self.ui_logger = setup_ui_logger(self.log_box)
# main transcription tab
### TRANSCRIPTION TAB
def _build_transcription_tab(self):
# file selectors
file_frame = ctk.CTkFrame(self.transcript_tab, corner_radius=10)
@@ -93,16 +141,34 @@ class TranscriberApp(ctk.CTk):
ctk.CTkButton(ctrl_frame, text="Check Torch", command=self._check_torch).pack(
side="left", padx=10, pady=5
)
self.start_btn = ctk.CTkButton(
ctrl_frame, text="Start transcription", command=self._start_transcription
self.start_button = ctk.CTkButton(
ctrl_frame, text="Start", command=self._start_transcription
)
self.start_btn.pack(side="left", padx=10, pady=5)
self.start_button.pack(side="left", padx=10, pady=5)
self.stop_btn = ctk.CTkButton(
self.stop_button = ctk.CTkButton(
ctrl_frame, text="Stop", command=self._stop_transcription, state="disabled"
)
self.stop_btn.pack(side="left", padx=10, pady=5)
self.stop_button.pack(side="left", padx=10, pady=5)
self.create_conspect_checkbox = ctk.CTkCheckBox(
ctrl_frame,
text="Create conspect",
variable=self.create_conspect,
onvalue=True,
offvalue=False,
)
self.create_conspect_checkbox.pack(side="left", padx=10, pady=5)
self.remove_transcription_checkbox = ctk.CTkCheckBox(
ctrl_frame,
text="Remove transcription file after",
variable=self.remove_transcription,
onvalue=True,
offvalue=False,
)
self.remove_transcription_checkbox.pack(side="left", padx=10, pady=5)
# TODO: add unload model button here
# log box
@@ -114,65 +180,181 @@ class TranscriberApp(ctk.CTk):
)
self.log_box.pack(padx=20, pady=10, expand=True, fill="both")
### SETTINGS TAB
def _build_settings_tab(self):
# TODO: add tooltips here
pad = 20
### Model
ctk.CTkLabel(self.settings_tab, text="Model:").pack(
anchor="w", padx=pad, pady=(pad, 5)
)
ctk.CTkOptionMenu(
self.settings_tab,
def add_setting(parent, row, col, text, tooltip, variable, values: list | None):
frame = ctk.CTkFrame(parent)
frame.grid(row=row, column=col, padx=pad, pady=(pad, 5), sticky="nsew")
label = ctk.CTkLabel(frame, text=text)
label.grid(row=0, column=0, sticky="w")
help_icon = ctk.CTkLabel(frame, text="?", width=20, cursor="question_arrow")
help_icon.grid(row=0, column=1, sticky="w", padx=(5, 0))
ToolTip(help_icon, tooltip)
if values:
ctk.CTkOptionMenu(frame, variable=variable, values=values).grid(
row=1, column=0, columnspan=2, sticky="ew", pady=(5, 0)
)
else:
ctk.CTkEntry(frame, textvariable=variable).grid(
row=1, column=0, columnspan=2, sticky="ew", pady=(5, 0)
)
frame.grid_columnconfigure(0, weight=1)
grid = ctk.CTkFrame(self.settings_tab)
grid.pack(fill="both", expand=True)
# first row
### Model setting
add_setting(
parent=grid,
row=0,
col=0,
text="Model:",
tooltip="Choose model for speed recognition",
variable=self.model_var,
values=[
# maybe delete this option?
"openai/whisper-large-v3-turbo",
"openai/whisper-large-v2",
"openai/whisper-large",
"openai/whisper-medium",
"openai/whisper-small",
"openai/whisper-tiny",
],
).pack(fill="x", padx=pad, pady=5)
### Batch size
ctk.CTkLabel(self.settings_tab, text="Batch size:").pack(
anchor="w", padx=pad, pady=(pad, 5)
)
ctk.CTkOptionMenu(
self.settings_tab,
### Batch size setting
add_setting(
parent=grid,
row=0,
col=1,
text="Batch size:",
tooltip="Chunks count for one iteration",
variable=self.batch_var,
values=["32", "16", "8", "4", "2"],
).pack(fill="x", padx=pad, pady=5)
### Data type
ctk.CTkLabel(self.settings_tab, text="Data type:").pack(
anchor="w", padx=pad, pady=(pad, 5)
)
ctk.CTkOptionMenu(
self.settings_tab,
# second row
### Data type setting
add_setting(
parent=grid,
row=1,
col=0,
text="Data type:",
tooltip="Data type for calculations",
variable=self.dtype_var,
values=["torch.float16", "torch.float32", "torch.bfloat16"],
).pack(fill="x", padx=pad, pady=5)
### Chunk length
ctk.CTkLabel(self.settings_tab, text="Chunk length (s):").pack(
anchor="w", padx=pad, pady=(pad, 5)
)
ctk.CTkOptionMenu(
self.settings_tab,
### Chunk Length setting
add_setting(
parent=grid,
row=1,
col=1,
text="Chunk length (s):",
tooltip="Maximum length of processing audio fragment",
variable=self.chunk_var,
values=["30", "25", "20", "15", "10", "5"],
).pack(fill="x", padx=pad, pady=5)
### Device
ctk.CTkLabel(self.settings_tab, text="Device:").pack(
anchor="w", padx=pad, pady=(pad, 5)
)
ctk.CTkOptionMenu(
self.settings_tab,
# third row
### Device setting
add_setting(
parent=grid,
row=2,
col=0,
text="Device:",
tooltip="Choose device\n- CUDA for CUDA & ROCm\n- MPS for Apple Silicon \n- CPU for CPU-only mode",
variable=self.device_var,
values=self.device_opts,
).pack(fill="x", padx=pad, pady=5)
)
### Transcription language setting
add_setting(
parent=grid,
row=2,
col=1,
text="Transcription language:",
tooltip="Choose the transcription language",
variable=self.transcription_lang_var,
values=["ru", "en"],
)
# fourth row
### OpenAI API key setting
add_setting(
parent=grid,
row=3,
col=0,
text="Insert OpenAI API key here:",
tooltip="Give this programm access to LLM that would create a fully prepared conspect with AI overviews",
variable=self.api_key_var,
values=None,
)
### Model name setting
add_setting(
parent=grid,
row=3,
col=1,
text="Model name:",
tooltip="Name of the model that you are going to use",
variable=self.api_model_var,
values=None,
)
# fifth row
### Base URL setting
add_setting(
parent=grid,
row=4,
col=0,
text="Base URL:",
tooltip="OpenAI base URL. Blank for None.",
variable=self.base_url_var,
values=None,
)
### Output (conspect) language setting
add_setting(
parent=grid,
row=4,
col=1,
text="Conspect language:",
tooltip="Conspect language. Blank for English (default)",
variable=self.conspect_transcription_lang_var,
values=None,
)
### Custom Prompt setting
customPromptFrame = ctk.CTkFrame(grid)
customPromptFrame.grid(
row=5, column=0, columnspan=2, padx=20, pady=(20, 5), sticky="nsew"
)
label = ctk.CTkLabel(customPromptFrame, text="Custom Prompt:")
label.grid(row=0, column=0, sticky="w")
help_icon = ctk.CTkLabel(
customPromptFrame, text="?", width=20, cursor="question_arrow"
)
help_icon.grid(row=0, column=1, sticky="w", padx=(5, 0))
ToolTip(
help_icon,
"Enter your custom prompt for model.",
)
self.custom_prompt_textbox = ctk.CTkTextbox(
customPromptFrame, width=400, height=150
)
self.custom_prompt_textbox.grid(
row=1, column=0, columnspan=2, sticky="nsew", pady=(5, 0)
)
customPromptFrame.grid_columnconfigure(0, weight=1)
customPromptFrame.grid_rowconfigure(1, weight=1)
grid.grid_columnconfigure((0, 1), weight=1)
# action buttons
def _browse_input(self):
@@ -183,7 +365,7 @@ class TranscriberApp(ctk.CTk):
("Audio files", "*.wav *.mp3 *.m4a *.flac *.ogg"),
("Video files", "*.mp4 *.mkv *.avi"),
("All files", "*.*"),
]
],
)
if path:
self.input_file_var.set(path)
@@ -204,6 +386,26 @@ class TranscriberApp(ctk.CTk):
def _check_torch(self):
check_torch(self.ui_logger)
self.ui_logger.info(f"==== Transcription ====")
self.ui_logger.info(f"Transcription model: {self.model_var.get()}")
self.ui_logger.info(f"Batch size (in chunks): {self.batch_var.get()}")
self.ui_logger.info(f"Chunk size (in seconds): {self.chunk_var.get()}")
self.ui_logger.info(f"Data type: {self.dtype_var.get()}")
self.ui_logger.info(f"Transcription language: {self.transcription_lang_var.get()}")
self.ui_logger.info(f"=======================")
debug_api_key_var: str = self.api_key_var.get() if self.api_key_var.get() else "Not set"
debug_api_model_var: str = self.api_model_var.get() if self.api_model_var.get() else "Not set"
debug_base_url_var: str = self.base_url_var.get() if self.base_url_var.get() else "Not set"
debug_transcription_lang_var = self.transcription_lang_var.get() if self.transcription_lang_var.get() else "Not set"
self.ui_logger.info(f"===== LLM Setting =====")
self.ui_logger.info(f"API key: {debug_api_key_var}")
self.ui_logger.info(f"Model name setting: {debug_api_model_var}")
self.ui_logger.info(f"Base URL setting: {debug_base_url_var}")
self.ui_logger.info(f"=======================")
def _start_transcription(self):
infile = self.input_file_var.get().strip()
@@ -211,8 +413,8 @@ class TranscriberApp(ctk.CTk):
messagebox.showerror("Error", "Please select a valid input file.")
return
self.start_btn.configure(state="disabled")
self.stop_btn.configure(state="normal")
self.start_button.configure(state="disabled")
self.stop_button.configure(state="normal")
self.ui_logger.info("Starting transcription...")
self.stop_flag.clear()
@@ -221,10 +423,12 @@ class TranscriberApp(ctk.CTk):
)
self.transcribe_thread.start()
def _stop_transcription(self):
def _stop_transcription(self, Audio: AudioTranscription):
self.stop_flag.set()
self.ui_logger.info("Stopping transcription...")
self.stop_btn.configure(state="disabled")
self.ui_logger.info("Unloading model...")
Audio._unload_model()
self.stop_button.configure(state="disabled")
def _transcribe_worker(self, infile: str):
try:
@@ -239,20 +443,44 @@ class TranscriberApp(ctk.CTk):
filepath=infile,
device_configuration=config,
logger=self.ui_logger,
language=self.transcription_lang_var.get(),
)
transcription = Audio.transcribe_audio()
outfile = self.output_file_var.get().strip()
if not outfile:
outfile = infile + ".txt"
outfile = infile
if not self.remove_transcription.get():
outfile += ".txt"
with open(outfile, "w", encoding="utf-8") as f:
f.write(transcription)
with open(outfile, "w", encoding="utf-8") as f:
f.write(transcription)
self.ui_logger.info(f"Transcription saved to {outfile}.")
if self.create_conspect.get():
# TODO: add custom prompt ability here
# TODO: add logging here
self.ui_logger.info(f"Starting creating conspect via {self.api_model_var.get()}...")
with open("utils/default_prompt.json", "r", encoding="utf-8") as f:
default_prompt = json.load(f)["prompt"]
prompt = transcription + default_prompt # if custom prompt is empty do something
request = LLMrequest(
api_key=self.api_key_var.get(),
model_name=self.api_model_var.get(),
base_url=self.base_url_var.get(),
)
response = request.get_response(prompt=prompt)
outfile += ".md"
with open(outfile, "w", encoding="utf-8") as f:
f.write(response)
self.ui_logger.info(f"Conspect saved to {outfile}.")
self.ui_logger.info(f"Transcription saved to {outfile}")
except Exception as e:
self.ui_logger.error(f"Error: {e}")
messagebox.showerror("Error", str(e))
finally:
self.start_btn.configure(state="normal")
self.stop_btn.configure(state="disabled")
self.start_button.configure(state="normal")
self.stop_button.configure(state="disabled")