working version. something wrong with icons
This commit is contained in:
@@ -12,3 +12,6 @@ wheels/
|
|||||||
main.todo
|
main.todo
|
||||||
sample.mp3
|
sample.mp3
|
||||||
*.spec
|
*.spec
|
||||||
|
|
||||||
|
# лютый завоз
|
||||||
|
ui/assets/question_mark.png
|
||||||
@@ -11,5 +11,5 @@ conda env create -f environment.yml
|
|||||||
or for CUDA/Nvidia:
|
or for CUDA/Nvidia:
|
||||||
```
|
```
|
||||||
conda create -n notecast -c pytorch -c nvidia pytorch torchvision torchaudio transformers python=3.12
|
conda create -n notecast -c pytorch -c nvidia pytorch torchvision torchaudio transformers python=3.12
|
||||||
conda install ffmpeg customtkinter -c conda-forge -c bioconda
|
conda install ffmpeg customtkinter openai -c conda-forge -c bioconda
|
||||||
```
|
```
|
||||||
+2
-1
@@ -6,9 +6,10 @@ channels:
|
|||||||
- bioconda
|
- bioconda
|
||||||
dependencies:
|
dependencies:
|
||||||
- pytorch
|
- pytorch
|
||||||
|
- ffmpeg
|
||||||
- torchvision
|
- torchvision
|
||||||
- torchaudio
|
- torchaudio
|
||||||
- transformers
|
- transformers
|
||||||
- python=3.12
|
- python=3.12
|
||||||
- ffmpeg
|
|
||||||
- customtkinter
|
- customtkinter
|
||||||
|
- openai
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
$envName = "notecast"
|
|
||||||
|
|
||||||
Write-Output >>> Creating environment $envName from environment.yml"
|
|
||||||
conda env create -f environment.yml -n $envName
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
Write-Output ">>> Environment already exists, updating..."
|
|
||||||
conda env update -f environment.yml --prune
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Output ">>> Activating environment"
|
|
||||||
conda activate $envName
|
|
||||||
|
|
||||||
Write-Output ">>> Download completed!"
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
ENV_NAME="notecast"
|
|
||||||
|
|
||||||
echo ">>> Creating environment $ENV_NAME from environment.yml"
|
|
||||||
conda env create -f environment.yml || conda env update -f environment.yml --prune
|
|
||||||
|
|
||||||
echo ">>> Activating environment"
|
|
||||||
eval "$(conda shell.bash hook)"
|
|
||||||
conda activate "$ENV_NAME"
|
|
||||||
|
|
||||||
echo ">>> Download completed!"
|
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
import time
|
import time
|
||||||
|
import sys
|
||||||
|
import gc
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
import torchaudio
|
import torchaudio
|
||||||
@@ -27,8 +29,8 @@ class AudioTranscription:
|
|||||||
custom_batch_length: int
|
custom_batch_length: int
|
||||||
|
|
||||||
device = "cuda"
|
device = "cuda"
|
||||||
processor: WhisperProcessor
|
processor: WhisperProcessor | None
|
||||||
model: WhisperForConditionalGeneration
|
model: WhisperForConditionalGeneration | None
|
||||||
logger: logging.Logger
|
logger: logging.Logger
|
||||||
torch_dtype: torch.dtype
|
torch_dtype: torch.dtype
|
||||||
|
|
||||||
@@ -41,7 +43,7 @@ class AudioTranscription:
|
|||||||
filepath: str,
|
filepath: str,
|
||||||
device_configuration: DeviceConfiguration,
|
device_configuration: DeviceConfiguration,
|
||||||
logger: logging.Logger,
|
logger: logging.Logger,
|
||||||
language="ru",
|
language: str = "ru",
|
||||||
) -> None:
|
) -> None:
|
||||||
# TODO: add pretty docs here
|
# TODO: add pretty docs here
|
||||||
self.filepath = filepath
|
self.filepath = filepath
|
||||||
@@ -65,15 +67,29 @@ class AudioTranscription:
|
|||||||
def _load_model(self) -> None:
|
def _load_model(self) -> None:
|
||||||
self.logger.info("Loading model WhisperProcessor...")
|
self.logger.info("Loading model WhisperProcessor...")
|
||||||
try:
|
try:
|
||||||
|
start_time = time.time()
|
||||||
|
|
||||||
self.processor = WhisperProcessor.from_pretrained(self.model_name)
|
self.processor = WhisperProcessor.from_pretrained(self.model_name)
|
||||||
self.model = WhisperForConditionalGeneration.from_pretrained(
|
self.model = WhisperForConditionalGeneration.from_pretrained(
|
||||||
self.model_name, torch_dtype = self.torch_dtype
|
self.model_name, torch_dtype=self.torch_dtype
|
||||||
).to(self.device)
|
).to(self.device)
|
||||||
|
|
||||||
self.logger.info("Model loaded.")
|
end_time = time.time()
|
||||||
|
self.logger.info(
|
||||||
|
f"Model loaded successfully in {end_time - start_time:.2f} seconds."
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error while loading model: {e}")
|
self.logger.error(f"Error while loading model: {e}")
|
||||||
|
|
||||||
|
def _unload_model(self) -> None:
|
||||||
|
self.logger.info("Unloading model...")
|
||||||
|
self.model = None
|
||||||
|
self.processor = None
|
||||||
|
if self.device == "cuda":
|
||||||
|
torch.cuda.empty_cache()
|
||||||
|
# TODO: maybe do something here for MPS
|
||||||
|
self.logger.info("Model unloaded successfully.")
|
||||||
|
|
||||||
def _load_file(self) -> None:
|
def _load_file(self) -> None:
|
||||||
self.logger.info(f"Loading file {self.filepath}")
|
self.logger.info(f"Loading file {self.filepath}")
|
||||||
try:
|
try:
|
||||||
@@ -118,11 +134,14 @@ class AudioTranscription:
|
|||||||
for i in range(0, len(self.chunks), self.custom_batch_length):
|
for i in range(0, len(self.chunks), self.custom_batch_length):
|
||||||
batch = self.chunks[i : i + self.custom_batch_length]
|
batch = self.chunks[i : i + self.custom_batch_length]
|
||||||
self.batches.append(batch)
|
self.batches.append(batch)
|
||||||
self.logger.info(f"Total: {len(self.batches)} batches")
|
self.logger.info(f"Total: {len(self.batches)} batches, weight = {sys.getsizeof(self.batches)}")
|
||||||
|
|
||||||
def _process_all_batches(self) -> None:
|
def _process_all_batches(self) -> None:
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
try:
|
try:
|
||||||
|
assert self.processor is not None
|
||||||
|
assert self.model is not None
|
||||||
|
|
||||||
self.all_transcription = []
|
self.all_transcription = []
|
||||||
|
|
||||||
for idx in tqdm(range(len(self.batches))):
|
for idx in tqdm(range(len(self.batches))):
|
||||||
@@ -149,6 +168,15 @@ class AudioTranscription:
|
|||||||
predicted_ids, skip_special_tokens=True
|
predicted_ids, skip_special_tokens=True
|
||||||
)
|
)
|
||||||
self.all_transcription.extend(texts)
|
self.all_transcription.extend(texts)
|
||||||
|
|
||||||
|
inputs = None
|
||||||
|
input_features = None
|
||||||
|
predicted_ids = None
|
||||||
|
gc.collect()
|
||||||
|
if self.device.startswith("cuda"):
|
||||||
|
torch.cuda.empty_cache()
|
||||||
|
torch.cuda.ipc_collect()
|
||||||
|
|
||||||
end_time = time.time()
|
end_time = time.time()
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
f"Transcription completed in {end_time - start_time:.2f} seconds"
|
f"Transcription completed in {end_time - start_time:.2f} seconds"
|
||||||
@@ -165,4 +193,5 @@ class AudioTranscription:
|
|||||||
self._split_to_chunks()
|
self._split_to_chunks()
|
||||||
self._resplit_chunks_to_batches()
|
self._resplit_chunks_to_batches()
|
||||||
self._process_all_batches()
|
self._process_all_batches()
|
||||||
|
self._unload_model()
|
||||||
return " ".join(self.all_transcription)
|
return " ".join(self.all_transcription)
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
+4
-1
@@ -1,5 +1,6 @@
|
|||||||
import customtkinter as ctk
|
import customtkinter as ctk
|
||||||
|
|
||||||
|
|
||||||
class ToolTip(ctk.CTkToplevel):
|
class ToolTip(ctk.CTkToplevel):
|
||||||
def __init__(self, widget, text):
|
def __init__(self, widget, text):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -7,7 +8,9 @@ class ToolTip(ctk.CTkToplevel):
|
|||||||
self.overrideredirect(True)
|
self.overrideredirect(True)
|
||||||
self.attributes("-topmost", True)
|
self.attributes("-topmost", True)
|
||||||
|
|
||||||
self.label = ctk.CTkLabel(self, text=text, fg_color="gray20", corner_radius=6, padx=10, pady=5)
|
self.label = ctk.CTkLabel(
|
||||||
|
self, text=text, fg_color="gray20", corner_radius=6, padx=10, pady=5
|
||||||
|
)
|
||||||
self.label.pack()
|
self.label.pack()
|
||||||
|
|
||||||
self.widget = widget
|
self.widget = widget
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import os
|
import os
|
||||||
import queue
|
import queue
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
import threading
|
import threading
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import filedialog, messagebox, scrolledtext
|
from tkinter import filedialog, messagebox, scrolledtext
|
||||||
|
from PIL import Image, ImageTk
|
||||||
|
|
||||||
import customtkinter as ctk
|
import customtkinter as ctk
|
||||||
import torch
|
import torch
|
||||||
@@ -10,11 +13,12 @@ import torch
|
|||||||
from transcription.audio_transcription import AudioTranscription
|
from transcription.audio_transcription import AudioTranscription
|
||||||
from transcription.device_configuration import DeviceConfiguration
|
from transcription.device_configuration import DeviceConfiguration
|
||||||
from transcription.torch_checker import check_torch
|
from transcription.torch_checker import check_torch
|
||||||
from ui.ui_log_handler import setup_ui_logger
|
|
||||||
from ui.tooltip import ToolTip
|
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_WIDTH = 1000
|
||||||
WINDOW_HEIGHT = 650
|
WINDOW_HEIGHT = 725
|
||||||
|
|
||||||
|
|
||||||
class TranscriberApp(ctk.CTk):
|
class TranscriberApp(ctk.CTk):
|
||||||
@@ -25,16 +29,60 @@ class TranscriberApp(ctk.CTk):
|
|||||||
ctk.set_appearance_mode("System")
|
ctk.set_appearance_mode("System")
|
||||||
ctk.set_default_color_theme("blue")
|
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
|
# states
|
||||||
self.progress_queue = queue.Queue()
|
self.progress_queue = queue.Queue()
|
||||||
self.transcribe_thread = None
|
self.transcribe_thread = None
|
||||||
self.stop_flag = threading.Event()
|
self.stop_flag = threading.Event()
|
||||||
|
|
||||||
# user variables
|
# USER VARIABLES
|
||||||
self.model_var = tk.StringVar(value="openai/whisper-large-v2")
|
# transcription model settings
|
||||||
|
self.model_var = tk.StringVar(value="openai/whisper-large-v3-turbo")
|
||||||
self.batch_var = tk.StringVar(value="32")
|
self.batch_var = tk.StringVar(value="32")
|
||||||
self.dtype_var = tk.StringVar(value="torch.float16")
|
|
||||||
self.chunk_var = tk.StringVar(value="30")
|
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
|
# settings device options
|
||||||
device_opts = []
|
device_opts = []
|
||||||
@@ -62,7 +110,7 @@ class TranscriberApp(ctk.CTk):
|
|||||||
# logger
|
# logger
|
||||||
self.ui_logger = setup_ui_logger(self.log_box)
|
self.ui_logger = setup_ui_logger(self.log_box)
|
||||||
|
|
||||||
# main transcription tab
|
### TRANSCRIPTION TAB
|
||||||
def _build_transcription_tab(self):
|
def _build_transcription_tab(self):
|
||||||
# file selectors
|
# file selectors
|
||||||
file_frame = ctk.CTkFrame(self.transcript_tab, corner_radius=10)
|
file_frame = ctk.CTkFrame(self.transcript_tab, corner_radius=10)
|
||||||
@@ -93,15 +141,33 @@ class TranscriberApp(ctk.CTk):
|
|||||||
ctk.CTkButton(ctrl_frame, text="Check Torch", command=self._check_torch).pack(
|
ctk.CTkButton(ctrl_frame, text="Check Torch", command=self._check_torch).pack(
|
||||||
side="left", padx=10, pady=5
|
side="left", padx=10, pady=5
|
||||||
)
|
)
|
||||||
self.start_btn = ctk.CTkButton(
|
self.start_button = ctk.CTkButton(
|
||||||
ctrl_frame, text="Start transcription", command=self._start_transcription
|
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"
|
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
|
# TODO: add unload model button here
|
||||||
|
|
||||||
@@ -114,65 +180,181 @@ class TranscriberApp(ctk.CTk):
|
|||||||
)
|
)
|
||||||
self.log_box.pack(padx=20, pady=10, expand=True, fill="both")
|
self.log_box.pack(padx=20, pady=10, expand=True, fill="both")
|
||||||
|
|
||||||
|
### SETTINGS TAB
|
||||||
def _build_settings_tab(self):
|
def _build_settings_tab(self):
|
||||||
# TODO: add tooltips here
|
|
||||||
pad = 20
|
pad = 20
|
||||||
|
|
||||||
### Model
|
def add_setting(parent, row, col, text, tooltip, variable, values: list | None):
|
||||||
ctk.CTkLabel(self.settings_tab, text="Model:").pack(
|
frame = ctk.CTkFrame(parent)
|
||||||
anchor="w", padx=pad, pady=(pad, 5)
|
frame.grid(row=row, column=col, padx=pad, pady=(pad, 5), sticky="nsew")
|
||||||
)
|
|
||||||
ctk.CTkOptionMenu(
|
label = ctk.CTkLabel(frame, text=text)
|
||||||
self.settings_tab,
|
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,
|
variable=self.model_var,
|
||||||
values=[
|
values=[
|
||||||
|
# maybe delete this option?
|
||||||
|
"openai/whisper-large-v3-turbo",
|
||||||
"openai/whisper-large-v2",
|
"openai/whisper-large-v2",
|
||||||
"openai/whisper-large",
|
"openai/whisper-large",
|
||||||
"openai/whisper-medium",
|
"openai/whisper-medium",
|
||||||
"openai/whisper-small",
|
"openai/whisper-small",
|
||||||
"openai/whisper-tiny",
|
"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(
|
### Batch size setting
|
||||||
self.settings_tab,
|
add_setting(
|
||||||
|
parent=grid,
|
||||||
|
row=0,
|
||||||
|
col=1,
|
||||||
|
text="Batch size:",
|
||||||
|
tooltip="Chunks count for one iteration",
|
||||||
variable=self.batch_var,
|
variable=self.batch_var,
|
||||||
values=["32", "16", "8", "4", "2"],
|
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,
|
variable=self.dtype_var,
|
||||||
values=["torch.float16", "torch.float32", "torch.bfloat16"],
|
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(
|
### Chunk Length setting
|
||||||
self.settings_tab,
|
add_setting(
|
||||||
|
parent=grid,
|
||||||
|
row=1,
|
||||||
|
col=1,
|
||||||
|
text="Chunk length (s):",
|
||||||
|
tooltip="Maximum length of processing audio fragment",
|
||||||
variable=self.chunk_var,
|
variable=self.chunk_var,
|
||||||
values=["30", "25", "20", "15", "10", "5"],
|
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,
|
variable=self.device_var,
|
||||||
values=self.device_opts,
|
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
|
# action buttons
|
||||||
def _browse_input(self):
|
def _browse_input(self):
|
||||||
@@ -183,7 +365,7 @@ class TranscriberApp(ctk.CTk):
|
|||||||
("Audio files", "*.wav *.mp3 *.m4a *.flac *.ogg"),
|
("Audio files", "*.wav *.mp3 *.m4a *.flac *.ogg"),
|
||||||
("Video files", "*.mp4 *.mkv *.avi"),
|
("Video files", "*.mp4 *.mkv *.avi"),
|
||||||
("All files", "*.*"),
|
("All files", "*.*"),
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
if path:
|
if path:
|
||||||
self.input_file_var.set(path)
|
self.input_file_var.set(path)
|
||||||
@@ -205,14 +387,34 @@ class TranscriberApp(ctk.CTk):
|
|||||||
def _check_torch(self):
|
def _check_torch(self):
|
||||||
check_torch(self.ui_logger)
|
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):
|
def _start_transcription(self):
|
||||||
infile = self.input_file_var.get().strip()
|
infile = self.input_file_var.get().strip()
|
||||||
if not infile or not os.path.isfile(infile):
|
if not infile or not os.path.isfile(infile):
|
||||||
messagebox.showerror("Error", "Please select a valid input file.")
|
messagebox.showerror("Error", "Please select a valid input file.")
|
||||||
return
|
return
|
||||||
|
|
||||||
self.start_btn.configure(state="disabled")
|
self.start_button.configure(state="disabled")
|
||||||
self.stop_btn.configure(state="normal")
|
self.stop_button.configure(state="normal")
|
||||||
self.ui_logger.info("Starting transcription...")
|
self.ui_logger.info("Starting transcription...")
|
||||||
|
|
||||||
self.stop_flag.clear()
|
self.stop_flag.clear()
|
||||||
@@ -221,10 +423,12 @@ class TranscriberApp(ctk.CTk):
|
|||||||
)
|
)
|
||||||
self.transcribe_thread.start()
|
self.transcribe_thread.start()
|
||||||
|
|
||||||
def _stop_transcription(self):
|
def _stop_transcription(self, Audio: AudioTranscription):
|
||||||
self.stop_flag.set()
|
self.stop_flag.set()
|
||||||
self.ui_logger.info("Stopping transcription...")
|
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):
|
def _transcribe_worker(self, infile: str):
|
||||||
try:
|
try:
|
||||||
@@ -239,20 +443,44 @@ class TranscriberApp(ctk.CTk):
|
|||||||
filepath=infile,
|
filepath=infile,
|
||||||
device_configuration=config,
|
device_configuration=config,
|
||||||
logger=self.ui_logger,
|
logger=self.ui_logger,
|
||||||
|
language=self.transcription_lang_var.get(),
|
||||||
)
|
)
|
||||||
transcription = Audio.transcribe_audio()
|
transcription = Audio.transcribe_audio()
|
||||||
|
|
||||||
outfile = self.output_file_var.get().strip()
|
outfile = self.output_file_var.get().strip()
|
||||||
if not outfile:
|
if not outfile:
|
||||||
outfile = infile + ".txt"
|
outfile = infile
|
||||||
|
|
||||||
with open(outfile, "w", encoding="utf-8") as f:
|
if not self.remove_transcription.get():
|
||||||
f.write(transcription)
|
outfile += ".txt"
|
||||||
|
|
||||||
|
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:
|
except Exception as e:
|
||||||
self.ui_logger.error(f"Error: {e}")
|
self.ui_logger.error(f"Error: {e}")
|
||||||
messagebox.showerror("Error", str(e))
|
messagebox.showerror("Error", str(e))
|
||||||
finally:
|
finally:
|
||||||
self.start_btn.configure(state="normal")
|
self.start_button.configure(state="normal")
|
||||||
self.stop_btn.configure(state="disabled")
|
self.stop_button.configure(state="disabled")
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"prompt": "Используй ТОЛЬКО текст из транскрибации. Любая информация, которой нет в транскрибации, запрещена. Если какой-то факт или определение раскрыт не до конца, или лектор сказал что-то неполно, или тему нужно дополнить для лучшего понимания — вставляй дополнение сразу в нужное место, но только под отдельной подписью: AI overview: текст дополнения (confidence: low|medium|high) Ответ должен быть строго в формате Markdown (.md). Никаких 'привет', 'вот результат' и т. п. — только готовый текст. Все цитаты из транскрибации — буквально, максимум 25 слов подряд."
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import openai
|
||||||
|
|
||||||
|
|
||||||
|
class LLMrequest:
|
||||||
|
def __init__(self, api_key: str, model_name: str, base_url: str = None):
|
||||||
|
if base_url:
|
||||||
|
self.client = openai.OpenAI(api_key=api_key, base_url=base_url)
|
||||||
|
else:
|
||||||
|
self.client = openai.OpenAI(api_key=api_key)
|
||||||
|
self.model = model_name
|
||||||
|
|
||||||
|
def get_response(self, prompt: str) -> str:
|
||||||
|
response = self.client.chat.completions.create(
|
||||||
|
model=self.model,
|
||||||
|
messages=[{"role": "user", "content": prompt}],
|
||||||
|
)
|
||||||
|
return response.choices[0].message.content
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
def save_to_file(
|
||||||
|
thing: str | dict,
|
||||||
|
path: str,
|
||||||
|
):
|
||||||
|
if isinstance(thing, dict):
|
||||||
|
pass
|
||||||
|
elif isinstance(thing, str):
|
||||||
|
with open(path, "w") as outputfile:
|
||||||
|
outputfile.write(thing)
|
||||||
Reference in New Issue
Block a user