docs: library comments say what the scanner and resolver do, not how they came to be (#6277)
release / govulncheck (push) Successful in 20s
release / web (push) Successful in 1m20s
release / go (push) Successful in 1m37s
release / integration (push) Successful in 5m15s
release / android (push) Successful in 5m26s
release / Build signed APK (releases and dev) (push) Successful in 5m45s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 16s
release / Verify release artifacts (tag releases only) (push) Skipped

Rewrites the internal/library comment blocks to rule 32. Task, milestone,
decision and rule ids are gone; the incidents behind them are restated as
the constraint they left (why a file is deleted before its row, why the
missing mark is a mark, why duplicates block on duration). Counts and
album names from one library are dropped from production comments; test
comments keep the names their fixtures use. The tagReadVersion log keeps
each version's before and after, without the measurements.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-23 12:27:34 -04:00
co-authored by Claude Opus 5.5
parent 08a2c06b85
commit 7e6228c13f
66 changed files with 578 additions and 564 deletions
+26 -26
View File
@@ -18,7 +18,7 @@ import (
"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
)
// Acoustic identity (M400).
// Acoustic identity.
//
// Two values per track, because they answer different questions:
//
@@ -26,7 +26,7 @@ import (
// same audio bytes, whatever the tags or container around
// them say. No threshold and no false positives — this is
// what catches two copies of one MP3 that differ only in
// their ID3 (#3885).
// their ID3.
//
// chromaprint fpcalc's raw fingerprint. Close means the same
// recording, even at another bitrate or in another codec
@@ -39,9 +39,9 @@ import (
// fingerprintTimeout bounds one ffmpeg hash or fpcalc call. Longer than
// probeTimeout because both read the audio rather than a header: the hash reads
// every packet and fpcalc decodes up to its -length. 60s leaves room for a large
// lossless file on a slow network mount; a call needing more is a stall, not a
// big file.
// every packet and fpcalc decodes up to its -length. 60s leaves room for a
// large lossless file on a slow network mount; a call needing more is a stall,
// not a big file.
const fingerprintTimeout = 60 * time.Second
// fingerprintWaitDelay bounds how long Output may keep waiting on the tool's
@@ -52,9 +52,10 @@ const fingerprintWaitDelay = 5 * time.Second
// fingerprintVersion stamps how a track_fingerprints row was derived. Bump it
// whenever the derivation changes — the hash arguments or fpcalc's flags — and
// the backfill re-derives every row below it. Fingerprints taken by two methods
// are not comparable, and nothing else would reveal that the library held a mix.
// are not comparable, and nothing else would reveal that the library held a
// mix.
//
// The length is deliberately not part of it: it is an operator setting (#3913),
// The length is deliberately not part of it: it is an admin setting,
// so each row records the length it was taken at and readers compare only rows
// at the current one. See fingerprint_settings.go.
const fingerprintVersion int16 = 1
@@ -63,7 +64,7 @@ const fingerprintVersion int16 = 1
// failed exit because a stall is a fact about the mount, not about the file.
var errFingerprintTimeout = errors.New("fingerprint tool timed out")
// defaultChromaprintLengthSec is the shipped value of the length setting (#3913):
// defaultChromaprintLengthSec is the shipped value of the length setting:
// how many seconds of audio fpcalc fingerprints. 120 is fpcalc's own default.
const defaultChromaprintLengthSec = 120
@@ -99,10 +100,10 @@ func streamHashArgs(path string) []string {
// fpcalcArgs asks for the raw fingerprint as SIGNED integers.
//
// -raw because the matcher compares items bit by bit, which the compressed form
// cannot do without being unpacked first. -signed because the column is Postgres
// integer[], which is signed: fpcalc's default prints uint32, and half of those
// values do not fit. Signed output is the same 32 bits with no reinterpretation
// step left to get wrong.
// cannot do without being unpacked first. -signed because the column is
// Postgres integer[], which is signed: fpcalc's default prints uint32, and half
// of those values do not fit. Signed output is the same 32 bits with no
// reinterpretation step left to get wrong.
func fpcalcArgs(path string, lengthSec int) []string {
return []string{
"-raw", "-signed",
@@ -112,7 +113,7 @@ func fpcalcArgs(path string, lengthSec int) []string {
}
// lookupFingerprintLengthSec is how much audio the AcoustID lookup print
// covers (M401). Fixed at fpcalc's default rather than following the
// covers. Fixed at fpcalc's default rather than following the
// duplicate-detection length setting: AcoustID's database is built from
// prints that taggers such as Picard take at the default, and a lookup print
// of a different span matches it less well.
@@ -120,7 +121,7 @@ const lookupFingerprintLengthSec = 120
// fpcalcLookupArgs asks for the COMPRESSED fingerprint, fpcalc's default
// output and the string AcoustID's lookup takes. A second decode beside the
// raw print, chosen over re-encoding the stored raw array in Go (M401 D1):
// raw print, chosen over re-encoding the stored raw array in Go:
// fpcalc is the reference encoder, while a subtle bug in a port would still
// produce a well-formed string that AcoustID answers with "no match".
func fpcalcLookupArgs(path string) []string {
@@ -147,8 +148,8 @@ type fingerprintResult struct {
}
// fingerprintOptions is what the settings decide for one attempt. Captured once
// per file, so the length a chromaprint was taken at is the length stored with it
// even if the setting changes mid-attempt.
// per file, so the length a chromaprint was taken at is the length stored with
// it even if the setting changes mid-attempt.
type fingerprintOptions struct {
lengthSec int32
// chromaprint false takes the stream hash alone: a demux, no decode.
@@ -208,11 +209,11 @@ const (
)
// storeFingerprint records one attempt, for the scan (new or changed bytes) and
// the backfill (#3908) alike, so there is one rule for what gets written. It
// the backfill alike, so there is one rule for what gets written. It
// never fails its caller: a missing fingerprint only keeps a track out of
// duplicate detection, which is not worth dropping a scan or a pass over.
//
// lengthSec is the length fp's chromaprint was taken at, stored with it (#3913).
// lengthSec is the length fp's chromaprint was taken at, stored with it.
func storeFingerprint(
ctx context.Context, q *dbq.Queries, logger *slog.Logger,
trackID pgtype.UUID, path string, fp fingerprintResult, lengthSec int32,
@@ -284,8 +285,8 @@ func computeLookupFingerprint(ctx context.Context, path string) (lookupFingerpri
// runFingerprintTool runs one tool under fingerprintTimeout.
//
// Any non-zero exit is an error, and that deliberately includes fpcalc's exit 3:
// "reading failed, but here is a fingerprint of what I got". A partial
// Any non-zero exit is an error, and that deliberately includes fpcalc's exit
// 3: "reading failed, but here is a fingerprint of what I got". A partial
// fingerprint of a damaged file is not that file's identity. Stored, it would
// score against a healthy copy over whatever prefix survived, and could group
// or fail to group either way. Absent is better than wrong.
@@ -336,11 +337,10 @@ func (e *toolExitError) Error() string {
const fpcalcExitPartial = 3
// fpcalcReadToEnd reports whether fpcalc's partial exit only means it decoded
// the whole file (#6178). The image's fpcalc 1.5.1 reports the decoder's end of
// file as "Error decoding audio frame (End of file)" and exits 3 whenever it
// reaches the end before -length, so it failed every file shorter than the
// span asked for: every track under two minutes, at any length setting. That
// print covers the whole file, so it is the file's identity.
// the whole file. The image's fpcalc 1.5.1 reports the decoder's end of file
// as "Error decoding audio frame (End of file)" and exits 3 whenever it
// reaches the end before -length, which is every file shorter than the span
// asked for. That print covers the whole file, so it is the file's identity.
//
// A damaged file can stop at an end of file too. Its DURATION, the length the
// container declares, is then longer than the span asked for, and it stays a
@@ -475,7 +475,7 @@ func notURLBase64(r rune) bool {
// LookupFingerprint is the compressed fingerprint AcoustID's lookup takes and
// the file's whole length in seconds, for a file not in the library yet: the
// import checks a download with it (M502 #5522). IsInconclusive tells a
// import checks a download with it. IsInconclusive tells a
// missing or stalled fpcalc from a file it rejected.
func LookupFingerprint(ctx context.Context, path string) (fingerprint string, durationSec int, err error) {
fp, err := computeLookupFingerprint(ctx, path)