From 4b7146afe4da40a08100ee89be00af7947b7a071 Mon Sep 17 00:00:00 2001 From: zarzet Date: Sun, 10 May 2026 18:31:19 +0700 Subject: [PATCH] fix: report zero bit depth for non-ALAC M4A containers GetM4AQuality previously defaulted to 16-bit whenever the audio sample entry was not ALAC, which silently labeled lossy AAC downloads as CD quality in the library and in extension APIs. Only fill BitDepth when the atom is ALAC (including the ALACSpecificConfig refinement), and leave it as zero for AAC/mp4a, matching how the MP3 and Opus probes already report lossy sources. Tests cover both the ALAC and AAC branches. --- go_backend/audio_metadata_supplement_test.go | 19 ++++++++++++++++--- go_backend/metadata.go | 7 ++----- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/go_backend/audio_metadata_supplement_test.go b/go_backend/audio_metadata_supplement_test.go index 6dbf999e..4348344e 100644 --- a/go_backend/audio_metadata_supplement_test.go +++ b/go_backend/audio_metadata_supplement_test.go @@ -308,16 +308,20 @@ func TestM4AMetadataAtomHelpers(t *testing.T) { t.Fatalf("ReplayGain fields = %#v", fields) } - qualityPath := filepath.Join(dir, "quality.m4a") + qualityPath := filepath.Join(dir, "quality-alac.m4a") mvhd := make([]byte, 20) binary.BigEndian.PutUint32(mvhd[12:16], 1000) binary.BigEndian.PutUint32(mvhd[16:20], 180000) sampleEntry := make([]byte, 32) - copy(sampleEntry[0:4], "mp4a") + copy(sampleEntry[0:4], "alac") binary.BigEndian.PutUint16(sampleEntry[22:24], 24) sampleEntry[28] = 0xAC sampleEntry[29] = 0x44 - qualityFile := append(buildM4AAtom("ftyp", []byte("M4A \x00\x00\x00\x00")), buildM4AAtom("moov", append(buildM4AAtom("mvhd", mvhd), sampleEntry...))...) + alacConfig := make([]byte, 24) + alacConfig[5] = 24 + binary.BigEndian.PutUint32(alacConfig[20:24], 44100) + alacEntryPayload := append(append([]byte{}, sampleEntry[4:]...), buildM4AAtom("alac", alacConfig)...) + qualityFile := append(buildM4AAtom("ftyp", []byte("M4A \x00\x00\x00\x00")), buildM4AAtom("moov", append(buildM4AAtom("mvhd", mvhd), buildM4AAtom("alac", alacEntryPayload)...))...) if err := os.WriteFile(qualityPath, qualityFile, 0600); err != nil { t.Fatal(err) } @@ -327,6 +331,15 @@ func TestM4AMetadataAtomHelpers(t *testing.T) { if quality, err := GetAudioQuality(qualityPath); err != nil || quality.SampleRate != 44100 { t.Fatalf("GetAudioQuality M4A = %#v/%v", quality, err) } + aacQualityPath := filepath.Join(dir, "quality-aac.m4a") + copy(sampleEntry[0:4], "mp4a") + aacQualityFile := append(buildM4AAtom("ftyp", []byte("M4A \x00\x00\x00\x00")), buildM4AAtom("moov", append(buildM4AAtom("mvhd", mvhd), sampleEntry...))...) + if err := os.WriteFile(aacQualityPath, aacQualityFile, 0600); err != nil { + t.Fatal(err) + } + if quality, err := GetM4AQuality(aacQualityPath); err != nil || quality.BitDepth != 0 || quality.SampleRate != 44100 || quality.Duration != 180 { + t.Fatalf("GetM4AQuality AAC = %#v/%v", quality, err) + } if _, _, ok := parseALACSpecificConfig(make([]byte, 4)); ok { t.Fatal("short ALAC config should not parse") } diff --git a/go_backend/metadata.go b/go_backend/metadata.go index cfa8432e..3db29102 100644 --- a/go_backend/metadata.go +++ b/go_backend/metadata.go @@ -1695,9 +1695,10 @@ func GetM4AQuality(filePath string) (AudioQuality, error) { // [26:28] reserved // [28:32] samplerate (16.16 fixed-point) sampleRate := int(buf[28])<<8 | int(buf[29]) - bitDepth := int(buf[22])<<8 | int(buf[23]) + bitDepth := 0 if atomType == "alac" { + bitDepth = int(buf[22])<<8 | int(buf[23]) if alacBitDepth, alacSampleRate, ok := readALACSpecificConfig(f, sampleOffset, fileSize); ok { if alacBitDepth > 0 { bitDepth = alacBitDepth @@ -1708,10 +1709,6 @@ func GetM4AQuality(filePath string) (AudioQuality, error) { } } - if bitDepth <= 0 { - bitDepth = 16 - } - return AudioQuality{BitDepth: bitDepth, SampleRate: sampleRate, Duration: duration}, nil }