diff --git a/go_backend/exports.go b/go_backend/exports.go index e65b0197..d0a86ccc 100644 --- a/go_backend/exports.go +++ b/go_backend/exports.go @@ -739,6 +739,26 @@ func ReadFileMetadata(filePath string) (string, error) { } } } else if isM4A { + meta, err := ReadM4ATags(filePath) + if err == nil && meta != nil { + result["title"] = meta.Title + result["artist"] = meta.Artist + result["album"] = meta.Album + result["album_artist"] = meta.AlbumArtist + result["date"] = meta.Date + if meta.Date == "" { + result["date"] = meta.Year + } + result["track_number"] = meta.TrackNumber + result["disc_number"] = meta.DiscNumber + result["isrc"] = meta.ISRC + result["lyrics"] = meta.Lyrics + result["genre"] = meta.Genre + result["label"] = meta.Label + result["copyright"] = meta.Copyright + result["composer"] = meta.Composer + result["comment"] = meta.Comment + } quality, qualityErr := GetM4AQuality(filePath) if qualityErr == nil { result["bit_depth"] = quality.BitDepth @@ -1960,8 +1980,15 @@ func ReEnrichFile(requestJSON string) (string, error) { } }() - // Fetch lyrics + // Preserve existing lyrics when online enrichment does not return a replacement. var lyricsLRC string + existingLyrics, existingLyricsErr := ExtractLyrics(req.FilePath) + if existingLyricsErr == nil && strings.TrimSpace(existingLyrics) != "" { + lyricsLRC = existingLyrics + GoLog("[ReEnrich] Preserving existing embedded/sidecar lyrics\n") + } + + // Fetch lyrics if req.EmbedLyrics { client := NewLyricsClient() durationSec := float64(req.DurationMs) / 1000.0 @@ -2042,7 +2069,6 @@ func ReEnrichFile(requestJSON string) (string, error) { return string(jsonBytes), nil } - // MP3/Opus: return metadata map for Dart to use FFmpeg // Don't cleanup cover temp — Dart needs it for FFmpeg embed cleanupCover = false result := map[string]interface{}{ diff --git a/go_backend/library_scan.go b/go_backend/library_scan.go index 20cfd626..ee8874d1 100644 --- a/go_backend/library_scan.go +++ b/go_backend/library_scan.go @@ -293,7 +293,7 @@ func scanAudioFileWithKnownModTimeAndDisplayName(filePath, displayNameHint, scan libraryCoverCacheMu.RLock() coverCacheDir := libraryCoverCacheDir libraryCoverCacheMu.RUnlock() - if coverCacheDir != "" && ext != ".m4a" { + if coverCacheDir != "" { coverPath, err := SaveCoverToCacheWithHint(filePath, displayNameHint, coverCacheDir) if err == nil && coverPath != "" { result.CoverPath = coverPath @@ -373,13 +373,30 @@ func scanFLACFile(filePath string, result *LibraryScanResult) (*LibraryScanResul } func scanM4AFile(filePath string, result *LibraryScanResult) (*LibraryScanResult, error) { + metadata, err := ReadM4ATags(filePath) + if err == nil && metadata != nil { + result.TrackName = metadata.Title + result.ArtistName = metadata.Artist + result.AlbumName = metadata.Album + result.AlbumArtist = metadata.AlbumArtist + result.ISRC = metadata.ISRC + result.TrackNumber = metadata.TrackNumber + result.DiscNumber = metadata.DiscNumber + result.ReleaseDate = metadata.Date + if result.ReleaseDate == "" { + result.ReleaseDate = metadata.Year + } + result.Genre = metadata.Genre + } + quality, err := GetM4AQuality(filePath) if err == nil { result.BitDepth = quality.BitDepth result.SampleRate = quality.SampleRate } - return scanFromFilename(filePath, "", result) + applyDefaultLibraryMetadata(filePath, "", result) + return result, nil } func scanMP3File(filePath string, result *LibraryScanResult) (*LibraryScanResult, error) {