Функция: реализованы консольный логгер и презентер для обработки аудиокниг

- Добавлен ConsoleLogger для подробного логирования этапов обработки аудиокниг в консоли.

- Введен ConsolePresenter для форматированного вывода результатов сканирования в консоль.

- Создан ProcessAudioBooksUseCase для обработки полного конвейера обработки аудиокниг, включая сканирование папок, извлечение метаданных, поиск торрентов и запись результатов.

- Реализована проверка LLM для улучшения метаданных.

- Добавлена ​​обработка ошибок и логирование на всех этапах обработки.
This commit is contained in:
Dmitriy Fofanov
2026-02-20 00:35:43 +03:00
parent 7d119927a1
commit 402ce7f4f1
26 changed files with 4323 additions and 0 deletions
+152
View File
@@ -0,0 +1,152 @@
// Package presentation реализует порт ProcessLogger —
// детальное логирование процесса обработки аудиокниг в консоль.
package presentation
import (
"fmt"
"os"
"sync"
"github.com/fofanov/genaudiobookinfo/internal/domain"
"github.com/schollz/progressbar/v3"
)
// ConsoleLogger реализует domain.ProcessLogger для вывода логов в консоль.
type ConsoleLogger struct {
mu sync.Mutex // защита от гонок при параллельном логировании
bar *progressbar.ProgressBar
}
// NewConsoleLogger создаёт новый экземпляр логгера.
func NewConsoleLogger() *ConsoleLogger {
return &ConsoleLogger{}
}
// NewConsoleLoggerWithProgress создаёт логгер с progress bar.
func NewConsoleLoggerWithProgress(total int) *ConsoleLogger {
bar := progressbar.NewOptions(total,
progressbar.OptionSetDescription("Обработка аудиокниг"),
progressbar.OptionSetWriter(os.Stderr),
progressbar.OptionSetWidth(40),
progressbar.OptionShowCount(),
progressbar.OptionShowIts(),
progressbar.OptionSetPredictTime(true),
progressbar.OptionSetTheme(progressbar.Theme{
Saucer: "=",
SaucerHead: ">",
SaucerPadding: " ",
BarStart: "[",
BarEnd: "]",
}),
progressbar.OptionOnCompletion(func() {
fmt.Fprintln(os.Stderr)
}),
)
return &ConsoleLogger{
bar: bar,
}
}
// LogStart логирует начало обработки книги.
func (l *ConsoleLogger) LogStart(folderName string) {
l.mu.Lock()
defer l.mu.Unlock()
if l.bar == nil {
fmt.Printf("\n📁 Обработка: %s\n", folderName)
}
// С progressbar не меняем описание — с N воркерами оно мерцает
}
// LogExtraction логирует этап извлечения метаданных.
func (l *ConsoleLogger) LogExtraction() {
l.mu.Lock()
defer l.mu.Unlock()
if l.bar == nil {
fmt.Println(" 🎵 Извлечение метаданных...")
}
}
// LogLLMValidation логирует этап валидации через LLM.
func (l *ConsoleLogger) LogLLMValidation() {
l.mu.Lock()
defer l.mu.Unlock()
if l.bar == nil {
fmt.Println(" 🤖 Валидация через LLM...")
}
}
// LogSearch логирует этап поиска на трекерах.
func (l *ConsoleLogger) LogSearch() {
l.mu.Lock()
defer l.mu.Unlock()
if l.bar == nil {
fmt.Println(" 🔍 Поиск на трекерах...")
}
}
// LogWrite логирует этап записи результата.
func (l *ConsoleLogger) LogWrite() {
l.mu.Lock()
defer l.mu.Unlock()
if l.bar == nil {
fmt.Println(" 💾 Запись результата...")
}
}
// LogCoverDownload логирует этап скачивания обложки.
func (l *ConsoleLogger) LogCoverDownload() {
l.mu.Lock()
defer l.mu.Unlock()
if l.bar == nil {
fmt.Println(" 🖼️ Скачивание обложки...")
}
}
// LogComplete логирует успешное завершение обработки книги.
func (l *ConsoleLogger) LogComplete(folderName string) {
l.mu.Lock()
defer l.mu.Unlock()
if l.bar != nil {
l.bar.Add(1)
} else {
fmt.Printf(" ✅ Завершено: %s\n", folderName)
}
}
// LogError логирует ошибку обработки.
func (l *ConsoleLogger) LogError(folderName string, err error) {
l.mu.Lock()
defer l.mu.Unlock()
if l.bar != nil {
l.bar.Add(1)
// Пишем ошибку в stderr (туда же, куда и progressbar), чтобы не ломать вывод
fmt.Fprintf(os.Stderr, "\n ❌ Ошибка [%s]: %v\n", folderName, err)
} else {
fmt.Printf(" ❌ Ошибка [%s]: %v\n", folderName, err)
}
}
// LogWarning логирует предупреждение.
func (l *ConsoleLogger) LogWarning(message string) {
l.mu.Lock()
defer l.mu.Unlock()
if l.bar != nil {
// Выводим предупреждения в stderr, чтобы они не терялись
fmt.Fprintf(os.Stderr, "\n ⚠️ %s\n", message)
} else {
fmt.Printf(" ⚠️ %s\n", message)
}
}
// Finish завершает работу progressbar (если есть).
func (l *ConsoleLogger) Finish() {
l.mu.Lock()
defer l.mu.Unlock()
if l.bar != nil {
l.bar.Finish()
}
}
// Проверка во время компиляции, что ConsoleLogger реализует domain.ProcessLogger
var _ domain.ProcessLogger = (*ConsoleLogger)(nil)