Files

104 lines
3.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package processor
import (
"context"
"errors"
"fmt"
"log/slog"
"os"
"gitea.mrixs.me/Mrixs/yamusic-bot/internal/interfaces"
"gitea.mrixs.me/Mrixs/yamusic-bot/internal/model"
"gitea.mrixs.me/Mrixs/yamusic-bot/internal/storage"
)
// TrackProcessor инкапсулирует всю логику обработки одного трека.
type TrackProcessor struct {
storage interfaces.TrackStorage
yandex interfaces.YandexMusicClient
downloader interfaces.FileDownloader
tagger interfaces.Tagger
telegram interfaces.TelegramClient
}
// NewTrackProcessor создает новый процессор.
func NewTrackProcessor(
storage interfaces.TrackStorage,
yandex interfaces.YandexMusicClient,
downloader interfaces.FileDownloader,
tagger interfaces.Tagger,
telegram interfaces.TelegramClient,
) *TrackProcessor {
return &TrackProcessor{
storage: storage,
yandex: yandex,
downloader: downloader,
tagger: tagger,
telegram: telegram,
}
}
// Process получает информацию о треке, обрабатывает его (если нужно) и возвращает Telegram File ID.
func (p *TrackProcessor) Process(ctx context.Context, trackInfo *model.TrackInfo) (string, error) {
const op = "processor.TrackProcessor.Process"
// 1. Проверяем кэш в БД
fileID, err := p.storage.Get(ctx, trackInfo.YandexTrackID)
if err == nil {
slog.Info("Cache hit", "track_id", trackInfo.YandexTrackID, "title", trackInfo.Title)
return fileID, nil
}
// Если ошибка - это не "не найдено", то это проблема
if !errors.Is(err, storage.ErrNotFound) {
return "", fmt.Errorf("%s: failed to get from storage: %w", op, err)
}
// 2. Cache Miss: начинаем полный цикл обработки
slog.Info("Cache miss, processing track", "track_id", trackInfo.YandexTrackID, "title", trackInfo.Title)
// 2a. Получаем URL для скачивания
downloadURL, err := p.yandex.GetDownloadURL(ctx, trackInfo.YandexTrackID)
if err != nil {
return "", fmt.Errorf("%s: failed to get download url: %w", op, err)
}
trackInfo.DownloadURL = downloadURL
// 2b. Скачиваем аудиофайл и обложку
audioPath, err := p.downloader.Download(ctx, trackInfo.DownloadURL)
if err != nil {
return "", fmt.Errorf("%s: failed to download audio: %w", op, err)
}
defer os.Remove(audioPath) // Гарантированное удаление временного аудиофайла
var coverPath string
if trackInfo.CoverURL != "" {
coverPath, err = p.downloader.Download(ctx, trackInfo.CoverURL)
if err != nil {
slog.Warn("Failed to download cover, proceeding without it", "url", trackInfo.CoverURL, "error", err)
} else {
defer os.Remove(coverPath) // Гарантированное удаление временной обложки
}
}
// 2c. Записываем теги
if err := p.tagger.WriteTags(audioPath, coverPath, trackInfo); err != nil {
// Не фатальная ошибка, просто логируем и продолжаем
slog.Warn("Failed to write tags, proceeding without them", "track_id", trackInfo.YandexTrackID, "error", err)
}
// 2d. Загружаем в Telegram (в кэш-канал)
newFileID, err := p.telegram.SendAudioToCacheChannel(ctx, audioPath, trackInfo.Title, trackInfo.Artist)
if err != nil {
return "", fmt.Errorf("%s: failed to upload to telegram: %w", op, err)
}
// 2e. Сохраняем в нашу БД
if err := p.storage.Set(ctx, trackInfo.YandexTrackID, newFileID); err != nil {
// Не фатальная ошибка для пользователя, но критичная для нас. Логируем как ошибку.
slog.Error("Failed to save track to cache storage", "track_id", trackInfo.YandexTrackID, "error", err)
}
return newFileID, nil
}