Files
pipu-music/services/api/internal/library/fingerprint_test.go
T
mrleo1nidandClaude Opus 5.5 9b50038bb4
ci / api / api (push) Successful in 27s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 58s
feat(api): SHA-256 and Chromaprint fingerprints for the library (M5.5)
A background Fingerprinter runs after every scan and at startup, one file
at a time, filling media_files.sha256 and track_fingerprints (fpcalc raw,
first 120 s). Without fpcalc only SHA-256 is computed and files stay
queued for Chromaprint. Changing a file's content resets its prints.
Similarity() compares raw prints with a ±80-point alignment search; it is
the basis for duplicate detection in the upload queue.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 13:37:05 +03:00

59 lines
1.5 KiB
Go

package library
import (
"math/rand/v2"
"testing"
)
func randomPrint(r *rand.Rand, n int) []uint32 {
out := make([]uint32, n)
for i := range out {
out[i] = r.Uint32()
}
return out
}
func TestSimilarity(t *testing.T) {
r := rand.New(rand.NewPCG(1, 2))
a := randomPrint(r, 950) // ~2 мин
if s := Similarity(a, a); s != 1 {
t.Fatalf("same print: %v", s)
}
// Перекодирование: другое вступление (сдвиг на 20 точек) и ~8% перевёрнутых бит.
b := append(randomPrint(r, 20), a...)
for i := range b {
for bit := range 32 {
if r.IntN(100) < 8 {
b[i] ^= 1 << bit
}
}
}
if s := Similarity(a, b); s < SameRecording {
t.Fatalf("re-encoded copy: %v < %v", s, SameRecording)
}
if s := Similarity(a, randomPrint(r, 950)); s >= SameRecording {
t.Fatalf("different recordings: %v >= %v", s, SameRecording)
}
if s := Similarity(a, a[:40]); s != 0 {
t.Fatalf("too short overlap must not match: %v", s)
}
}
func TestParseFPCalc(t *testing.T) {
fp, err := parseFPCalc([]byte(`{"duration": 215.4, "fingerprint": [4294967295, 1, 2]}`))
if err != nil {
t.Fatal(err)
}
if fp.DurationMs != 215400 || len(fp.Points) != 3 || fp.Points[0] != 4294967295 {
t.Fatalf("parsed %+v", fp)
}
if fp.Prefix() != int64(-4294967295) { // 0xFFFFFFFF_00000001 как int64
t.Fatalf("prefix %d", fp.Prefix())
}
if _, err := parseFPCalc([]byte(`{"duration": 1, "fingerprint": []}`)); err == nil {
t.Fatal("empty fingerprint must fail")
}
}