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 }