isort & black

- trying to fix environment issues
This commit is contained in:
2025-09-14 03:07:06 +04:00
parent 7cf4c5259b
commit 7291b148e5
11 changed files with 177 additions and 99 deletions
+19
View File
@@ -0,0 +1,19 @@
name: notecast
channels:
- pytorch
- conda-forge
- defaults
dependencies:
- python=3.10
- pytorch::pytorch=2.0.1
- pytorch::torchvision=0.15.2
- pytorch::torchaudio=2.0.2
- transformers=4.30.0
- accelerate=0.20.0
- ffmpeg
- pyinstaller
- customtkinter
- isort
- mypy
- black
- tqdm
+21
View File
@@ -0,0 +1,21 @@
name: notecast
channels:
- pytorch
- nvidia
- conda-forge
- defaults
dependencies:
- python=3.10
- pytorch::pytorch=2.0.1
- pytorch::torchvision=0.15.2
- pytorch::torchaudio=2.0.2
- pytorch::cudatoolkit=11.8
- transformers=4.30.0
- accelerate=0.20.0
- ffmpeg
- pyinstaller
- customtkinter
- isort
- mypy
- black
- tqdm
+19
View File
@@ -0,0 +1,19 @@
name: notecast
channels:
- pytorch
- conda-forge
- defaults
dependencies:
- python=3.10
- pytorch::pytorch=2.0.1
- pytorch::torchvision=0.15.2
- pytorch::torchaudio=2.0.2
- transformers=4.30.0
- accelerate=0.20.0
- ffmpeg
- pyinstaller
- customtkinter
- isort
- mypy
- black
- tqdm
-16
View File
@@ -1,16 +0,0 @@
name: notecast
channels:
- defaults
- conda-forge
dependencies:
- torchvision
- pytorch
- torchaudio
- transformers
- accelerate
- ffmpeg
- pyinstaller
- customtkinter
- isort
- mypy
- black
+31 -18
View File
@@ -1,12 +1,15 @@
from transformers import WhisperProcessor, WhisperForConditionalGeneration import logging
import math
import time
import torch import torch
import torchaudio import torchaudio
from ui.ui_log_handler import UILogHandler
from transcription.device_configuration import DeviceConfiguration
import logging
import time
import math
from tqdm import tqdm from tqdm import tqdm
from transformers import WhisperForConditionalGeneration, WhisperProcessor
from transcription.device_configuration import DeviceConfiguration
from ui.ui_log_handler import UILogHandler
# TODO: implement transcription with shift # TODO: implement transcription with shift
class AudioTranscription: class AudioTranscription:
@@ -37,7 +40,7 @@ class AudioTranscription:
filepath: str, filepath: str,
device_configuration: DeviceConfiguration, device_configuration: DeviceConfiguration,
logger: logging.Logger, logger: logging.Logger,
language = "ru" language="ru",
) -> None: ) -> None:
# TODO: add pretty docs here # TODO: add pretty docs here
self.filepath = filepath self.filepath = filepath
@@ -59,13 +62,14 @@ class AudioTranscription:
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, self.model_name, torch_dtype=self.torch_dtype
torch_dtype=self.torch_dtype
).to(self.device) ).to(self.device)
logger.info("Model loaded.") logger.info("Model loaded.")
self.waveform, self.sampling_rate = torchaudio.load(filepath, format="mp3", backend="ffmpeg") self.waveform, self.sampling_rate = torchaudio.load(
filepath, format="mp3", backend="ffmpeg"
)
logger.info(f"Successfully loaded file {filepath}.") logger.info(f"Successfully loaded file {filepath}.")
except Exception as e: except Exception as e:
@@ -73,7 +77,9 @@ class AudioTranscription:
raise raise
def _resample(self) -> None: def _resample(self) -> None:
self.waveform = torchaudio.functional.resample(self.waveform, self.sampling_rate, 16000) self.waveform = torchaudio.functional.resample(
self.waveform, self.sampling_rate, 16000
)
def _to_mono(self): def _to_mono(self):
if self.waveform.shape[0] > 1: if self.waveform.shape[0] > 1:
@@ -87,7 +93,9 @@ class AudioTranscription:
total_samples = self.waveform.shape[0] total_samples = self.waveform.shape[0]
chunks_count = (total_samples + self.chunk_size - 1) // self.chunk_size chunks_count = (total_samples + self.chunk_size - 1) // self.chunk_size
self.logger.info(f"File length - {total_samples / 16000:.1f} seconds, splitting on {chunks_count} chunks by {chunk_length_s} seconds per chunk.") self.logger.info(
f"File length - {total_samples / 16000:.1f} seconds, splitting on {chunks_count} chunks by {chunk_length_s} seconds per chunk."
)
self.chunks = [] self.chunks = []
for idx in tqdm(range(chunks_count)): for idx in tqdm(range(chunks_count)):
@@ -113,28 +121,33 @@ class AudioTranscription:
self.batches[idx], self.batches[idx],
sampling_rate=16000, sampling_rate=16000,
return_tensors="pt", return_tensors="pt",
padding=True padding=True,
) )
input_features = inputs.input_features.to(self.device).to(self.torch_dtype) input_features = inputs.input_features.to(self.device).to(
self.torch_dtype
)
with torch.no_grad(): with torch.no_grad():
predicted_ids = self.model.generate( predicted_ids = self.model.generate(
input_features, input_features,
language=self.language, language=self.language,
task="transcribe", task="transcribe",
temperature=0.0 temperature=0.0,
) )
texts = self.processor.batch_decode(predicted_ids, skip_special_tokens=True) texts = self.processor.batch_decode(
predicted_ids, skip_special_tokens=True
)
self.all_transcription.extend(texts) self.all_transcription.extend(texts)
end_time = time.time() end_time = time.time()
self.logger.info(f"Transcription completed in {end_time - start_time:.2f} seconds") self.logger.info(
f"Transcription completed in {end_time - start_time:.2f} seconds"
)
except Exception as e: except Exception as e:
self.logger.error(f"Errors occured while processing chunks: {e}") self.logger.error(f"Errors occured while processing chunks: {e}")
def transcribe_audio(self) -> str: def transcribe_audio(self) -> str:
# TODO: maybe something else, not str? # TODO: maybe something else, not str?
self._resample() self._resample()
+4 -1
View File
@@ -1,6 +1,8 @@
from dataclasses import dataclass from dataclasses import dataclass
import torch import torch
@dataclass @dataclass
class DeviceConfiguration: class DeviceConfiguration:
""" """
@@ -25,6 +27,7 @@ class DeviceConfiguration:
- torch.float32 - for CPU / weak GPU - torch.float32 - for CPU / weak GPU
- torch.bfloat16 - for GPUs which has BF16 support - torch.bfloat16 - for GPUs which has BF16 support
""" """
device: str = "cuda" device: str = "cuda"
model_name: str = "openai/whisper-large-v2" model_name: str = "openai/whisper-large-v2"
batch_size: int = 16 batch_size: int = 16
@@ -34,7 +37,7 @@ class DeviceConfiguration:
_dtype_map = { _dtype_map = {
"torch.float16": torch.float16, "torch.float16": torch.float16,
"torch.float32": torch.float32, "torch.float32": torch.float32,
"torch.bfloat16": torch.bfloat16 "torch.bfloat16": torch.bfloat16,
} }
torch_dtype: torch.dtype = None torch_dtype: torch.dtype = None
+13 -7
View File
@@ -1,31 +1,37 @@
import torch
from dataclasses import dataclass
import logging import logging
from dataclasses import dataclass
import torch
def check_torch(logger: logging.Logger) -> None: def check_torch(logger: logging.Logger) -> None:
logger.info("=== Checking PyTorch ===") logger.info("=== Checking PyTorch ===")
logger.info(f"Torch version: {torch.__version__}") logger.info(f"Torch version: {torch.__version__}")
# === NVIDIA / AMD (CUDA API) === # NVIDIA / AMD (CUDA API)
if torch.cuda.is_available(): if torch.cuda.is_available():
backend = "CUDA" backend = "CUDA"
if torch.version.hip is not None: if torch.version.hip is not None:
backend = "ROCm (AMD HIP)" backend = "ROCm (AMD HIP)"
logger.info(f"{backend} backend is available") logger.info(f"{backend} backend is available")
logger.info(f"Compiled with: CUDA {torch.version.cuda}, ROCm {torch.version.hip}") logger.info(
f"Compiled with: CUDA {torch.version.cuda}, ROCm {torch.version.hip}"
)
logger.info(f"Number of devices: {torch.cuda.device_count()}") logger.info(f"Number of devices: {torch.cuda.device_count()}")
for i in range(torch.cuda.device_count()): for i in range(torch.cuda.device_count()):
logger.info(f"GPU {i}: {torch.cuda.get_device_name(i)}") logger.info(f"GPU {i}: {torch.cuda.get_device_name(i)}")
# === Apple Silicon (MPS) === # Apple Silicon (MPS)
elif hasattr(torch.backends, "mps") and torch.backends.mps.is_available(): elif hasattr(torch.backends, "mps") and torch.backends.mps.is_available():
logger.info("MPS backend is available (Apple Silicon)") logger.info("MPS backend is available (Apple Silicon)")
logger.info(f"MPS version: {getattr(torch.backends.mps, '__version__', 'unknown')}") logger.info(
f"MPS version: {getattr(torch.backends.mps, '__version__', 'unknown')}"
)
logger.info("GPU: Apple Silicon (Metal)") logger.info("GPU: Apple Silicon (Metal)")
# === CPU only mode === # CPU only mode
else: else:
logger.info("Only CPU is available") logger.info("Only CPU is available")
+10 -1
View File
@@ -1,9 +1,18 @@
from transcription.device_configuration import DeviceConfiguration from transcription.device_configuration import DeviceConfiguration
# TODO: implement saving & removing configuration # TODO: implement saving & removing configuration
def save_configuration(cfg: DeviceConfiguration): def save_configuration(cfg: DeviceConfiguration):
config = { config = {
"Model": cfg.model_name, "Model": cfg.model_name,
"Batch Size": cfg.batch_size, "Batch Size": cfg.batch_size,
"Data Type": cfg.data_type "Data Type": cfg.data_type,
} }
def load_config():
pass
def delete_config():
pass
+10 -8
View File
@@ -1,17 +1,16 @@
import os import os
import threading
import queue import queue
import threading
import tkinter as tk import tkinter as tk
from tkinter import scrolledtext, filedialog, messagebox from tkinter import filedialog, messagebox, scrolledtext
import customtkinter as ctk import customtkinter as ctk
import torch import torch
from ui.ui_log_handler import setup_ui_logger
from transcription.torch_checker import check_torch
from transcription.device_configuration import DeviceConfiguration
from transcription.audio_transcription import AudioTranscription 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
WINDOW_WIDTH = 900 WINDOW_WIDTH = 900
WINDOW_HEIGHT = 650 WINDOW_HEIGHT = 650
@@ -175,7 +174,10 @@ class TranscriberApp(ctk.CTk):
def _browse_input(self): def _browse_input(self):
path = filedialog.askopenfilename( path = filedialog.askopenfilename(
title="Select input audio file", title="Select input audio file",
filetypes=[("Audio files", "*.wav *.mp3 *.m4a *.flac"), ("All files", "*.*")] filetypes=[
("Audio files", "*.wav *.mp3 *.m4a *.flac"),
("All files", "*.*"),
],
) )
if path: if path:
self.input_file_var.set(path) self.input_file_var.set(path)
@@ -184,7 +186,7 @@ class TranscriberApp(ctk.CTk):
path = filedialog.asksaveasfilename( path = filedialog.asksaveasfilename(
title="Select output file", title="Select output file",
defaultextension=".txt", defaultextension=".txt",
filetypes=[("Text files", "*.txt"), ("All files", "*.*")] filetypes=[("Text files", "*.txt"), ("All files", "*.*")],
) )
if path: if path:
self.output_file_var.set(path) self.output_file_var.set(path)
+3 -1
View File
@@ -1,5 +1,6 @@
import tkinter as tk
import logging import logging
import tkinter as tk
class UILogHandler(logging.Handler): class UILogHandler(logging.Handler):
def __init__(self, text_widget: tk.Text): def __init__(self, text_widget: tk.Text):
@@ -11,6 +12,7 @@ class UILogHandler(logging.Handler):
self.text_widget.insert(tk.END, log_entry + "\n") self.text_widget.insert(tk.END, log_entry + "\n")
self.text_widget.see(tk.END) self.text_widget.see(tk.END)
# TODO: maybe some tqdm here, not in console? # TODO: maybe some tqdm here, not in console?
def setup_ui_logger(text_widget: tk.Text, level=logging.INFO): def setup_ui_logger(text_widget: tk.Text, level=logging.INFO):
logger = logging.getLogger("UI_LOGGER") logger = logging.getLogger("UI_LOGGER")