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
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:
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")
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
Reference in new issue
Block a user