release / govulncheck (push) Successful in 17s
release / web (push) Successful in 1m33s
release / go (push) Successful in 1m49s
release / integration (push) Successful in 5m42s
release / android (push) Successful in 5m59s
release / Build signed APK (releases and dev) (push) Successful in 6m9s
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
Applies rule 32's migration exception: each header still says what the schema was, what changes and what it prevents, but task, milestone, decision and rule ids are replaced by the practice they stood for, the operator quotes by the decision, single-install counts are dropped, and pointers to a deleted package and an external design doc are gone. golang-migrate keeps no checksum, so editing applied migrations' comments is safe. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
38 lines
2.1 KiB
SQL
38 lines
2.1 KiB
SQL
-- 0058_track_fingerprints.up.sql — an acoustic identity per track.
|
|
--
|
|
-- A table of its own rather than columns on tracks, for the hot path's sake:
|
|
-- tracks is read with SELECT * by eight queries, among them ListTracksByAlbum,
|
|
-- SearchTracks and GetTracksByIDs — album pages, search, the Subsonic surface.
|
|
-- A ~4 KB chromaprint column on tracks would be de-TOASTed on every one of
|
|
-- those reads to carry a value only the duplicate sweep ever looks at.
|
|
--
|
|
-- What a row means, which the backfill depends on:
|
|
-- no row never fingerprinted
|
|
-- fingerprint_version < current derived by an older method; re-derive it
|
|
-- fingerprint_version = current attempted; a NULL value means that tool
|
|
-- failed on this file, and it is not retried
|
|
-- until the file changes
|
|
-- A failure that says nothing about the file — a timeout, a cancelled scan, a
|
|
-- missing binary — writes no row at all, so the backfill tries again.
|
|
CREATE TABLE track_fingerprints (
|
|
-- CASCADE is right here, unlike for the likes and play history the
|
|
-- duplicate merge has to carry across: a fingerprint describes one file's
|
|
-- bytes and means nothing once that file's row is gone.
|
|
track_id uuid PRIMARY KEY REFERENCES tracks (id) ON DELETE CASCADE,
|
|
-- SHA-256 of the ENCODED audio packets (ffmpeg -c:a copy -f hash), not of
|
|
-- decoded samples. internal/library/fingerprint.go says why.
|
|
audio_stream_sha256 bytea
|
|
CHECK (audio_stream_sha256 IS NULL OR octet_length(audio_stream_sha256) = 32),
|
|
-- fpcalc -raw -signed: the same 32 bits per item, stored signed because
|
|
-- integer is.
|
|
chromaprint integer[],
|
|
fingerprint_version smallint NOT NULL,
|
|
computed_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
|
|
-- The exact duplicate tier is an equality match on this column. Partial
|
|
-- because a NULL is never looked up — it only means the hash was not taken.
|
|
CREATE INDEX track_fingerprints_audio_stream_sha256
|
|
ON track_fingerprints (audio_stream_sha256)
|
|
WHERE audio_stream_sha256 IS NOT NULL;
|