// 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)