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
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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user