veza/veza-backend-api/internal/services/metadata_service.go
2025-12-03 20:29:37 +01:00

112 lines
2.6 KiB
Go

package services
import (
"fmt"
"os"
"path/filepath"
"github.com/dhowden/tag"
"go.uber.org/zap"
)
// AudioMetadata represents extracted audio metadata
type AudioMetadata struct {
Title string
Artist string
Album string
Genre string
Year int
Track int
Duration float64 // in seconds
Bitrate int
Format string
}
// MetadataService extracts metadata from audio files
type MetadataService struct {
logger *zap.Logger
}
// NewMetadataService creates a new metadata service
func NewMetadataService(logger *zap.Logger) *MetadataService {
return &MetadataService{
logger: logger,
}
}
// ExtractMetadata extracts metadata from an audio file
func (ms *MetadataService) ExtractMetadata(filePath string) (*AudioMetadata, error) {
// Open file
file, err := os.Open(filePath)
if err != nil {
return nil, fmt.Errorf("failed to open file for metadata extraction: %w", err)
}
defer file.Close()
// Read metadata from file
metadata, err := tag.ReadFrom(file)
if err != nil {
// If metadata can't be read, return default metadata
ms.logger.Warn("Failed to extract metadata, using defaults",
zap.Error(err),
zap.String("file_path", filePath),
)
return ms.getDefaultMetadata(filePath), nil
}
// Extract metadata
trackNum, _ := metadata.Track()
result := &AudioMetadata{
Title: metadata.Title(),
Artist: metadata.Artist(),
Album: metadata.Album(),
Genre: metadata.Genre(),
Year: metadata.Year(),
Track: trackNum,
Format: filepath.Ext(filePath),
}
// Duration and bitrate would typically be extracted using ffprobe or similar
// For now, we'll leave these as 0
return result, nil
}
// getDefaultMetadata returns default metadata based on filename
func (ms *MetadataService) getDefaultMetadata(filePath string) *AudioMetadata {
filename := filepath.Base(filePath)
ext := filepath.Ext(filename)
baseName := filename[:len(filename)-len(ext)]
// Try to parse "Artist - Title" pattern
var artist, title string
if len(baseName) > 3 && baseName[2:4] == " - " {
parts := baseName[:3] // This won't work, need proper parsing
artist = string(parts)
title = baseName[3:]
} else {
title = baseName
artist = "Unknown"
}
return &AudioMetadata{
Title: title,
Artist: artist,
Album: "",
Genre: "",
Year: 0,
Track: 0,
Format: ext,
}
}
// ValidateMetadata validates extracted metadata
func (ms *MetadataService) ValidateMetadata(metadata *AudioMetadata) error {
if metadata.Title == "" {
return fmt.Errorf("title is required")
}
if metadata.Artist == "" {
return fmt.Errorf("artist is required")
}
return nil
}