From 4cf885a52e6243823bb94ac47bfec3b56cf9c483 Mon Sep 17 00:00:00 2001 From: zarzet Date: Sun, 22 Mar 2026 23:00:55 +0700 Subject: [PATCH] feat: populate M4A metadata in ReadFileMetadata and library scan ReadFileMetadata now fills all tag fields (title, artist, album, ISRC, lyrics, genre, label, copyright, composer, comment, track/disc number) for M4A files using the new ReadM4ATags helper, matching the existing behavior for FLAC, MP3, and Ogg. scanM4AFile reads tags via ReadM4ATags instead of falling back to the filename, and applies applyDefaultLibraryMetadata for missing fields (consistent with FLAC/MP3 scan path). Remove the '&& ext != ".m4a"' guard in cover cache so M4A cover art is extracted and cached during library scans. --- go_backend/exports.go | 30 ++++++++++++++++++++++++++++-- go_backend/library_scan.go | 21 +++++++++++++++++++-- 2 files changed, 47 insertions(+), 4 deletions(-) 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) {