feat(uploads): match release groups with MusicBrainz and AcoustID
ci / module boundaries / boundaries (push) Successful in 10s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m30s
ci / api / api (push) Successful in 58s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m1s
ci / db / db (push) Failing after 25s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 1m36s
ci / ts / ts (packages + web) (push) Failing after 6m3s

This commit is contained in:
mrleo1nid committed 2026-10-29 20:44:59 +03:00
1 parent 1e3a149aef
commit 39bf7d4b70
45 files changed
+4792 -417

No files matched your search

@@ -79,6 +79,37 @@ type FPCalc struct {
Timeout time.Duration
}
// EncodedFingerprint returns compressed Chromaprint for AcoustID. Use ffprobe for full duration.
func (f FPCalc) EncodedFingerprint(ctx context.Context, absPath string) (string, error) {
bin := f.Bin
if bin == "" {
bin = "fpcalc"
}
timeout := f.Timeout
if timeout <= 0 {
timeout = time.Minute
}
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
out, err := exec.CommandContext(ctx, bin, "-json", "-length", fmt.Sprint(FingerprintSeconds), "--", absPath).Output()
if err != nil {
if errors.Is(err, exec.ErrNotFound) {
return "", ErrNoFPCalc
}
return "", fmt.Errorf("fpcalc: encoded fingerprint: %w", err)
}
var result struct {
Fingerprint string `json:"fingerprint"`
}
if err = json.Unmarshal(out, &result); err != nil {
return "", err
}
if len(result.Fingerprint) == 0 || len(result.Fingerprint) > 65536 || strings.ContainsAny(result.Fingerprint, " \t\r\n") {
return "", errors.New("fpcalc: invalid encoded fingerprint")
}
return result.Fingerprint, nil
}
// ErrNoFPCalc — fpcalc не установлен: отпечатки Chromaprint не считаются.
var ErrNoFPCalc = errors.New("library: fpcalc not found")
+2
View File
@@ -34,6 +34,7 @@ type Probe struct {
Channels int
DurationMs int
Tags meta.Fields
RawTags map[string]string
}
// Lossless сообщает, без потерь ли кодек.
@@ -160,6 +161,7 @@ func parseFFprobe(raw []byte, name string) (Probe, error) {
Track: first("track", "tracknumber"),
Disc: first("disc", "discnumber"),
}
p.RawTags = tags
return p, nil
}
@@ -0,0 +1,15 @@
package library
import (
"github.com/stretchr/testify/require"
"testing"
)
func TestProbeKeepsMusicBrainzIdentifiersForUploadLookup(t *testing.T) {
raw := []byte(`{"streams":[{"codec_type":"audio","codec_name":"flac","sample_rate":"44100","duration":"180","tags":{"MusicBrainz Track Id":"recording-id"}}],"format":{"tags":{"ARTIST":"Artist","ALBUM":"Album","TITLE":"Song","MusicBrainz Album Id":"release-id"}}}`)
probe, err := parseFFprobe(raw, "song.flac")
require.NoError(t, err)
require.Equal(t, "release-id", probe.RawTags["musicbrainz album id"])
require.Equal(t, "recording-id", probe.RawTags["musicbrainz track id"])
require.Equal(t, "Song", probe.Tags.Title)
}