docs: migration headers keep the before and after, without ids, quotes or one library's numbers (#6277)
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
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>
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
-- Core library schema per spec §5. Dual-like, session, scrobble, and
|
-- Core library schema: artists, albums, tracks and users. Likes, sessions,
|
||||||
-- similarity tables land with their respective feature milestones.
|
-- scrobbling and similarity get tables of their own in later migrations.
|
||||||
|
|
||||||
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
-- Session tokens authenticate /api/* requests. We store sha256(token) so a
|
-- Session tokens authenticate /api/* requests. We store sha256(token) so a
|
||||||
-- read-only DB leak doesn't grant active sessions; the raw token lives only
|
-- read-only DB leak doesn't grant active sessions; the raw token lives only
|
||||||
-- in the client's cookie (web) or bearer header (Flutter).
|
-- in the client's cookie (web) or bearer header (native clients).
|
||||||
--
|
--
|
||||||
-- last_seen_at enables an "active sessions" UI later (not wired in this plan)
|
-- last_seen_at and user_agent (captured at issue time) are for an "active
|
||||||
-- without schema churn. user_agent is captured at issue time for the same
|
-- sessions" list.
|
||||||
-- reason — free metadata now, no migration later.
|
|
||||||
|
|
||||||
CREATE TABLE sessions (
|
CREATE TABLE sessions (
|
||||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
-- M2 listening telemetry: play_sessions, play_events, skip_events.
|
-- Listening telemetry: play_sessions, play_events, skip_events.
|
||||||
-- Note the table is named play_sessions (not sessions — that table already
|
-- The table is named play_sessions (not sessions — that table already
|
||||||
-- exists from migration 0004 and holds HTTP auth sessions). Listening
|
-- exists from migration 0004 and holds HTTP auth sessions). Listening
|
||||||
-- sessions are an entirely separate concept driven by spec §6.
|
-- sessions are an entirely separate concept.
|
||||||
--
|
--
|
||||||
-- session_vector_at_play and contextual_likes ship as nullable now even
|
-- session_vector_at_play is nullable: plays are recorded before the
|
||||||
-- though M2 writes NULL; M3 will populate them without a follow-up
|
-- recommendation engine fills it.
|
||||||
-- migration.
|
|
||||||
|
|
||||||
CREATE TABLE play_sessions (
|
CREATE TABLE play_sessions (
|
||||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
-- M2 likes: three tables, one per entity type. Schema follows the spec §5
|
-- Likes: three tables, one per entity type. general_likes was track-only;
|
||||||
-- pattern (general_likes was originally track-only); this slice extends it
|
-- albums and artists get the same shape for Subsonic star/unstar.
|
||||||
-- to albums and artists for full Subsonic star/unstar support.
|
|
||||||
|
|
||||||
CREATE TABLE general_likes (
|
CREATE TABLE general_likes (
|
||||||
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
-- contextual_likes captures a per-session-context snapshot at the time of
|
-- contextual_likes captures a per-session-context snapshot at the time of each
|
||||||
-- each like. The recommendation engine in M3 sub-plan #3 uses these rows
|
-- like. The recommendation engine uses these rows to compute
|
||||||
-- to compute contextual_match_score per (current session, candidate track).
|
-- contextual_match_score per (current session, candidate track).
|
||||||
--
|
--
|
||||||
-- Multiple rows per (user, track) are allowed and expected — each row
|
-- Multiple rows per (user, track) are allowed and expected — each row
|
||||||
-- represents a like in a specific session context. Soft-deleted rows
|
-- represents a like in a specific session context. Soft-deleted rows
|
||||||
-- remain in the table (deleted_at populated) but are filtered out of the
|
-- remain in the table (deleted_at populated) but are filtered out of the
|
||||||
-- engine's queries via the partial index. Re-liking a previously-unliked
|
-- engine's queries via the partial index. Re-liking a previously-unliked
|
||||||
-- track adds a NEW row (does not undelete the old one).
|
-- track adds a NEW row (does not undelete the old one).
|
||||||
--
|
|
||||||
-- Note: the M2 events migration (0005) commented that contextual_likes
|
|
||||||
-- "ships nullable now" but the actual CREATE TABLE was missed. This slice
|
|
||||||
-- ships the table for the first time.
|
|
||||||
|
|
||||||
CREATE TABLE contextual_likes (
|
CREATE TABLE contextual_likes (
|
||||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
@@ -27,6 +23,6 @@ CREATE INDEX contextual_likes_active_idx
|
|||||||
ON contextual_likes (user_id, track_id)
|
ON contextual_likes (user_id, track_id)
|
||||||
WHERE deleted_at IS NULL;
|
WHERE deleted_at IS NULL;
|
||||||
|
|
||||||
-- Vector similarity queries from M3 sub-plan #3 will use this.
|
-- For the engine's vector similarity queries.
|
||||||
CREATE INDEX contextual_likes_vector_idx
|
CREATE INDEX contextual_likes_vector_idx
|
||||||
ON contextual_likes USING gin (session_vector);
|
ON contextual_likes USING gin (session_vector);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
-- M4a: outbound ListenBrainz scrobble worker.
|
-- Outbound ListenBrainz scrobble worker.
|
||||||
-- Per-user LB config (plaintext token; users rotate via /settings if leaked).
|
-- Per-user LB config (plaintext token; users rotate via /settings if leaked).
|
||||||
-- scrobble_queue is a work list, not a log: successfully-sent rows are
|
-- scrobble_queue is a work list, not a log: successfully-sent rows are
|
||||||
-- DELETEd by the worker (the canonical record stays in play_events.scrobbled_at
|
-- DELETEd by the worker (the canonical record stays in
|
||||||
-- via M2's column). Only `pending` and `failed` states exist.
|
-- play_events.scrobbled_at). Only `pending` and `failed` states exist.
|
||||||
|
|
||||||
ALTER TABLE users
|
ALTER TABLE users
|
||||||
ADD COLUMN listenbrainz_token TEXT NULL,
|
ADD COLUMN listenbrainz_token TEXT NULL,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- M4b: ListenBrainz similarity ingest. Two symmetric tables — tracks and
|
-- ListenBrainz similarity ingest. Two symmetric tables — tracks and
|
||||||
-- artists — both keyed by (a_id, b_id, source) so the schema reserves room
|
-- artists — both keyed by (a_id, b_id, source) so the schema reserves room
|
||||||
-- for additional similarity sources (musicbrainz_tag, user_cooccurrence)
|
-- for additional similarity sources (musicbrainz_tag, user_cooccurrence)
|
||||||
-- alongside listenbrainz. M4b only writes 'listenbrainz' rows.
|
-- alongside listenbrainz. M4b only writes 'listenbrainz' rows.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
-- M5a: Lidarr integration foundation. Two tables:
|
-- Lidarr integration. Two tables:
|
||||||
--
|
--
|
||||||
-- lidarr_config — singleton (CHECK id=1) holding the operator's Lidarr
|
-- lidarr_config — singleton (CHECK id=1) holding the Lidarr
|
||||||
-- connection. enabled=false is the unconfigured state.
|
-- connection. enabled=false is the unconfigured state.
|
||||||
--
|
--
|
||||||
-- lidarr_requests — per-request lifecycle row created by users at
|
-- lidarr_requests — per-request lifecycle row created by users at
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- M5b: per-user track quarantines + admin action audit log.
|
-- Per-user track quarantines + admin action audit log.
|
||||||
--
|
--
|
||||||
-- lidarr_quarantine — one row per (user, track) complaint. PK matches
|
-- lidarr_quarantine — one row per (user, track) complaint. PK matches
|
||||||
-- the general_likes pattern. Re-flagging the same track upserts. Deleted
|
-- the general_likes pattern. Re-flagging the same track upserts. Deleted
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- M5c: persist unmatched-similar-artist MBIDs that the M4b worker would
|
-- Persist unmatched-similar-artist MBIDs that the M4b worker would
|
||||||
-- otherwise discard. Mirrors artist_similarity shape: same composite PK
|
-- otherwise discard. Mirrors artist_similarity shape: same composite PK
|
||||||
-- with source, same (seed_id, score DESC) index, same source enum check.
|
-- with source, same (seed_id, score DESC) index, same source enum check.
|
||||||
-- The candidate side is text + name (no FK) — that's the whole point.
|
-- The candidate side is text + name (no FK) — that's the whole point.
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
-- M6a follow-up: persist the operator's metadata-profile choice alongside
|
-- Persist the chosen Lidarr metadata profile alongside quality profile +
|
||||||
-- quality profile + root folder. Lidarr POST /api/v1/artist requires
|
-- root folder. Lidarr POST /api/v1/artist requires metadataProfileId;
|
||||||
-- metadataProfileId; without storing it we'd refetch and pick first on
|
-- without storing it every Approve would refetch and pick the first.
|
||||||
-- every Approve, denying the operator a choice. Nullable: existing rows
|
-- Nullable: existing rows get NULL and the service falls back to "fetch
|
||||||
-- (and operators upgrading from before this migration) get NULL and the
|
-- list and pick first" until an admin saves.
|
||||||
-- service falls back to "fetch list and pick first" until they save.
|
|
||||||
|
|
||||||
ALTER TABLE lidarr_config
|
ALTER TABLE lidarr_config
|
||||||
ADD COLUMN default_metadata_profile_id integer;
|
ADD COLUMN default_metadata_profile_id integer;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
-- M7 #352 slice 1: playlists CRUD foundation.
|
-- User playlists.
|
||||||
-- See docs/superpowers/specs/2026-05-03-m7-playlists-crud-design.md.
|
|
||||||
|
|
||||||
CREATE TABLE playlists (
|
CREATE TABLE playlists (
|
||||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
-- M7 #352 slice 2: system-generated playlists.
|
-- System-generated playlists.
|
||||||
-- Adds kind/system_variant/seed_artist_id columns to playlists and a
|
-- Adds kind/system_variant/seed_artist_id columns to playlists and a
|
||||||
-- per-user run-tracking table. Backfills existing slice-1 playlists
|
-- per-user run-tracking table. Backfills existing playlists to
|
||||||
-- to kind='user'.
|
-- kind='user'.
|
||||||
|
|
||||||
-- Discriminator on existing playlists table.
|
-- Discriminator on existing playlists table.
|
||||||
ALTER TABLE playlists
|
ALTER TABLE playlists
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
-- M7 #353: album cover origin attribution.
|
-- Album cover origin attribution.
|
||||||
-- 'embedded' — extracted from audio file (reserved for future slice).
|
-- 'embedded' — extracted from audio file (reserved).
|
||||||
-- 'sidecar' — found in the album directory (cover.jpg/folder.jpg/etc).
|
-- 'sidecar' — found in the album directory (cover.jpg/folder.jpg/etc).
|
||||||
-- 'mbcaa' — fetched from MusicBrainz Cover Art Archive.
|
-- 'mbcaa' — fetched from MusicBrainz Cover Art Archive.
|
||||||
-- 'none' — tried, no cover available. Skip auto-retry; only manual.
|
-- 'none' — tried, no cover available. Skip auto-retry; only manual.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- M7 #381: track library-scan / MBID-backfill / cover-enrich runs so the
|
-- Track library-scan / MBID-backfill / cover-enrich runs so the
|
||||||
-- admin overview can show last-run results + manual trigger state.
|
-- admin overview can show last-run results + manual trigger state.
|
||||||
CREATE TABLE scan_runs (
|
CREATE TABLE scan_runs (
|
||||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
-- M7 cover-sources: schema for additional cover-art providers (TheAudioDB
|
-- Schema for additional cover-art providers (TheAudioDB first), artist art
|
||||||
-- as the v1 implementation), artist art (thumb + fanart), and the
|
-- (thumb + fanart), and the recheck-on-source-change trigger.
|
||||||
-- recheck-on-source-change trigger.
|
|
||||||
|
|
||||||
-- (0) Extend the existing albums.cover_art_source CHECK to accept the
|
-- (0) Extend the existing albums.cover_art_source CHECK to accept the
|
||||||
-- new 'theaudiodb' value. The constraint was created in migration 0016
|
-- new 'theaudiodb' value. The constraint was created in migration 0016
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- M7 recurring-scans: singleton table holding the operator's schedule
|
-- Recurring scans: singleton table holding the scan schedule
|
||||||
-- config. mode is the discriminator: 'off' / 'interval' / 'daily' / 'weekly'.
|
-- config. mode is the discriminator: 'off' / 'interval' / 'daily' / 'weekly'.
|
||||||
-- Per-mode fields are populated only when their mode is selected; others
|
-- Per-mode fields are populated only when their mode is selected; others
|
||||||
-- stay NULL. weekly_day uses ISO 8601: 1=Monday … 7=Sunday.
|
-- stay NULL. weekly_day uses ISO 8601: 1=Monday … 7=Sunday.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
-- Relax cover-art and artist-art source CHECK constraints so the new
|
-- Relax cover-art and artist-art source CHECK constraints so the new
|
||||||
-- Deezer and Last.fm providers (next slice) can persist their IDs as
|
-- Deezer and Last.fm providers can persist their IDs as
|
||||||
-- accepted source values. Also add the
|
-- accepted source values. Also add the
|
||||||
-- last_registered_providers_hash column to cover_art_sources_meta for
|
-- last_registered_providers_hash column to cover_art_sources_meta for
|
||||||
-- boot-time auto-bump detection (when the registered-provider set
|
-- boot-time auto-bump detection (when the registered-provider set
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
-- User management foundation (U1 of #376):
|
-- User management:
|
||||||
--
|
--
|
||||||
-- - users.display_name: optional human-readable name distinct from username.
|
-- - users.display_name: optional human-readable name distinct from username.
|
||||||
-- - user_invites: admin-generated tokens; user redeems during signup.
|
-- - user_invites: admin-generated tokens; user redeems during signup.
|
||||||
-- - registration_settings (singleton): controls whether registration is
|
-- - registration_settings (singleton): controls whether registration is
|
||||||
-- 'invite_only' (default after first admin) or 'open' (admin can
|
-- 'invite_only' (default after first admin) or 'open' (admin can
|
||||||
-- re-enable). The handler in U1-T2 also has a special-case for the
|
-- re-enable). The registration handler also has a special-case for the
|
||||||
-- "no users yet" state where it ignores invites and lets the first
|
-- "no users yet" state where it ignores invites and lets the first
|
||||||
-- register as admin.
|
-- register as admin.
|
||||||
-- - audit_log: append-only record of admin-driven user mutations.
|
-- - audit_log: append-only record of admin-driven user mutations.
|
||||||
-- No retention policy in v1.
|
-- No retention policy.
|
||||||
|
|
||||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS display_name text;
|
ALTER TABLE users ADD COLUMN IF NOT EXISTS display_name text;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
-- Adds users.auto_approve_requests for the per-user auto-approve toggle
|
-- Adds users.auto_approve_requests for the per-user auto-approve toggle:
|
||||||
-- (#355 sub-feature surface). The handler that honors this flag in
|
-- the request submission flow approves that user's requests at once.
|
||||||
-- the request submission flow is sibling work (U2.5 follow-up); this
|
|
||||||
-- migration just lands the schema + the column default.
|
|
||||||
--
|
--
|
||||||
-- Default false: existing users keep manual approval, opt-in via the
|
-- Default false: existing users keep manual approval, opt-in via the
|
||||||
-- admin user-management UI.
|
-- admin user-management UI.
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
-- Append-only change log for library entities. Every mutation on
|
-- Append-only change log for library entities. Every mutation on
|
||||||
-- artists/albums/tracks/likes/playlists/playlist_tracks writes a row
|
-- artists/albums/tracks/likes/playlists/playlist_tracks writes a row
|
||||||
-- in the same transaction as the mutation itself. Powers the Flutter
|
-- in the same transaction as the mutation itself. Powers the delta-sync
|
||||||
-- delta-sync endpoint (#357) — clients pass back the last-seen `id`
|
-- endpoint — clients pass back the last-seen `id` as a cursor.
|
||||||
-- as a cursor.
|
|
||||||
--
|
--
|
||||||
-- Retention: not capped here. If the table grows unbounded a follow-up
|
-- Retention: not capped here. A cursor older than what the table keeps gets
|
||||||
-- adds a periodic compactor (collapse contiguous upserts on the same
|
-- the reset path (410).
|
||||||
-- entity, drop pre-delete history). Cursor reset path (410) covers the
|
|
||||||
-- compactor's edge.
|
|
||||||
|
|
||||||
CREATE TABLE library_changes (
|
CREATE TABLE library_changes (
|
||||||
id BIGSERIAL PRIMARY KEY,
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
-- 0026_users_timezone.up.sql — per-user timezone for the system-playlist
|
-- 0026_users_timezone.up.sql — per-user timezone for the system-playlist
|
||||||
-- scheduler (#392 Half B). Default 'UTC' so existing rows + brand-new
|
-- scheduler. Default 'UTC' so existing rows + brand-new
|
||||||
-- users have a safe value before their first client check-in via
|
-- users have a safe value before their first client check-in via
|
||||||
-- PUT /api/me/timezone. The scheduler reads this column to decide
|
-- PUT /api/me/timezone. The scheduler reads this column to decide
|
||||||
-- which IANA zone to anchor each user's daily 03:00 build to.
|
-- which IANA zone to anchor each user's daily 03:00 build to.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
-- 0027_system_playlist_rotation.up.sql — sample-history dedup for
|
-- 0027_system_playlist_rotation.up.sql — sample-history dedup for
|
||||||
-- system playlists (Fable #415). Two parts:
|
-- system playlists. Two parts:
|
||||||
--
|
--
|
||||||
-- 1. play_events.source: which system playlist a play originated
|
-- 1. play_events.source: which system playlist a play originated
|
||||||
-- from ('for_you' | 'discover' | future kinds), or NULL for any
|
-- from ('for_you' | 'discover' | future kinds), or NULL for any
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
-- #411 R3 bugfix: the five discovery mixes (deep_cuts, rediscover,
|
-- Before: the five discovery mixes (deep_cuts, rediscover, new_for_you,
|
||||||
-- new_for_you, on_this_day, first_listens) were added as producers +
|
-- on_this_day, first_listens) had producers and registry entries but no
|
||||||
-- registry entries but their system_variant values were never added
|
-- system_variant values in the two playlists CHECK constraints, so inserting
|
||||||
-- to the two playlists CHECK constraints. Result: insertSystemPlaylist
|
-- any of them failed playlists_kind_variant_consistent (SQLSTATE 23514) —
|
||||||
-- for any new mix fails with playlists_kind_variant_consistent
|
-- and because BuildSystemPlaylists is one all-or-nothing transaction, that
|
||||||
-- (SQLSTATE 23514), which — because BuildSystemPlaylists is one
|
-- aborts the WHOLE build. After: the constraints accept them.
|
||||||
-- all-or-nothing transaction — aborts the WHOLE build, so For-You /
|
|
||||||
-- Discover refresh 500s and the lazy daily build fails too.
|
|
||||||
--
|
--
|
||||||
-- All five are seedless (like for_you/discover): seed_artist_id IS
|
-- All five are seedless (like for_you/discover): seed_artist_id IS
|
||||||
-- NULL. Drop + re-add both constraints with the expanded set, same
|
-- NULL. Drop + re-add both constraints with the expanded set, same
|
||||||
|
|||||||
@@ -2,9 +2,8 @@
|
|||||||
-- provider's registry ID. coverart.Register makes the provider set
|
-- provider's registry ID. coverart.Register makes the provider set
|
||||||
-- extensible, so a fixed IN-list CHECK (0016 → 0018 → 0020) required a
|
-- extensible, so a fixed IN-list CHECK (0016 → 0018 → 0020) required a
|
||||||
-- schema migration for every new provider and rejected any
|
-- schema migration for every new provider and rejected any
|
||||||
-- out-of-list value (e.g. test stub providers). Same brittleness
|
-- out-of-list value (e.g. test stub providers): a fixed-value CHECK
|
||||||
-- class as the discovery-mix CHECK incident (#433): a fixed-value
|
-- fighting an extensible value set. Relax to "NULL or any
|
||||||
-- CHECK fighting an extensible value set. Relax to "NULL or any
|
|
||||||
-- non-empty string" — the registry, not the schema, is the source of
|
-- non-empty string" — the registry, not the schema, is the source of
|
||||||
-- truth for valid provider IDs.
|
-- truth for valid provider IDs.
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
-- for an approved request. NULL = not yet sent/confirmed; the
|
-- for an approved request. NULL = not yet sent/confirmed; the
|
||||||
-- reconciler idempotently (re)sends the add until this is set, then
|
-- reconciler idempotently (re)sends the add until this is set, then
|
||||||
-- watches for the import. Decouples the durable admin approval from the
|
-- watches for the import. Decouples the durable admin approval from the
|
||||||
-- best-effort Lidarr call so a transient Lidarr outage no longer forces
|
-- best-effort Lidarr call, so a transient Lidarr outage does not force a
|
||||||
-- a manual re-approve.
|
-- manual re-approve.
|
||||||
ALTER TABLE lidarr_requests
|
ALTER TABLE lidarr_requests
|
||||||
ADD COLUMN lidarr_add_confirmed_at timestamptz;
|
ADD COLUMN lidarr_add_confirmed_at timestamptz;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
-- Client-reported playback errors. Populated by clients when a track
|
-- Client-reported playback errors. Populated by clients when a track
|
||||||
-- fails to play (zero duration, decode error, etc.); surfaced in the
|
-- fails to play (zero duration, decode error, etc.); surfaced in the
|
||||||
-- admin /admin/playback-errors inbox so the operator can hide / delete
|
-- admin /admin/playback-errors inbox so an admin can hide / delete
|
||||||
-- / re-request the offending track.
|
-- / re-request the offending track.
|
||||||
--
|
--
|
||||||
-- resolved_at + resolved_by + resolution are NULL until an admin acts
|
-- resolved_at + resolved_by + resolution are NULL until an admin acts
|
||||||
@@ -10,8 +10,8 @@
|
|||||||
-- unresolved-queue lookup fast even as resolved history accumulates.
|
-- unresolved-queue lookup fast even as resolved history accumulates.
|
||||||
--
|
--
|
||||||
-- CHECK constraints on kind/resolution gate the enum values so client
|
-- CHECK constraints on kind/resolution gate the enum values so client
|
||||||
-- typos can't pollute the data (per the standing rule about
|
-- typos can't pollute the data; a new value means swapping the constraint
|
||||||
-- enum-CHECK whitelists needing migrations).
|
-- in the same migration as the code that writes it.
|
||||||
|
|
||||||
CREATE TABLE playback_errors (
|
CREATE TABLE playback_errors (
|
||||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- Retire the configurable scan scheduler. New-file pickup is now handled by
|
-- Retire the configurable scan scheduler. New-file pickup is now handled by
|
||||||
-- the fsnotify watcher + a hardcoded safety-net delta walk (cmd/minstrel),
|
-- the fsnotify watcher + a hardcoded safety-net delta walk (cmd/minstrel),
|
||||||
-- so the operator-configurable schedule (and its admin UI) is gone.
|
-- so the configurable schedule (and its admin UI) is gone.
|
||||||
DROP TABLE IF EXISTS scan_schedule;
|
DROP TABLE IF EXISTS scan_schedule;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- 0034_you_might_like.up.sql — "You might like" Home rows (#790).
|
-- 0034_you_might_like.up.sql — "You might like" Home rows.
|
||||||
--
|
--
|
||||||
-- Per-user ranked lists of IN-LIBRARY albums/artists the listener
|
-- Per-user ranked lists of IN-LIBRARY albums/artists the listener
|
||||||
-- doesn't actively spin but is predicted to enjoy, derived from the
|
-- doesn't actively spin but is predicted to enjoy, derived from the
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
-- 0035_taste_profile.up.sql — per-user taste profile (#796 phase 1).
|
-- 0035_taste_profile.up.sql — per-user taste profile.
|
||||||
--
|
--
|
||||||
-- A persistent, decaying model of each user's taste, rebuilt daily from
|
-- A persistent, decaying model of each user's taste, rebuilt daily from
|
||||||
-- graded play engagement (completion + skip position) plus explicit
|
-- graded play engagement (completion + skip position) plus explicit
|
||||||
-- likes. Two facet tables: weighted artists and weighted genre tags.
|
-- likes. Two facet tables: weighted artists and weighted genre tags.
|
||||||
-- Weight is a signed float — positive = drawn to, negative = (passively)
|
-- Weight is a signed float — positive = drawn to, negative = (passively)
|
||||||
-- avoided; magnitude reflects decayed engagement. Phase 1 only BUILDS
|
-- avoided; magnitude reflects decayed engagement. Candidate sourcing and
|
||||||
-- these; consumption (candidate sourcing + scoring) lands in phase 2.
|
-- scoring read them.
|
||||||
--
|
--
|
||||||
-- CASCADE on user/artist deletes keeps them clean. Indexed by
|
-- CASCADE on user/artist deletes keeps them clean. Indexed by
|
||||||
-- (user_id, weight DESC) to serve "top taste artists/tags" reads cheaply.
|
-- (user_id, weight DESC) to serve "top taste artists/tags" reads cheaply.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- Device diagnostics / debug-reporting subsystem (M9).
|
-- Device diagnostics / debug-reporting subsystem.
|
||||||
--
|
--
|
||||||
-- When an account has users.debug_mode_enabled = true, that account's
|
-- When an account has users.debug_mode_enabled = true, that account's
|
||||||
-- client(s) stream a timeseries of device state here: connectivity +
|
-- client(s) stream a timeseries of device state here: connectivity +
|
||||||
@@ -8,12 +8,12 @@
|
|||||||
-- /admin/diagnostics view filters by account + device + time window and
|
-- /admin/diagnostics view filters by account + device + time window and
|
||||||
-- exports the slice as JSON for offline analysis.
|
-- exports the slice as JSON for offline analysis.
|
||||||
--
|
--
|
||||||
-- `kind` is a COARSE category gated by a CHECK whitelist; the finer
|
-- `kind` is a COARSE category gated by a CHECK whitelist; the finer event
|
||||||
-- event discriminator + all event-specific fields live in the `payload`
|
-- discriminator + all event-specific fields live in the `payload` jsonb. This
|
||||||
-- jsonb. This keeps the enum small and stable so new event variants
|
-- keeps the enum small and stable so new event variants don't require a
|
||||||
-- don't require a migration (per the enum-CHECK-whitelist rule, only a
|
-- migration (only a new *category* would: a new CHECK value means swapping the
|
||||||
-- new *category* would). If you add a category here, mirror it in the
|
-- constraint in the same migration). If you add a category here, mirror it in
|
||||||
-- handler whitelist in internal/api/diagnostics.go.
|
-- the handler whitelist in internal/api/diagnostics.go.
|
||||||
--
|
--
|
||||||
-- Two timestamps on purpose: occurred_at is the client device clock
|
-- Two timestamps on purpose: occurred_at is the client device clock
|
||||||
-- (when the event happened on the phone — may be skewed/wrong, e.g. in
|
-- (when the event happened on the phone — may be skewed/wrong, e.g. in
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
-- Add 'playback' to the diagnostic_events kind whitelist (M9 follow-up).
|
-- Add 'playback' to the diagnostic_events kind whitelist.
|
||||||
--
|
--
|
||||||
-- `route` and `player_state` events fire for EVERY output route (phone
|
-- `route` and `player_state` events fire for EVERY output route (phone
|
||||||
-- speaker, Bluetooth, UPnP/Sonos), so bucketing them under 'upnp_sync'
|
-- speaker, Bluetooth, UPnP/Sonos), so bucketing them under 'upnp_sync'
|
||||||
-- was misleading — an operator on Bluetooth earbuds saw "upnp_sync"
|
-- was misleading — a listener on Bluetooth earbuds produced "upnp_sync"
|
||||||
-- rows. 'upnp_sync' now means genuinely UPnP/Sonos-specific signal
|
-- rows. 'upnp_sync' now means genuinely UPnP/Sonos-specific signal
|
||||||
-- (drops, and future resync/desync); general playback + route telemetry
|
-- (drops, and future resync/desync); general playback + route telemetry
|
||||||
-- moves to the new 'playback' kind.
|
-- moves to the new 'playback' kind.
|
||||||
--
|
--
|
||||||
-- Per the enum-CHECK-whitelist rule, the new value lands by dropping and
|
-- The new value lands by dropping and re-adding the constraint in the same
|
||||||
-- re-adding the constraint in the same change. Existing rows tagged
|
-- migration. Existing rows tagged
|
||||||
-- 'upnp_sync' stay as-is (debug telemetry on a 30-day retention).
|
-- 'upnp_sync' stay as-is (debug telemetry on a 30-day retention).
|
||||||
|
|
||||||
ALTER TABLE diagnostic_events DROP CONSTRAINT diagnostic_events_kind_check;
|
ALTER TABLE diagnostic_events DROP CONSTRAINT diagnostic_events_kind_check;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
-- For You exploration attribution (milestone #127 step 2, Scribe #1249).
|
-- For You exploration attribution.
|
||||||
--
|
--
|
||||||
-- For You is deliberately composed of two populations: a head of
|
-- For You is deliberately composed of two populations: a head of
|
||||||
-- top-scored similarity matches ("taste" picks) and a tail sampled from
|
-- top-scored similarity matches ("taste" picks) and a tail sampled from
|
||||||
-- deeper in the candidate ranking ("fresh" picks — the freshness
|
-- deeper in the candidate ranking ("fresh" picks — the freshness
|
||||||
-- injection). Until now the metrics could only judge For You as one
|
-- injection). Before this the metrics could only judge For You as one
|
||||||
-- blob, so a high skip rate couldn't be read as "the taste engine is
|
-- blob, so a high skip rate couldn't be read as "the taste engine is
|
||||||
-- missing" vs "the freshness tax is too high".
|
-- missing" vs "the freshness tax is too high".
|
||||||
--
|
--
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
-- Provenance as standard (milestone #127, Scribe #1270).
|
-- Provenance as standard.
|
||||||
--
|
--
|
||||||
-- #1249 introduced pick_kind as a For You one-off ('taste'/'fresh').
|
-- 0038 introduced pick_kind as a For You one-off ('taste'/'fresh').
|
||||||
-- The mechanism — stamp WHY a track is in the snapshot at build time,
|
-- The mechanism — stamp WHY a track is in the snapshot at build time,
|
||||||
-- freeze it onto the play at ingestion, break it down in metrics — is
|
-- freeze it onto the play at ingestion, break it down in metrics — is
|
||||||
-- now the standard for every system mix, so the CHECK vocabulary
|
-- now the standard for every system mix, so the CHECK vocabulary
|
||||||
@@ -11,9 +11,9 @@
|
|||||||
-- Discover's three candidate buckets — makes
|
-- Discover's three candidate buckets — makes
|
||||||
-- the 40/30/30 allocation measurable instead
|
-- the 40/30/30 allocation measurable instead
|
||||||
-- of a guess
|
-- of a guess
|
||||||
-- tier1 / tier2 / tier3 the tiered-eligibility ladder (project rule
|
-- tier1 / tier2 / tier3 the tiered-eligibility ladder: tier 1 pins the
|
||||||
-- #131): tier 1 pins the mix's exact desire,
|
-- mix's exact desire, each higher tier steps
|
||||||
-- each higher tier steps back a little. Tier
|
-- back a little. Tier
|
||||||
-- provenance is how we measure what the
|
-- provenance is how we measure what the
|
||||||
-- step-back trades away.
|
-- step-back trades away.
|
||||||
--
|
--
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
-- Recommendation tuning lab (milestone #127, Scribe #1250).
|
-- Recommendation tuning lab.
|
||||||
--
|
--
|
||||||
-- The scoring weights move out of YAML (radio profile) and out of the
|
-- The scoring weights move out of YAML (radio profile) and out of the
|
||||||
-- systemMixWeights hard-code (daily_mix profile) into DB-backed
|
-- systemMixWeights hard-code (daily_mix profile) into DB-backed
|
||||||
-- settings with live effect — the defaults-discovery lab (decision
|
-- settings with live effect — a lab for FINDING good values, which then
|
||||||
-- #1247): the operator turns knobs here to FIND good values, which
|
-- get baked into shipped defaults; nobody should need this card to get
|
||||||
-- then get baked into shipped defaults; end users and other operators
|
-- good recommendations.
|
||||||
-- should never need this card.
|
|
||||||
--
|
--
|
||||||
-- Rows are seeded by the recsettings service's boot reconcile (the
|
-- Rows are seeded by the recsettings service's boot reconcile (the
|
||||||
-- coverart SettingsService pattern), not by this migration, so shipped
|
-- coverart SettingsService pattern), not by this migration, so shipped
|
||||||
@@ -39,7 +38,7 @@ CREATE TABLE taste_tuning (
|
|||||||
);
|
);
|
||||||
|
|
||||||
-- Every knob turn writes one audit row; the metrics trend view
|
-- Every knob turn writes one audit row; the metrics trend view
|
||||||
-- (#1251) annotates these on its timeline so cause→effect is visible
|
-- annotates these on its timeline so cause→effect is visible
|
||||||
-- after a change. changes is a jsonb array of {field, old, new}.
|
-- after a change. changes is a jsonb array of {field, old, new}.
|
||||||
CREATE TABLE recommendation_tuning_audit (
|
CREATE TABLE recommendation_tuning_audit (
|
||||||
id bigserial PRIMARY KEY,
|
id bigserial PRIMARY KEY,
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
-- Discover taste-targeted novelty bucket (milestone #127, Scribe #1252/#1488).
|
-- Discover taste-targeted novelty bucket.
|
||||||
--
|
--
|
||||||
-- The 2-week metrics review found Discover beating the manual baseline on
|
-- Without it, Discover on a single-user server is effectively dormant +
|
||||||
-- skip rate — which for a discovery surface means it is playing it safe:
|
-- crude-random, and the per-user taste profile's tags go unused. This adds a
|
||||||
-- on a single-user server it is effectively dormant + crude-random, and
|
|
||||||
-- the per-user taste profile (n_tags, #796) went unused. This adds a
|
|
||||||
-- fourth Discover candidate bucket that ranks unheard tracks by the
|
-- fourth Discover candidate bucket that ranks unheard tracks by the
|
||||||
-- user's taste-profile tag weights ("novelty that fits your vibe"), so
|
-- user's taste-profile tag weights ("novelty that fits your vibe"), so
|
||||||
-- its picks need a new pick_kind provenance value.
|
-- its picks need a new pick_kind provenance value.
|
||||||
--
|
--
|
||||||
-- Postgres CHECK whitelists can't be altered in place: DROP + re-ADD with
|
-- Postgres CHECK whitelists can't be altered in place: DROP + re-ADD with
|
||||||
-- the expanded list, in the same migration (family rule — new CHECK-enum
|
-- the expanded list, in the same migration.
|
||||||
-- value needs a same-change migration).
|
|
||||||
ALTER TABLE playlist_tracks
|
ALTER TABLE playlist_tracks
|
||||||
DROP CONSTRAINT playlist_tracks_pick_kind_check;
|
DROP CONSTRAINT playlist_tracks_pick_kind_check;
|
||||||
ALTER TABLE playlist_tracks
|
ALTER TABLE playlist_tracks
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- Track-level folksonomy tags for taste enrichment (milestone #160, #1490).
|
-- Track-level folksonomy tags for taste enrichment.
|
||||||
--
|
--
|
||||||
-- A global (not per-user) cache of style/mood tags fetched from
|
-- A global (not per-user) cache of style/mood tags fetched from
|
||||||
-- MusicBrainz (keyless) + Last.fm (when a key is configured), keyed by
|
-- MusicBrainz (keyless) + Last.fm (when a key is configured), keyed by
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- Tag-enrichment provider settings (milestone #160, #1490 Step 2).
|
-- Tag-enrichment provider settings.
|
||||||
--
|
--
|
||||||
-- Mirrors the cover-art provider-settings substrate (migration 0018) so
|
-- Mirrors the cover-art provider-settings substrate (migration 0018) so
|
||||||
-- adding a new tag source later is "register a provider + it auto-upserts
|
-- adding a new tag source later is "register a provider + it auto-upserts
|
||||||
@@ -28,8 +28,9 @@ CREATE TABLE tag_sources_meta (
|
|||||||
INSERT INTO tag_sources_meta (id, current_version) VALUES (true, 1);
|
INSERT INTO tag_sources_meta (id, current_version) VALUES (true, 1);
|
||||||
|
|
||||||
-- Seed the v1 providers. MusicBrainz is the keyless always-on default;
|
-- Seed the v1 providers. MusicBrainz is the keyless always-on default;
|
||||||
-- Last.fm ships disabled and is opt-in once the operator supplies a key
|
-- Last.fm ships disabled and is opt-in once an admin supplies a key in
|
||||||
-- (Step 4 Settings UI), per the no-coercive-settings rule (#26). The
|
-- Settings: a feature that needs a third-party account is never on by
|
||||||
|
-- default. The
|
||||||
-- SettingsService also upserts these at boot, so the seed is belt-and-
|
-- SettingsService also upserts these at boot, so the seed is belt-and-
|
||||||
-- suspenders / documents the intended defaults.
|
-- suspenders / documents the intended defaults.
|
||||||
INSERT INTO tag_provider_settings (provider_id, enabled, display_order) VALUES
|
INSERT INTO tag_provider_settings (provider_id, enabled, display_order) VALUES
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
-- Expose EnrichedTagScale in the tuning lab (#1520). Adds the enriched-tag
|
-- Expose EnrichedTagScale in the tuning lab. Adds the enriched-tag
|
||||||
-- weight knob to the taste_tuning singleton so operators can dial how much
|
-- weight knob to the taste_tuning singleton so an admin can dial how much
|
||||||
-- folksonomy tags (#1490) count vs raw ID3 genre. DEFAULT 0.5 backfills the
|
-- folksonomy tags count vs raw ID3 genre. DEFAULT 0.5 backfills the
|
||||||
-- existing row and matches taste.DefaultConfig().EnrichedTagScale.
|
-- existing row and matches taste.DefaultConfig().EnrichedTagScale.
|
||||||
ALTER TABLE taste_tuning
|
ALTER TABLE taste_tuning
|
||||||
ADD COLUMN enriched_tag_scale double precision NOT NULL DEFAULT 0.5;
|
ADD COLUMN enriched_tag_scale double precision NOT NULL DEFAULT 0.5;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
-- 0045_taste_profile_eras.up.sql — era/decade taste facet (#1530,
|
-- 0045_taste_profile_eras.up.sql — era/decade taste facet. A third taste
|
||||||
-- milestone #160 Opt 2). A third taste facet alongside artists + tags:
|
-- facet alongside artists + tags:
|
||||||
-- signed weights over decade buckets ("1990s") derived from
|
-- signed weights over decade buckets ("1990s") derived from
|
||||||
-- albums.release_date, rebuilt daily by internal/taste next to the
|
-- albums.release_date, rebuilt daily by internal/taste next to the
|
||||||
-- artist/tag facets. Weight is a signed float — positive = drawn to
|
-- artist/tag facets. Weight is a signed float — positive = drawn to
|
||||||
@@ -18,8 +18,8 @@ CREATE TABLE taste_profile_eras (
|
|||||||
CREATE INDEX taste_profile_eras_user_weight_idx
|
CREATE INDEX taste_profile_eras_user_weight_idx
|
||||||
ON taste_profile_eras (user_id, weight DESC);
|
ON taste_profile_eras (user_id, weight DESC);
|
||||||
|
|
||||||
-- Era-facet build knob, tunable in the lab (mirrors enriched_tag_scale,
|
-- Era-facet build knob, tunable in the lab (mirrors enriched_tag_scale):
|
||||||
-- #1520): scales how strongly a decade-play imprints on the profile.
|
-- scales how strongly a decade-play imprints on the profile.
|
||||||
-- DEFAULT 0.5 backfills the existing singleton and matches
|
-- DEFAULT 0.5 backfills the existing singleton and matches
|
||||||
-- taste.DefaultConfig().EraScale; 0 disables the era facet entirely.
|
-- taste.DefaultConfig().EraScale; 0 disables the era facet entirely.
|
||||||
ALTER TABLE taste_tuning
|
ALTER TABLE taste_tuning
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
-- 0046_context_time_weight.up.sql — time-of-day/weekday context conditioning
|
-- 0046_context_time_weight.up.sql — time-of-day/weekday context conditioning.
|
||||||
-- (#1531, milestone #160 Opt 3). Adds a per-profile scoring weight for the new
|
-- Adds a per-profile scoring weight for the new
|
||||||
-- context-affinity term: how strongly a candidate is boosted when its artist's
|
-- context-affinity term: how strongly a candidate is boosted when its artist's
|
||||||
-- play history concentrates in the current daypart × weekday-type cell (in the
|
-- play history concentrates in the current daypart × weekday-type cell (in the
|
||||||
-- user's local timezone). Mirrors the other ScoringWeights columns.
|
-- user's local timezone). Mirrors the other ScoringWeights columns.
|
||||||
@@ -7,6 +7,6 @@
|
|||||||
-- DEFAULT 1.0 backfills both existing profile rows to a modest on-value; the
|
-- DEFAULT 1.0 backfills both existing profile rows to a modest on-value; the
|
||||||
-- Go ShippedRadioWeights/ShippedDailyMixWeights carry the same 1.0 so fresh
|
-- Go ShippedRadioWeights/ShippedDailyMixWeights carry the same 1.0 so fresh
|
||||||
-- installs seed identically. Reconcile only seeds MISSING rows (ON CONFLICT DO
|
-- installs seed identically. Reconcile only seeds MISSING rows (ON CONFLICT DO
|
||||||
-- NOTHING), so existing rows rely on this DEFAULT until an operator resets.
|
-- NOTHING), so existing rows rely on this DEFAULT until an admin resets.
|
||||||
ALTER TABLE recommendation_weight_profiles
|
ALTER TABLE recommendation_weight_profiles
|
||||||
ADD COLUMN context_time_weight double precision NOT NULL DEFAULT 1.0;
|
ADD COLUMN context_time_weight double precision NOT NULL DEFAULT 1.0;
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
-- 0047_taste_profile_moods.up.sql — mood taste facet (#1534, milestone #160
|
-- 0047_taste_profile_moods.up.sql — mood taste facet. A fourth taste facet
|
||||||
-- Opt 2b). A fourth taste facet alongside artists + tags + eras: signed weights
|
-- alongside artists + tags + eras: signed weights over canonical mood buckets
|
||||||
-- over canonical mood buckets (melancholic / energetic / chill / …) derived
|
-- (melancholic / energetic / chill / …) derived from a track's enriched
|
||||||
-- from a track's enriched folksonomy tags (#1490) via the internal/mood
|
-- folksonomy tags via the internal/mood vocabulary, rebuilt daily. Weight is a
|
||||||
-- vocabulary, rebuilt daily. Weight is a signed float — positive = drawn to
|
-- signed float — positive = drawn to that mood, negative = passively avoided.
|
||||||
-- that mood, negative = passively avoided. Coverage is partial (grows with tag
|
-- Coverage is partial (grows with tag enrichment), so it's a supplement, not a
|
||||||
-- enrichment), so it's a supplement, not a foundation. Mirrors
|
-- foundation. Mirrors taste_profile_tags: CASCADE on user delete, indexed by
|
||||||
-- taste_profile_tags: CASCADE on user delete, indexed by (user_id, weight DESC).
|
-- (user_id, weight DESC).
|
||||||
CREATE TABLE taste_profile_moods (
|
CREATE TABLE taste_profile_moods (
|
||||||
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
mood text NOT NULL,
|
mood text NOT NULL,
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
-- 0048_play_events_device_class.up.sql — device-class context conditioning
|
-- 0048_play_events_device_class.up.sql — device-class context conditioning.
|
||||||
-- (#1551, milestone #160 Opt 3b). Records which class of device a play came
|
-- Records which class of device a play came from (mobile / web / …), sent by
|
||||||
-- from (mobile / web / …), sent by the client on play-start and normalized
|
-- the client on play-start and normalized server-side. Extends the
|
||||||
-- server-side. Extends the #1531 time-of-day/weekday affinity cell to
|
-- time-of-day/weekday affinity cell (0046) to (daypart × weekday × device) for
|
||||||
-- (daypart × weekday × device) for the radio path. Nullable + no CHECK: values
|
-- the radio path. Nullable + no CHECK: values are normalized in Go, so a new
|
||||||
-- are normalized in Go (avoids a CHECK-migration each time a client class is
|
-- client class needs no migration, and historical rows stay NULL → excluded
|
||||||
-- added, rule #36), and historical rows stay NULL → excluded from the device
|
-- from the device dimension (a graceful cold-start; the facet ramps as new
|
||||||
-- dimension (a graceful cold-start; the facet ramps as new plays land).
|
-- plays land).
|
||||||
ALTER TABLE play_events ADD COLUMN device_class text;
|
ALTER TABLE play_events ADD COLUMN device_class text;
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
-- 0049_suggestion_snoozes.up.sql — time-boxed "not right now" on a Discover
|
-- 0049_suggestion_snoozes.up.sql — time-boxed "not right now" on a Discover
|
||||||
-- artist suggestion (#2374, milestone #268 slice 3).
|
-- artist suggestion.
|
||||||
--
|
--
|
||||||
-- This is NOT a dislike. Rule #101 forbids a "Not for me" / thumbs-down /
|
-- This is NOT a dislike. Minstrel has no "Not for me" / thumbs-down /
|
||||||
-- exclusion UI, and a snooze deliberately isn't one: it records no verdict on
|
-- exclusion control — taste is learned from listening — and a snooze
|
||||||
|
-- deliberately isn't one: it records no verdict on
|
||||||
-- the music, expires on its own, and MUST NEVER feed the taste profile. It is
|
-- the music, expires on its own, and MUST NEVER feed the taste profile. It is
|
||||||
-- acquisition triage — "I don't want to request this right now" — so the same
|
-- acquisition triage — "I don't want to request this right now" — so the same
|
||||||
-- candidate is free to return once snoozed_until passes. Anything that reads
|
-- candidate is free to return once snoozed_until passes. Anything that reads
|
||||||
-- this table as negative preference signal is a bug.
|
-- this table as negative preference signal is a bug.
|
||||||
--
|
--
|
||||||
-- Per-user (rule #47), never global: one household member parking a
|
-- Per-user, never global: one household member parking a
|
||||||
-- suggestion must not remove it from anyone else's deck.
|
-- suggestion must not remove it from anyone else's deck.
|
||||||
--
|
--
|
||||||
-- candidate_mbid is text with NO foreign key, on purpose. Suggestions come
|
-- candidate_mbid is text with NO foreign key, on purpose. Suggestions come
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
-- 0050_candidate_artist_tags.up.sql — folksonomy tags for OUT-OF-LIBRARY
|
-- 0050_candidate_artist_tags.up.sql — folksonomy tags for OUT-OF-LIBRARY
|
||||||
-- artists (#2376, milestone #268 slice 5).
|
-- artists.
|
||||||
--
|
--
|
||||||
-- track_tags (0042) cannot hold these: it is FK'd to tracks(id), and a
|
-- track_tags (0042) cannot hold these: it is FK'd to tracks(id), and a
|
||||||
-- Discover candidate has no local row by definition. So this is a parallel
|
-- Discover candidate has no local row by definition. So this is a parallel
|
||||||
-- cache keyed by the candidate's MusicBrainz MBID — the only stable identity
|
-- cache keyed by the candidate's MusicBrainz MBID — the only stable identity
|
||||||
-- available for an artist we don't have.
|
-- available for an artist we don't have.
|
||||||
--
|
--
|
||||||
-- Purpose is slice 6: rank suggestions by overlap between these tags and the
|
-- Purpose: rank Discover suggestions by overlap between these tags and the
|
||||||
-- user's taste_profile_tags, turning "neighbour of an artist you play" into
|
-- user's taste_profile_tags, turning "neighbour of an artist you play" into
|
||||||
-- "matches the sound you like".
|
-- "matches the sound you like".
|
||||||
--
|
--
|
||||||
-- GLOBAL, not per-user: a candidate's tags are a property of the artist, not
|
-- GLOBAL, not per-user: a candidate's tags are a property of the artist, not
|
||||||
-- of anyone's taste. Nothing here is user-scoped, so rule #47 has nothing to
|
-- of anyone's taste. The per-user part lives entirely in the ranking.
|
||||||
-- scope — the per-user part lives entirely in slice 6's ranking.
|
|
||||||
--
|
--
|
||||||
-- weight is a normalized folksonomy strength in [0,1], same scale as
|
-- weight is a normalized folksonomy strength in [0,1], same scale as
|
||||||
-- track_tags, so the two can be compared without a conversion step.
|
-- track_tags, so the two can be compared without a conversion step.
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
-- 0051_discover_tuning.up.sql — tunable knobs for the Discover request
|
-- 0051_discover_tuning.up.sql — tunable knobs for the Discover request
|
||||||
-- surface (#2377, milestone #268 slice 6).
|
-- surface.
|
||||||
--
|
--
|
||||||
-- A FOURTH tuning scope alongside radio / daily_mix / taste. Its own scope
|
-- A FOURTH tuning scope alongside radio / daily_mix / taste. Its own scope
|
||||||
-- rather than extra columns on taste_tuning, for a reason that matters:
|
-- rather than extra columns on taste_tuning, for a reason that matters:
|
||||||
-- snooze_days lives here, and a snooze must never be read as taste signal
|
-- snooze_days lives here, and a snooze must never be read as taste signal
|
||||||
-- (#2374). Filing it under 'taste' would put it one careless join away from
|
-- (0049). Filing it under 'taste' would put it one careless join away from
|
||||||
-- exactly the leak that design forbids.
|
-- exactly that leak.
|
||||||
--
|
--
|
||||||
-- Per rule #25 these are DB-backed and editable in the admin UI with no
|
-- These are DB-backed and editable in the admin UI with no restart — the
|
||||||
-- restart — the shipped values below are defaults, not settings.
|
-- shipped values below are defaults, not settings.
|
||||||
CREATE TABLE discover_tuning (
|
CREATE TABLE discover_tuning (
|
||||||
singleton boolean PRIMARY KEY DEFAULT true
|
singleton boolean PRIMARY KEY DEFAULT true
|
||||||
CONSTRAINT discover_tuning_singleton_check CHECK (singleton),
|
CONSTRAINT discover_tuning_singleton_check CHECK (singleton),
|
||||||
@@ -17,10 +17,10 @@ CREATE TABLE discover_tuning (
|
|||||||
-- [0,1]. So 0 disables the feature outright and leaves pure similarity
|
-- [0,1]. So 0 disables the feature outright and leaves pure similarity
|
||||||
-- ranking, 1.0 lets a perfectly-matching candidate double its score, and
|
-- ranking, 1.0 lets a perfectly-matching candidate double its score, and
|
||||||
-- a candidate with no cached tags is unchanged rather than penalised
|
-- a candidate with no cached tags is unchanged rather than penalised
|
||||||
-- (rule #131 — tag coverage is permanently partial, see #2376).
|
-- (tag coverage is permanently partial, so a missing tag is no signal).
|
||||||
tag_overlap_weight double precision NOT NULL,
|
tag_overlap_weight double precision NOT NULL,
|
||||||
-- Default snooze duration in days. Was a Go constant in
|
-- Default snooze duration in days. Before this a Go constant in
|
||||||
-- internal/api/suggestions.go; moved here per rule #25.
|
-- internal/api/suggestions.go; now an admin setting.
|
||||||
snooze_days double precision NOT NULL,
|
snooze_days double precision NOT NULL,
|
||||||
updated_at timestamptz NOT NULL DEFAULT now()
|
updated_at timestamptz NOT NULL DEFAULT now()
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- Session provenance for the active-sessions surface (#370).
|
-- Session provenance for the active-sessions surface.
|
||||||
--
|
--
|
||||||
-- TWO addresses, not one, and the pair is the point: a session created at
|
-- TWO addresses, not one, and the pair is the point: a session created at
|
||||||
-- home and now being used from somewhere else is the shape of a stolen
|
-- home and now being used from somewhere else is the shape of a stolen
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- Trusted reverse-proxy depth for client-IP extraction (#2453).
|
-- Trusted reverse-proxy depth for client-IP extraction.
|
||||||
--
|
--
|
||||||
-- X-Forwarded-For grows left-to-right: each proxy APPENDS the peer it
|
-- X-Forwarded-For grows left-to-right: each proxy APPENDS the peer it
|
||||||
-- received the request from. For client -> CDN -> own-proxy -> Minstrel the
|
-- received the request from. For client -> CDN -> own-proxy -> Minstrel the
|
||||||
@@ -11,14 +11,15 @@
|
|||||||
--
|
--
|
||||||
-- Default 1: a publicly reachable Minstrel needs a TLS terminator in front of
|
-- Default 1: a publicly reachable Minstrel needs a TLS terminator in front of
|
||||||
-- it, and recording that terminator's own address for every session makes the
|
-- it, and recording that terminator's own address for every session makes the
|
||||||
-- active-sessions surface (#370) useless — created_ip and last_ip would both
|
-- active-sessions surface useless — created_ip and last_ip would both
|
||||||
-- be the proxy, so the "address changed" signal could never fire.
|
-- be the proxy, so the "address changed" signal could never fire.
|
||||||
--
|
--
|
||||||
-- The cost, stated on the admin card rather than buried: hops >= 1 DECLARES
|
-- The cost, stated on the admin card rather than buried: hops >= 1 DECLARES
|
||||||
-- that a proxy exists. If one doesn't, a client can forge X-Forwarded-For and
|
-- that a proxy exists. If one doesn't, a client can forge X-Forwarded-For and
|
||||||
-- choose what its own session row shows, which defeats exactly the compromise
|
-- choose what its own session row shows, which defeats exactly the compromise
|
||||||
-- detection #370 exists for. That is inherent to the trusted-hop model, which
|
-- detection the active-sessions surface exists for. That is inherent to the
|
||||||
-- is why 0 is a first-class setting and not a hidden escape hatch.
|
-- trusted-hop model, which is why 0 is a first-class setting and not a hidden
|
||||||
|
-- escape hatch.
|
||||||
--
|
--
|
||||||
-- Upper bound 10 guards a typo turning into "trust the whole header"; no real
|
-- Upper bound 10 guards a typo turning into "trust the whole header"; no real
|
||||||
-- deployment chains ten proxies.
|
-- deployment chains ten proxies.
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
-- Records which version of the scanner's tag-extraction logic last wrote a
|
-- Records which version of the scanner's tag-extraction logic last wrote a
|
||||||
-- track's tag-derived columns (#2499).
|
-- track's tag-derived columns.
|
||||||
--
|
--
|
||||||
-- DEFAULT 0 is the point of this migration: every existing row lands below the
|
-- DEFAULT 0 is the point of this migration: every existing row lands below the
|
||||||
-- scanner's current library.tagReadVersion, so the next scan re-reads its tags
|
-- scanner's current library.tagReadVersion, so the next scan re-reads its tags
|
||||||
-- instead of short-circuiting on the mtime check. That repairs genre values the
|
-- instead of short-circuiting on the mtime check. That repairs genre values the
|
||||||
-- old reader welded together ("Alternative Rock" + "Rock" -> "Alternative
|
-- old reader welded together ("Alternative Rock" + "Rock" -> "Alternative
|
||||||
-- RockRock") without asking the operator to wipe and rebuild the library.
|
-- RockRock") without anyone wiping and rebuilding the library.
|
||||||
--
|
--
|
||||||
-- Bump library.tagReadVersion in Go — not this default — whenever a tag
|
-- Bump library.tagReadVersion in Go — not this default — whenever a tag
|
||||||
-- extraction fix needs to reach already-indexed files. That makes tag repairs a
|
-- extraction fix needs to reach already-indexed files. That makes tag repairs a
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- Marks a track whose file the scanner could no longer find (#2523).
|
-- Marks a track whose file the scanner could no longer find.
|
||||||
--
|
--
|
||||||
-- NULL means present. A timestamp means the file was absent as of that scan,
|
-- NULL means present. A timestamp means the file was absent as of that scan,
|
||||||
-- and is the point from which "how long has this been gone" is measured — which
|
-- and is the point from which "how long has this been gone" is measured — which
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
-- Auto re-acquisition of missing files via Lidarr (#2527 slice 3, milestone
|
-- Auto re-acquisition of missing files via Lidarr. Before this a missing
|
||||||
-- #290). A file going missing has been a dead end until now: reconcile marks
|
-- file was a dead end: reconcile marks it, every selection path skips it,
|
||||||
-- it (#2523), every selection path skips it, the admin surface lists it, and
|
-- the admin surface lists it, and there it sits.
|
||||||
-- there it sits.
|
|
||||||
--
|
--
|
||||||
-- The unit here is the ALBUM, not the track, and that is the design decision
|
-- The unit here is the ALBUM, not the track, and that is the design decision
|
||||||
-- carrying most of the safety. Lidarr acquires releases; there is no
|
-- carrying most of the safety. Lidarr acquires releases; there is no
|
||||||
-- meaningful "fetch me one track" operation, and a track-kind request needs a
|
-- meaningful "fetch me one track" operation, and a track-kind request needs a
|
||||||
-- recording MBID plenty of files simply don't have. Grouping by album means
|
-- recording MBID plenty of files simply don't have. Grouping by album means
|
||||||
-- the case that produced #2523 -- three reorganised albums, ~40 missing files
|
-- a reorganised album with dozens of missing files becomes one request.
|
||||||
-- -- becomes three requests instead of forty.
|
|
||||||
|
|
||||||
CREATE TABLE missing_reacquisitions (
|
CREATE TABLE missing_reacquisitions (
|
||||||
album_id uuid PRIMARY KEY REFERENCES albums (id) ON DELETE CASCADE,
|
album_id uuid PRIMARY KEY REFERENCES albums (id) ON DELETE CASCADE,
|
||||||
@@ -36,12 +34,13 @@ CREATE INDEX missing_reacquisitions_due_idx
|
|||||||
WHERE gave_up_at IS NULL;
|
WHERE gave_up_at IS NULL;
|
||||||
|
|
||||||
-- Settings, singleton in the style of network_settings (0053). Every value an
|
-- Settings, singleton in the style of network_settings (0053). Every value an
|
||||||
-- operator might want to tune lives here rather than in YAML (rule #25).
|
-- admin might want to tune lives here rather than in YAML, changed without a
|
||||||
|
-- restart.
|
||||||
CREATE TABLE reacquisition_settings (
|
CREATE TABLE reacquisition_settings (
|
||||||
id boolean PRIMARY KEY DEFAULT true,
|
id boolean PRIMARY KEY DEFAULT true,
|
||||||
|
|
||||||
-- Master switch. Default true: the operator asked for this to happen by
|
-- Master switch. Default true: this is meant to happen by itself, and a
|
||||||
-- itself, and a feature that ships switched off is a feature nobody finds.
|
-- feature that ships switched off is a feature nobody finds.
|
||||||
enabled boolean NOT NULL DEFAULT true,
|
enabled boolean NOT NULL DEFAULT true,
|
||||||
|
|
||||||
-- How long a file must have been missing before the FIRST attempt. This
|
-- How long a file must have been missing before the FIRST attempt. This
|
||||||
@@ -49,7 +48,7 @@ CREATE TABLE reacquisition_settings (
|
|||||||
-- transiently -- an unmounted volume, a container that started before its
|
-- transiently -- an unmounted volume, a container that started before its
|
||||||
-- media mount attached, a NAS mid-reboot -- and every one of those
|
-- media mount attached, a NAS mid-reboot -- and every one of those
|
||||||
-- resolves itself well inside a day at no cost. tracks.missing_since is
|
-- resolves itself well inside a day at no cost. tracks.missing_since is
|
||||||
-- never re-stamped (#2523), so it is a true "gone since" clock to measure
|
-- never re-stamped, so it is a true "gone since" clock to measure
|
||||||
-- against.
|
-- against.
|
||||||
grace_hours int NOT NULL DEFAULT 24,
|
grace_hours int NOT NULL DEFAULT 24,
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
-- 0057_songs_like_tuning.up.sql — a THIRD weight profile, for Songs-like
|
-- 0057_songs_like_tuning.up.sql — a THIRD weight profile, for Songs-like.
|
||||||
-- (Scribe #3881, milestone #398).
|
|
||||||
--
|
--
|
||||||
-- Songs-like shared the `daily_mix` profile with For-You, and that is the bug.
|
-- Before: Songs-like shared the `daily_mix` profile with For-You. The two
|
||||||
-- The two surfaces want opposite things: For-You is a broad "what will they
|
-- surfaces want opposite things: For-You is a broad "what will they enjoy
|
||||||
-- enjoy today", Songs-like answers "what sounds like THIS", and under one set
|
-- today", Songs-like answers "what sounds like THIS" and should stay in one
|
||||||
-- of weights the broad answer wins. Operator, 2026-09-10: "I'm expecting to
|
-- neighbourhood of sound, and under one set of weights the broad answer wins.
|
||||||
-- get a consistent sound and style from the experience... I was getting a
|
|
||||||
-- seeming wide variety of music from each one when I was hoping to stay in a
|
|
||||||
-- certain neighborhood."
|
|
||||||
--
|
--
|
||||||
-- Under the shared daily_mix weights, an UNRELATED track the user had liked
|
-- Under the shared daily_mix weights, an UNRELATED track the user had liked
|
||||||
-- and not played recently scored 1.0 + 2.0 + 1.0 = 4.0 before taste, while a
|
-- and not played recently scored 1.0 + 2.0 + 1.0 = 4.0 before taste, while a
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
-- 0058_track_fingerprints.up.sql — an acoustic identity per track (Scribe
|
-- 0058_track_fingerprints.up.sql — an acoustic identity per track.
|
||||||
-- milestone #400: #3905, #3906).
|
|
||||||
--
|
--
|
||||||
-- A table of its own rather than columns on tracks, for the hot path's sake:
|
-- 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,
|
-- tracks is read with SELECT * by eight queries, among them ListTracksByAlbum,
|
||||||
@@ -16,9 +15,9 @@
|
|||||||
-- A failure that says nothing about the file — a timeout, a cancelled scan, a
|
-- 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.
|
-- missing binary — writes no row at all, so the backfill tries again.
|
||||||
CREATE TABLE track_fingerprints (
|
CREATE TABLE track_fingerprints (
|
||||||
-- CASCADE is right here, unlike for the likes and play history M400's
|
-- CASCADE is right here, unlike for the likes and play history the
|
||||||
-- merge has to carry across: a fingerprint describes one file's bytes and
|
-- duplicate merge has to carry across: a fingerprint describes one file's
|
||||||
-- means nothing once that file's row is gone.
|
-- bytes and means nothing once that file's row is gone.
|
||||||
track_id uuid PRIMARY KEY REFERENCES tracks (id) ON DELETE CASCADE,
|
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
|
-- SHA-256 of the ENCODED audio packets (ffmpeg -c:a copy -f hash), not of
|
||||||
-- decoded samples. internal/library/fingerprint.go says why.
|
-- decoded samples. internal/library/fingerprint.go says why.
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
-- 0059_duplicate_groups.up.sql — proposed duplicates and the sweeps that find
|
-- 0059_duplicate_groups.up.sql — proposed duplicates and the sweeps that find
|
||||||
-- them (Scribe milestone #400: #3910).
|
-- them.
|
||||||
--
|
--
|
||||||
-- The sweep compares fingerprints (track_fingerprints, 0058) and proposes groups
|
-- The sweep compares fingerprints (track_fingerprints, 0058) and proposes
|
||||||
-- of tracks that hold one recording. Nothing here merges anything: a group is a
|
-- groups of tracks that hold one recording. Nothing here merges anything: a
|
||||||
-- proposal the operator reviews, and the merge (#3911) is a separate act.
|
-- group is a proposal an admin reviews, and the merge is a separate act.
|
||||||
|
|
||||||
-- One row per sweep. Lets the report tell "the sweep has never run" apart from
|
-- One row per sweep. Lets the report tell "the sweep has never run" apart from
|
||||||
-- "it ran and found nothing", and gives the in-flight guard something to check,
|
-- "it ran and found nothing", and gives the in-flight guard something to check,
|
||||||
@@ -49,6 +49,6 @@ CREATE TABLE duplicate_group_members (
|
|||||||
CREATE INDEX duplicate_group_members_track_idx ON duplicate_group_members (track_id);
|
CREATE INDEX duplicate_group_members_track_idx ON duplicate_group_members (track_id);
|
||||||
|
|
||||||
-- The sweep streams candidates in (duration_ms, id) order, keyset-paged, so it
|
-- The sweep streams candidates in (duration_ms, id) order, keyset-paged, so it
|
||||||
-- only ever holds a few seconds' worth of durations in memory. Without this each
|
-- only ever holds a few seconds' worth of durations in memory. Without this
|
||||||
-- page would sort the whole library again.
|
-- each page would sort the whole library again.
|
||||||
CREATE INDEX tracks_duration_id_idx ON tracks (duration_ms, id);
|
CREATE INDEX tracks_duration_id_idx ON tracks (duration_ms, id);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
-- 0060_play_events_track_index.up.sql — play_events by track (Scribe #3912, #3911).
|
-- 0060_play_events_track_index.up.sql — play_events by track.
|
||||||
--
|
--
|
||||||
-- play_events is indexed by (user_id, started_at) and (user_id, track_id), both
|
-- play_events is indexed by (user_id, started_at) and (user_id, track_id), both
|
||||||
-- led by user. Nothing reached it by track alone until the duplicates report,
|
-- led by user. Nothing reached it by track alone until the duplicates report,
|
||||||
-- which shows each copy's play count — a scan of the whole table per copy — and
|
-- which shows each copy's play count — a scan of the whole table per copy — and
|
||||||
-- the merge (#3911), which repoints a duplicate's play history onto the copy
|
-- the duplicate merge, which repoints a duplicate's play history onto the copy
|
||||||
-- being kept. Both ask "every play of this track", whoever played it.
|
-- being kept. Both ask "every play of this track", whoever played it.
|
||||||
CREATE INDEX play_events_track_idx ON play_events (track_id);
|
CREATE INDEX play_events_track_idx ON play_events (track_id);
|
||||||
|
|||||||
@@ -1,20 +1,18 @@
|
|||||||
-- 0061_fingerprint_settings.up.sql — fingerprinting's knobs, in admin Settings
|
-- 0061_fingerprint_settings.up.sql — fingerprinting's knobs, in admin
|
||||||
-- (Scribe #3913, milestone #400). Rule 25: anything an operator might tune is a
|
-- Settings: anything an admin might tune is a database row, changed without a
|
||||||
-- database row, changed without a restart. Singleton in the style of
|
-- restart. Singleton in the style of reacquisition_settings (0056).
|
||||||
-- reacquisition_settings (0056).
|
|
||||||
CREATE TABLE fingerprint_settings (
|
CREATE TABLE fingerprint_settings (
|
||||||
id boolean PRIMARY KEY DEFAULT true,
|
id boolean PRIMARY KEY DEFAULT true,
|
||||||
|
|
||||||
-- Fingerprinting new files, the backfill, and the duplicate sweep. Off stops
|
-- Fingerprinting new files, the backfill, and the duplicate sweep. Off
|
||||||
-- the decode work entirely — the reason to turn it off is a slow NAS, and
|
-- stops the decode work entirely — the reason to turn it off is a slow NAS.
|
||||||
-- that is the operator's call. On by default: a library that cannot tell its
|
-- On by default: without it the library cannot tell its duplicates apart.
|
||||||
-- duplicates apart is what milestone #400 exists to end.
|
|
||||||
enabled boolean NOT NULL DEFAULT true,
|
enabled boolean NOT NULL DEFAULT true,
|
||||||
|
|
||||||
-- Seconds of audio fpcalc fingerprints. Chromaprints taken at different
|
-- Seconds of audio fpcalc fingerprints. Chromaprints taken at different
|
||||||
-- lengths cannot be compared, which is why track_fingerprints records the
|
-- lengths cannot be compared, which is why track_fingerprints records the
|
||||||
-- length each row was taken at (below): change this and every chromaprint is
|
-- length each row was taken at (below): change this and every chromaprint
|
||||||
-- re-derived, and until then only rows at the new length are compared.
|
-- is re-derived, and until then only rows at the new length are compared.
|
||||||
chromaprint_length_sec integer NOT NULL DEFAULT 120,
|
chromaprint_length_sec integer NOT NULL DEFAULT 120,
|
||||||
|
|
||||||
-- The most disagreement two aligned fingerprints may show and still be
|
-- The most disagreement two aligned fingerprints may show and still be
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
-- The address users reach Minstrel at, e.g. https://music.example.com.
|
-- The address users reach Minstrel at, e.g. https://music.example.com.
|
||||||
--
|
--
|
||||||
-- Password-reset links used to be built from the request's Host header,
|
-- Before: password-reset links were built from the request's Host header,
|
||||||
-- which the requester controls: a forgot-password call with a forged Host
|
-- which the requester controls, so a forgot-password call with a forged Host
|
||||||
-- would email the victim a real reset token on a link to the attacker's
|
-- would email the victim a real reset token on a link to the attacker's
|
||||||
-- server. Links are now built only from this operator-set value, and none
|
-- server. After: links are built only from this admin-set value, and none
|
||||||
-- are sent while it is empty (M462 #4981).
|
-- are sent while it is empty.
|
||||||
ALTER TABLE network_settings ADD COLUMN public_url text NOT NULL DEFAULT '';
|
ALTER TABLE network_settings ADD COLUMN public_url text NOT NULL DEFAULT '';
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
-- same way session tokens are. A leaked database row or backup no longer
|
-- same way session tokens are. A leaked database row or backup no longer
|
||||||
-- hands out working keys. Existing keys are hashed in place, so every
|
-- hands out working keys. Existing keys are hashed in place, so every
|
||||||
-- Subsonic client keeps working; the key itself can no longer be shown
|
-- Subsonic client keeps working; the key itself can no longer be shown
|
||||||
-- again, only replaced (M462 #4983).
|
-- again, only replaced.
|
||||||
ALTER TABLE users ADD COLUMN api_token_hash text;
|
ALTER TABLE users ADD COLUMN api_token_hash text;
|
||||||
UPDATE users SET api_token_hash = encode(sha256(convert_to(api_token, 'UTF8')), 'hex');
|
UPDATE users SET api_token_hash = encode(sha256(convert_to(api_token, 'UTF8')), 'hex');
|
||||||
ALTER TABLE users ALTER COLUMN api_token_hash SET NOT NULL;
|
ALTER TABLE users ALTER COLUMN api_token_hash SET NOT NULL;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
-- `minstrel admin reset-password` used to copy the new login password into
|
-- Before: `minstrel admin reset-password` copied the new login password into
|
||||||
-- subsonic_password, so every account recovered through the CLI had its login
|
-- subsonic_password, so every account recovered through the CLI had its login
|
||||||
-- password stored in plain text, and a later password change in Settings left
|
-- password stored in plain text, and a later password change in Settings left
|
||||||
-- that copy behind (M462 #5026). The CLI no longer writes this column; a
|
-- that copy behind. After: the CLI does not write this column; a Subsonic
|
||||||
-- Subsonic password is now generated separately in Settings and is never the
|
-- password is generated separately in Settings and is never the login
|
||||||
-- login password. Clearing every value here removes the copies already made.
|
-- password. Clearing every value here removes the copies already made.
|
||||||
--
|
--
|
||||||
-- Accounts whose Subsonic client signs in with t/s stop working until the user
|
-- Accounts whose Subsonic client signs in with t/s stop working until the user
|
||||||
-- generates a Subsonic password (or switches the client to an API key).
|
-- generates a Subsonic password (or switches the client to an API key).
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
-- 0065_track_loudness.up.sql — measured loudness per track, for loudness
|
-- 0065_track_loudness.up.sql — measured loudness per track, for loudness
|
||||||
-- normalization (Scribe milestone #464, #4995).
|
-- normalization.
|
||||||
--
|
--
|
||||||
-- Measured with ffmpeg's EBU R128 filter rather than read from ReplayGain tags:
|
-- Measured with ffmpeg's EBU R128 filter rather than read from ReplayGain tags:
|
||||||
-- tags in the wild are written against four different reference levels, and
|
-- tags in the wild are written against four different reference levels, and
|
||||||
@@ -32,9 +32,9 @@ CREATE TABLE track_loudness (
|
|||||||
loudness_range_lu real,
|
loudness_range_lu real,
|
||||||
-- How many 400 ms gating blocks fell in each 0.1 LU bin. Bin i holds blocks
|
-- How many 400 ms gating blocks fell in each 0.1 LU bin. Bin i holds blocks
|
||||||
-- measuring -70.0 + (block_hist_start + i) / 10 LUFS; the array is trimmed
|
-- measuring -70.0 + (block_hist_start + i) / 10 LUFS; the array is trimmed
|
||||||
-- to the first and last non-empty bins. Album loudness is the gated loudness
|
-- to the first and last non-empty bins. Album loudness is the gated
|
||||||
-- of every block on the album, so it is computed from these exactly, with no
|
-- loudness of every block on the album, so it is computed from these
|
||||||
-- second decode (#4996).
|
-- exactly, with no second decode.
|
||||||
block_hist_start smallint,
|
block_hist_start smallint,
|
||||||
block_hist integer[],
|
block_hist integer[],
|
||||||
unreadable boolean NOT NULL DEFAULT false,
|
unreadable boolean NOT NULL DEFAULT false,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
-- 0066_album_loudness.up.sql — album loudness for album-mode normalization
|
-- 0066_album_loudness.up.sql — album loudness for album-mode normalization.
|
||||||
-- (Scribe milestone #464, #4996).
|
|
||||||
--
|
--
|
||||||
-- An album's loudness is the gated loudness of every 400 ms block on the album,
|
-- An album's loudness is the gated loudness of every 400 ms block on the album,
|
||||||
-- not an average of its tracks' values: a quiet interlude and a loud single
|
-- not an average of its tracks' values: a quiet interlude and a loud single
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- Each user's loudness-normalization preference (M464 #4998). It is kept on
|
-- Each user's loudness-normalization preference. It is kept on
|
||||||
-- the server, not per device, so the web player, the Android app and the
|
-- the server, not per device, so the web player, the Android app and the
|
||||||
-- leveled stream for UPnP/Sonos all apply the same one.
|
-- leveled stream for UPnP/Sonos all apply the same one.
|
||||||
--
|
--
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
-- 0068_leveled_stream_cache.up.sql — the size cap of the leveled-stream render
|
-- 0068_leveled_stream_cache.up.sql — the size cap of the leveled-stream render
|
||||||
-- cache (Scribe milestone #464, #5001).
|
-- cache.
|
||||||
--
|
--
|
||||||
-- Sonos and other UPnP renderers fetch a track's URL themselves, so the phone
|
-- Sonos and other UPnP renderers fetch a track's URL themselves, so the phone
|
||||||
-- cannot level what they play. The server renders a gain-applied FLAC for them
|
-- cannot level what they play. The server renders a gain-applied FLAC for them
|
||||||
-- instead and keeps it on disk, oldest-used first out once the cache is over
|
-- instead and keeps it on disk, oldest-used first out once the cache is over
|
||||||
-- this size. An operator setting (rule 25), on the loudness settings row it
|
-- this size. An admin setting, on the loudness settings row it belongs with.
|
||||||
-- belongs with.
|
|
||||||
ALTER TABLE loudness_settings
|
ALTER TABLE loudness_settings
|
||||||
ADD COLUMN leveled_cache_mb integer NOT NULL DEFAULT 2048,
|
ADD COLUMN leveled_cache_mb integer NOT NULL DEFAULT 2048,
|
||||||
ADD CONSTRAINT loudness_settings_leveled_cache_range
|
ADD CONSTRAINT loudness_settings_leveled_cache_range
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
-- 0069_acoustid_lookup.up.sql — recording MBIDs looked up through AcoustID
|
-- 0069_acoustid_lookup.up.sql — recording MBIDs looked up through AcoustID
|
||||||
-- for tracks whose tags carry none (Scribe milestone #401).
|
-- for tracks whose tags carry none.
|
||||||
--
|
--
|
||||||
-- A track with no recording MBID is invisible to the ListenBrainz similarity
|
-- A track with no recording MBID is invisible to the ListenBrainz similarity
|
||||||
-- arm in both directions, so this fills the ids the tags leave empty.
|
-- arm in both directions, so this fills the ids the tags leave empty.
|
||||||
|
|
||||||
-- Where tracks.mbid came from. The file's own tag always outranks a lookup
|
-- Where tracks.mbid came from. The file's own tag always outranks a lookup:
|
||||||
-- (M401 D4): the scan's upsert keeps a looked-up id while the tag carries
|
-- the scan's upsert keeps a looked-up id while the tag carries
|
||||||
-- none, and replaces it the moment the tag carries one.
|
-- none, and replaces it the moment the tag carries one.
|
||||||
ALTER TABLE tracks ADD COLUMN mbid_source text;
|
ALTER TABLE tracks ADD COLUMN mbid_source text;
|
||||||
-- Every id stored so far was read from a tag (the scan or the tag backfill),
|
-- Every id stored so far was read from a tag (the scan or the tag backfill),
|
||||||
@@ -25,7 +25,7 @@ ALTER TABLE tracks
|
|||||||
-- after disambiguating by title and length; recording_mbid is it
|
-- after disambiguating by title and length; recording_mbid is it
|
||||||
-- ambiguous several recordings cleared it and nothing told them apart.
|
-- ambiguous several recordings cleared it and nothing told them apart.
|
||||||
-- Nothing is written: a wrong MBID would feed similarity the
|
-- Nothing is written: a wrong MBID would feed similarity the
|
||||||
-- wrong neighbours, which is worse than none (M401 D5)
|
-- wrong neighbours, which is worse than none
|
||||||
-- no_match AcoustID linked nothing at or above the threshold
|
-- no_match AcoustID linked nothing at or above the threshold
|
||||||
-- failed fpcalc rejected the file, or AcoustID rejected its print
|
-- failed fpcalc rejected the file, or AcoustID rejected its print
|
||||||
-- Every state is settled until the file changes. A failure that says nothing
|
-- Every state is settled until the file changes. A failure that says nothing
|
||||||
@@ -50,12 +50,12 @@ CREATE TABLE track_acoustid_lookups (
|
|||||||
);
|
);
|
||||||
CREATE INDEX track_acoustid_lookups_state ON track_acoustid_lookups (state);
|
CREATE INDEX track_acoustid_lookups_state ON track_acoustid_lookups (state);
|
||||||
|
|
||||||
-- AcoustID's knobs, in admin Settings (rule 25). Singleton in the style of
|
-- AcoustID's knobs, in admin Settings. Singleton in the style of
|
||||||
-- loudness_settings (0065).
|
-- loudness_settings (0065).
|
||||||
CREATE TABLE acoustid_settings (
|
CREATE TABLE acoustid_settings (
|
||||||
id boolean PRIMARY KEY DEFAULT true,
|
id boolean PRIMARY KEY DEFAULT true,
|
||||||
-- Off by default (rule 26): AcoustID needs a key the operator registers,
|
-- Off by default: AcoustID needs a key an admin registers, and the lookup
|
||||||
-- and the lookup sends fingerprints to a third party.
|
-- sends fingerprints to a third party.
|
||||||
enabled boolean NOT NULL DEFAULT false,
|
enabled boolean NOT NULL DEFAULT false,
|
||||||
api_key text,
|
api_key text,
|
||||||
-- The score a recording needs before it is considered at all. AcoustID
|
-- The score a recording needs before it is considered at all. AcoustID
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- MusicBrainz release-group id for an album (#5241). albums.mbid holds the
|
-- MusicBrainz release-group id for an album. albums.mbid holds the
|
||||||
-- *release* id (Picard's musicbrainz_albumid: one edition), but Lidarr names
|
-- *release* id (Picard's musicbrainz_albumid: one edition), but Lidarr names
|
||||||
-- albums by release group, so re-acquisition and request completion need
|
-- albums by release group, so re-acquisition and request completion need
|
||||||
-- this one. Not unique: several releases of one group can each be an album
|
-- this one. Not unique: several releases of one group can each be an album
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
-- #5263: the reconciler completed album and track requests as soon as the album
|
-- Before: the reconciler completed album and track requests as soon as the
|
||||||
-- had any track on disk. Re-acquisition targets albums with only SOME tracks
|
-- album had any track on disk. Re-acquisition targets albums with only SOME
|
||||||
-- missing, so those requests completed the moment Lidarr accepted the add,
|
-- tracks missing, so those requests completed the moment Lidarr accepted the
|
||||||
-- with nothing downloaded.
|
-- add, with nothing downloaded.
|
||||||
--
|
--
|
||||||
-- Reopen every completed album/track request whose matched album does not meet
|
-- Reopen every completed album/track request whose matched album does not meet
|
||||||
-- the corrected test (see albumForRequest in internal/lidarrrequests): a track
|
-- the corrected test: a track
|
||||||
-- that arrived after the request, or no track that was already missing at the
|
-- that arrived after the request, or no track that was already missing at the
|
||||||
-- request still missing. The reconciler then judges them again; the Lidarr add
|
-- request still missing. The reconciler then judges them again; the Lidarr add
|
||||||
-- stays confirmed, so nothing is re-sent. Requests that genuinely completed
|
-- stays confirmed, so nothing is re-sent. Requests that genuinely completed
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
-- #5296: keep ListenBrainz's whole similar-recordings answer, and record every
|
-- Keep ListenBrainz's whole similar-recordings answer, and record every
|
||||||
-- fetch, so the library match is made locally and can be made again.
|
-- fetch, so the library match is made locally and can be made again.
|
||||||
--
|
--
|
||||||
-- Until now the worker kept only the recordings already in the library (at most
|
-- Before: the worker kept only the recordings already in the library (at most
|
||||||
-- 20 of LB's 50) and threw the rest away. Two things followed:
|
-- 20 of LB's 50) and threw the rest away. Two things followed:
|
||||||
--
|
--
|
||||||
-- * A recording that reached the library later — a Lidarr import, or an MBID
|
-- * A recording that reached the library later — a Lidarr import, or an MBID
|
||||||
@@ -9,8 +9,8 @@
|
|||||||
-- fetched again.
|
-- fetched again.
|
||||||
-- * A seed with no in-library match wrote nothing at all, and freshness was
|
-- * A seed with no in-library match wrote nothing at all, and freshness was
|
||||||
-- read from track_similarity, so that seed was never fresh. With the queue
|
-- read from track_similarity, so that seed was never fresh. With the queue
|
||||||
-- ordered by id, 25 such seeds held its head and were re-asked every hour
|
-- ordered by id, such seeds held its head and were re-asked every hour
|
||||||
-- while 2,449 others waited (#3879).
|
-- while the rest waited.
|
||||||
--
|
--
|
||||||
-- The listenbrainz rows in track_similarity are now derived from this cache by
|
-- The listenbrainz rows in track_similarity are now derived from this cache by
|
||||||
-- ResolveListenBrainzTrackEdges.
|
-- ResolveListenBrainzTrackEdges.
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
-- M489: a persistent, per-user notifications inbox (#726).
|
-- A persistent, per-user notifications inbox.
|
||||||
--
|
--
|
||||||
-- The event bus is fire-and-forget: a client that is not connected when a
|
-- The event bus is fire-and-forget: a client that is not connected when a
|
||||||
-- request completes, or when the scanner marks tracks missing, never hears of
|
-- request completes, or when the scanner marks tracks missing, never hears of
|
||||||
-- it. A row here is the durable record; the bus only nudges open clients to
|
-- it. A row here is the durable record; the bus only nudges open clients to
|
||||||
-- come and read it.
|
-- come and read it.
|
||||||
--
|
--
|
||||||
-- `kind` is CHECK-gated (rule 36). A new kind adds its value here, in the
|
-- `kind` is CHECK-gated. A new kind adds its value here, in the
|
||||||
-- same migration as the code that produces it. The list is mirrored in
|
-- same migration as the code that produces it. The list is mirrored in
|
||||||
-- internal/notifications/kinds.go, and both CHECKs below must agree with it.
|
-- internal/notifications/kinds.go, and both CHECKs below must agree with it.
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
-- M489 #5346: notifications by email, grouped. Nothing is emailed per event.
|
-- Notifications by email, grouped. Nothing is emailed per event.
|
||||||
--
|
--
|
||||||
-- New music (request_completed) goes out as at most one summary a day, at a set
|
-- New music (request_completed) goes out as at most one summary a day, at a set
|
||||||
-- hour in each user's own timezone. Everything else is batched: a batch opens
|
-- hour in each user's own timezone. Everything else is batched: a batch opens
|
||||||
-- at the first un-emailed item and one email goes out a window later, holding
|
-- at the first un-emailed item and one email goes out a window later, holding
|
||||||
-- whatever accumulated. internal/notifications/digest.go is the one sender.
|
-- whatever accumulated. internal/notifications/digest.go is the one sender.
|
||||||
|
|
||||||
-- The two knobs, in admin Settings (rule 25). Singleton in the style of
|
-- The two knobs, in admin Settings. Singleton in the style of
|
||||||
-- fingerprint_settings (0061).
|
-- fingerprint_settings (0061).
|
||||||
CREATE TABLE notification_email_settings (
|
CREATE TABLE notification_email_settings (
|
||||||
id boolean PRIMARY KEY DEFAULT true,
|
id boolean PRIMARY KEY DEFAULT true,
|
||||||
|
|||||||
@@ -1,30 +1,30 @@
|
|||||||
-- 0075_duplicate_resolve.up.sql — duplicates resolve themselves (Scribe
|
-- 0075_duplicate_resolve.up.sql — duplicates resolve themselves.
|
||||||
-- milestone #498: #5435, #5436, #5437).
|
|
||||||
--
|
--
|
||||||
-- The sweep (0059) proposes groups and, until now, every proposal waited for the
|
-- Before: the sweep (0059) proposed groups and every proposal waited for an
|
||||||
-- operator. The resolver classifies each pending group and acts on the classes
|
-- admin. After: the resolver classifies each pending group and acts on the
|
||||||
-- that are safe. What makes deletion safe is Lidarr's view of the file: Lidarr
|
-- classes that are safe. What makes deletion safe is Lidarr's view of the file:
|
||||||
-- maps one file to each track of the release it monitors, so deleting a mapped
|
-- Lidarr maps one file to each track of the release it monitors, so deleting a
|
||||||
-- file opens a hole it fills by downloading again. Only a copy Lidarr does not
|
-- mapped file opens a hole it fills by downloading again. Only a copy Lidarr
|
||||||
-- map may go.
|
-- does not map may go.
|
||||||
|
|
||||||
-- What kind of duplicate a group is. NULL until the resolver has classified it.
|
-- What kind of duplicate a group is. NULL until the resolver has classified it.
|
||||||
-- same_release every copy is on one album
|
-- same_release every copy is on one album
|
||||||
-- cross_release the same song on different releases (a single and its album)
|
-- cross_release the same song on different releases (a single and its album)
|
||||||
-- mismatch identical audio under different titles on different albums:
|
-- mismatch identical audio under different titles on different albums:
|
||||||
-- a file imported as the wrong song
|
-- a file imported as the wrong song
|
||||||
-- review anything else: the operator decides
|
-- review anything else: an admin decides
|
||||||
-- Rule 36: a new value swaps this constraint in the same migration.
|
-- A new value swaps this constraint in the same migration.
|
||||||
ALTER TABLE duplicate_groups ADD COLUMN class text;
|
ALTER TABLE duplicate_groups ADD COLUMN class text;
|
||||||
ALTER TABLE duplicate_groups ADD CONSTRAINT duplicate_groups_class_check
|
ALTER TABLE duplicate_groups ADD CONSTRAINT duplicate_groups_class_check
|
||||||
CHECK (class IN ('same_release', 'cross_release', 'mismatch', 'review'));
|
CHECK (class IN ('same_release', 'cross_release', 'mismatch', 'review'));
|
||||||
|
|
||||||
-- Why the resolver left a group for the operator, in words the report shows
|
-- Why the resolver left a group for an admin, in words the report shows
|
||||||
-- ("Lidarr tracks every copy as its own track"). NULL when it had nothing to say.
|
-- ("Lidarr tracks every copy as its own track"). NULL when it had nothing to
|
||||||
|
-- say.
|
||||||
ALTER TABLE duplicate_groups ADD COLUMN resolve_note text;
|
ALTER TABLE duplicate_groups ADD COLUMN resolve_note text;
|
||||||
|
|
||||||
-- True when the resolver merged the group itself, so the report can tell its
|
-- True when the resolver merged the group itself, so the report can tell its
|
||||||
-- merges from the operator's.
|
-- merges from an admin's.
|
||||||
ALTER TABLE duplicate_groups ADD COLUMN resolved_automatically boolean NOT NULL DEFAULT false;
|
ALTER TABLE duplicate_groups ADD COLUMN resolved_automatically boolean NOT NULL DEFAULT false;
|
||||||
|
|
||||||
-- Lidarr's view of each copy, as of the resolver's last pass:
|
-- Lidarr's view of each copy, as of the resolver's last pass:
|
||||||
@@ -36,12 +36,12 @@ ALTER TABLE duplicate_group_members ADD COLUMN lidarr_state text;
|
|||||||
ALTER TABLE duplicate_group_members ADD CONSTRAINT duplicate_group_members_lidarr_state_check
|
ALTER TABLE duplicate_group_members ADD CONSTRAINT duplicate_group_members_lidarr_state_check
|
||||||
CHECK (lidarr_state IN ('tracked', 'unmapped'));
|
CHECK (lidarr_state IN ('tracked', 'unmapped'));
|
||||||
|
|
||||||
-- Rule 25: the operator can turn the resolver off. On by default: the operator
|
-- An admin can turn the resolver off. On by default: duplicates are meant to
|
||||||
-- asked for duplicates to resolve themselves (2026-10-09).
|
-- resolve themselves.
|
||||||
ALTER TABLE fingerprint_settings ADD COLUMN auto_resolve boolean NOT NULL DEFAULT true;
|
ALTER TABLE fingerprint_settings ADD COLUMN auto_resolve boolean NOT NULL DEFAULT true;
|
||||||
|
|
||||||
-- The resolver tells admins what it did (rule 36: the kind CHECKs of 0073 are
|
-- The resolver tells admins what it did (the kind CHECKs of 0073 are swapped
|
||||||
-- swapped to carry the new value). Postgres named both from their columns.
|
-- to carry the new value). Postgres named both from their columns.
|
||||||
ALTER TABLE user_notifications DROP CONSTRAINT user_notifications_kind_check;
|
ALTER TABLE user_notifications DROP CONSTRAINT user_notifications_kind_check;
|
||||||
ALTER TABLE user_notifications ADD CONSTRAINT user_notifications_kind_check
|
ALTER TABLE user_notifications ADD CONSTRAINT user_notifications_kind_check
|
||||||
CHECK (kind IN ('request_approved', 'request_rejected', 'request_completed',
|
CHECK (kind IN ('request_approved', 'request_rejected', 'request_completed',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
-- 0076_track_song_link.up.sql — the same song on several releases counts as
|
-- 0076_track_song_link.up.sql — the same song on several releases counts as
|
||||||
-- one song (Scribe milestone #498, #5438; operator decision D-b).
|
-- one song.
|
||||||
--
|
--
|
||||||
-- A single and the album it came from each hold the song, and Lidarr keeps
|
-- A single and the album it came from each hold the song, and Lidarr keeps
|
||||||
-- both: each fulfils its own release. Minstrel keeps both files and links
|
-- both: each fulfils its own release. Minstrel keeps both files and links
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
-- 0077_track_source_verdict.up.sql — video rips are held back from mixes and
|
-- 0077_track_source_verdict.up.sql — video rips are held back from mixes and
|
||||||
-- radio until the operator says otherwise (Scribe milestone #498, #5439;
|
-- radio until an admin says otherwise.
|
||||||
-- operator decision D-d).
|
|
||||||
--
|
--
|
||||||
-- A file whose name carries a video-rip marker ("(Official Video)", "[HD]",
|
-- A file whose name carries a video-rip marker ("(Official Video)", "[HD]")
|
||||||
-- #5410) holds the audio of an upload, which can carry intros, skits or a
|
-- holds the audio of an upload, which can carry intros, skits or a
|
||||||
-- different take. The duplicate resolver sets 'suspect' on such a present
|
-- different take. The duplicate resolver sets 'suspect' on such a present
|
||||||
-- track each pass, and clears it when the name no longer matches. 'suspect'
|
-- track each pass, and clears it when the name no longer matches. 'suspect'
|
||||||
-- keeps the track out of radio and the system mixes; it still plays when
|
-- keeps the track out of radio and the system mixes; it still plays when
|
||||||
-- chosen. 'fine' is the operator's "this one is fine" and is never
|
-- chosen. 'fine' is an admin's "this one is fine" and is never
|
||||||
-- overwritten by the resolver.
|
-- overwritten by the resolver.
|
||||||
ALTER TABLE tracks ADD COLUMN source_verdict text
|
ALTER TABLE tracks ADD COLUMN source_verdict text
|
||||||
CONSTRAINT tracks_source_verdict_check CHECK (source_verdict IN ('suspect', 'fine'));
|
CONSTRAINT tracks_source_verdict_check CHECK (source_verdict IN ('suspect', 'fine'));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
-- 0078_drop_duplicate_lidarr_state.up.sql — the duplicate resolver works
|
-- 0078_drop_duplicate_lidarr_state.up.sql — the duplicate resolver works
|
||||||
-- without Lidarr (Scribe milestone #502, #5517).
|
-- without Lidarr.
|
||||||
--
|
--
|
||||||
-- 0075 recorded Lidarr's view of each duplicate copy (tracked or unmapped)
|
-- 0075 recorded Lidarr's view of each duplicate copy (tracked or unmapped)
|
||||||
-- because a copy Lidarr mapped could not be removed without Lidarr downloading
|
-- because a copy Lidarr mapped could not be removed without Lidarr downloading
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
-- 0079_acquisition_services.up.sql — the indexers and download clients the
|
-- 0079_acquisition_services.up.sql — the indexers and download clients the
|
||||||
-- operator adds in Admin → Integrations (Scribe milestone #502, #5518).
|
-- admin adds in Admin → Integrations.
|
||||||
--
|
--
|
||||||
-- Minstrel replaces Lidarr, so it searches and downloads itself. Nothing is
|
-- Minstrel replaces Lidarr, so it searches and downloads itself. Nothing is
|
||||||
-- seeded and nothing is copied from Lidarr: a connection is a row the operator
|
-- seeded and nothing is copied from Lidarr: a connection is a row an admin
|
||||||
-- adds, from a list of what Minstrel can speak. Ported from Fabled
|
-- adds, from a list of what Minstrel can speak:
|
||||||
-- Librarian's acquisition_services (its migrations 0044, 0055, 0062, 0064,
|
|
||||||
-- 0070), which settled the shape:
|
|
||||||
--
|
--
|
||||||
-- kind which list the row appears in: search (Indexers) or download
|
-- kind which list the row appears in: search (Indexers) or download
|
||||||
-- (Download clients).
|
-- (Download clients).
|
||||||
@@ -32,7 +30,7 @@ CREATE TABLE acquisition_services (
|
|||||||
label text NOT NULL,
|
label text NOT NULL,
|
||||||
-- A new row is off: it has no address yet.
|
-- A new row is off: it has no address yet.
|
||||||
enabled boolean NOT NULL DEFAULT false,
|
enabled boolean NOT NULL DEFAULT false,
|
||||||
-- The address as the operator's browser opens it. For a Torznab feed it
|
-- The address as an admin's browser opens it. For a Torznab feed it
|
||||||
-- is the whole feed URL up to and including /api.
|
-- is the whole feed URL up to and including /api.
|
||||||
base_url text NOT NULL DEFAULT '',
|
base_url text NOT NULL DEFAULT '',
|
||||||
api_key text NOT NULL DEFAULT '',
|
api_key text NOT NULL DEFAULT '',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
-- 0080_release_catalogue.up.sql — Minstrel's own copy of what artists have
|
-- 0080_release_catalogue.up.sql — Minstrel's own copy of what artists have
|
||||||
-- released, read from MusicBrainz (Scribe milestone #502, #5519).
|
-- released, read from MusicBrainz.
|
||||||
--
|
--
|
||||||
-- With Lidarr gone, the wanted list, search and import need to know an
|
-- With Lidarr gone, the wanted list, search and import need to know an
|
||||||
-- artist's discography and each release's track list. MusicBrainz allows one
|
-- artist's discography and each release's track list. MusicBrainz allows one
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- 0081_wanted.up.sql — the wanted list (Scribe milestone #502, #5520).
|
-- 0081_wanted.up.sql — the wanted list.
|
||||||
--
|
--
|
||||||
-- With Lidarr gone, Minstrel keeps its own record of what it is looking for:
|
-- With Lidarr gone, Minstrel keeps its own record of what it is looking for:
|
||||||
--
|
--
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
-- lidarr_requests becomes requests with no Lidarr columns: approving a request
|
-- lidarr_requests becomes requests with no Lidarr columns: approving a request
|
||||||
-- now records a want or a follow instead of sending an add to Lidarr. The
|
-- now records a want or a follow instead of sending an add to Lidarr. The
|
||||||
-- missing-file sweeper's attempt bookkeeping goes too; retrying belongs to the
|
-- missing-file sweeper's attempt bookkeeping goes too; retrying belongs to the
|
||||||
-- search schedule (#5521), which works per want.
|
-- search schedule, which works per want.
|
||||||
|
|
||||||
CREATE TABLE release_type_profiles (
|
CREATE TABLE release_type_profiles (
|
||||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
@@ -36,8 +36,8 @@ CREATE TABLE release_type_profiles (
|
|||||||
CREATE UNIQUE INDEX release_type_profiles_one_default
|
CREATE UNIQUE INDEX release_type_profiles_one_default
|
||||||
ON release_type_profiles (is_default) WHERE is_default;
|
ON release_type_profiles (is_default) WHERE is_default;
|
||||||
|
|
||||||
-- Lidarr's "Standard" metadata profile, which every followed artist used
|
-- Lidarr's "Standard" metadata profile, which followed artists used: albums,
|
||||||
-- (note #5527): albums, EPs and singles, studio releases only.
|
-- EPs and singles, studio releases only.
|
||||||
INSERT INTO release_type_profiles (name, primary_types, secondary_types, is_default)
|
INSERT INTO release_type_profiles (name, primary_types, secondary_types, is_default)
|
||||||
VALUES ('Standard', ARRAY['Album', 'EP', 'Single'], '{}', true);
|
VALUES ('Standard', ARRAY['Album', 'EP', 'Single'], '{}', true);
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ CREATE TABLE follows (
|
|||||||
baseline_at timestamptz,
|
baseline_at timestamptz,
|
||||||
-- Whether the baseline also wants what the artist had already released
|
-- Whether the baseline also wants what the artist had already released
|
||||||
-- that the profile passes and the library lacks: an approved artist
|
-- that the profile passes and the library lacks: an approved artist
|
||||||
-- request, or the operator ticking "also want…". Cleared once applied.
|
-- request, or an admin ticking "also want…". Cleared once applied.
|
||||||
want_existing boolean NOT NULL DEFAULT false,
|
want_existing boolean NOT NULL DEFAULT false,
|
||||||
-- The discography read this follow last applied.
|
-- The discography read this follow last applied.
|
||||||
checked_at timestamptz,
|
checked_at timestamptz,
|
||||||
@@ -111,7 +111,7 @@ ALTER INDEX lidarr_requests_status_idx RENAME TO requests_status_idx;
|
|||||||
ALTER INDEX lidarr_requests_artist_mbid_idx RENAME TO requests_artist_mbid_idx;
|
ALTER INDEX lidarr_requests_artist_mbid_idx RENAME TO requests_artist_mbid_idx;
|
||||||
ALTER INDEX lidarr_requests_album_mbid_idx RENAME TO requests_album_mbid_idx;
|
ALTER INDEX lidarr_requests_album_mbid_idx RENAME TO requests_album_mbid_idx;
|
||||||
|
|
||||||
-- Requests made before #5241 name albums by release id. A want is keyed by
|
-- Older requests may name albums by release id. A want is keyed by
|
||||||
-- release group, so repoint every request the library can translate.
|
-- release group, so repoint every request the library can translate.
|
||||||
UPDATE requests r
|
UPDATE requests r
|
||||||
SET album_mbid = a.release_group_mbid,
|
SET album_mbid = a.release_group_mbid,
|
||||||
@@ -133,10 +133,10 @@ SELECT DISTINCT ON (r.album_mbid)
|
|||||||
ORDER BY r.album_mbid, r.requested_at
|
ORDER BY r.album_mbid, r.requested_at
|
||||||
ON CONFLICT (release_group_mbid) DO NOTHING;
|
ON CONFLICT (release_group_mbid) DO NOTHING;
|
||||||
|
|
||||||
-- The library's album artists start out followed on the default profile
|
-- The library's album artists start out followed on the default profile: the
|
||||||
-- (operator, 2026-10-09: "we already have artists in minstrel those start as
|
-- artists already in the library are the wanted artists. Various Artists and
|
||||||
-- the wanted artists"). Various Artists and MusicBrainz's bracketed
|
-- MusicBrainz's bracketed placeholders ([unknown], [anonymous]…) are not
|
||||||
-- placeholders ([unknown], [anonymous]…) are not artists anyone follows.
|
-- artists anyone follows.
|
||||||
INSERT INTO follows (artist_mbid, artist_name, profile_id)
|
INSERT INTO follows (artist_mbid, artist_name, profile_id)
|
||||||
SELECT ar.mbid, ar.name, p.id
|
SELECT ar.mbid, ar.name, p.id
|
||||||
FROM artists ar
|
FROM artists ar
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
-- 0082_search_and_grab.up.sql — searching for wants and grabbing releases
|
-- 0082_search_and_grab.up.sql — searching for wants and grabbing releases.
|
||||||
-- (Scribe milestone #502, #5521).
|
|
||||||
--
|
--
|
||||||
-- acquisition_settings one row: whether the schedule takes the best release
|
-- acquisition_settings one row: whether the schedule takes the best release
|
||||||
-- by itself, how often a want is searched again, which
|
-- by itself, how often a want is searched again, which
|
||||||
@@ -16,7 +15,7 @@ CREATE TABLE acquisition_settings (
|
|||||||
-- 0 switches the schedule off.
|
-- 0 switches the schedule off.
|
||||||
search_interval_hours integer NOT NULL DEFAULT 24
|
search_interval_hours integer NOT NULL DEFAULT 24
|
||||||
CONSTRAINT acquisition_settings_interval_check CHECK (search_interval_hours BETWEEN 0 AND 720),
|
CONSTRAINT acquisition_settings_interval_check CHECK (search_interval_hours BETWEEN 0 AND 720),
|
||||||
-- Lidarr's "Any" profile (note #5527): trash lossy to lossless; unknown off.
|
-- As Lidarr's "Any" profile: trash lossy to lossless; unknown off.
|
||||||
allowed_qualities text[] NOT NULL DEFAULT ARRAY['lossless_hires', 'lossless', 'lossy_high',
|
allowed_qualities text[] NOT NULL DEFAULT ARRAY['lossless_hires', 'lossless', 'lossy_high',
|
||||||
'lossy_standard', 'lossy_low', 'lossy_poor', 'lossy_trash'],
|
'lossy_standard', 'lossy_low', 'lossy_poor', 'lossy_trash'],
|
||||||
last_pass_at timestamptz,
|
last_pass_at timestamptz,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- Import with verification (M502 #5522): a finished download is checked
|
-- Import with verification: a finished download is checked
|
||||||
-- against the album it was grabbed for before anything reaches the library.
|
-- against the album it was grabbed for before anything reaches the library.
|
||||||
--
|
--
|
||||||
-- grabs (columns) how far the download has got, and what the import
|
-- grabs (columns) how far the download has got, and what the import
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
-- Download lifecycle (M502 #5523), ported from Fabled Librarian's System 104:
|
-- Download lifecycle: a download that stalls or fails is given up and its album
|
||||||
-- a download that stalls or fails is given up and its album searched again;
|
-- searched again; an imported download seeds, then is removed.
|
||||||
-- an imported download seeds, then is removed.
|
|
||||||
--
|
--
|
||||||
-- download_interventions what was seen wrong with a download or a client,
|
-- download_interventions what was seen wrong with a download or a client,
|
||||||
-- what Minstrel did about it, and whether it
|
-- what Minstrel did about it, and whether it
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
-- The library's albums go on the wanted list as held, once (M502 #5567,
|
-- The library's albums go on the wanted list as held, once. After that the list
|
||||||
-- operator 2026-10-10). After that the list is the truth: an album taken
|
-- is the truth: an album taken off it stays off, and one dropped into the
|
||||||
-- off it stays off, and one dropped into the library folder by hand is not
|
-- library folder by hand is not added. Re-acquisition fetches back only albums
|
||||||
-- added. Re-acquisition fetches back only albums on the list.
|
-- on the list.
|
||||||
--
|
--
|
||||||
-- wants.reason library: on the list because the library
|
-- wants.reason library: on the list because the library
|
||||||
-- held it when the list began.
|
-- held it when the list began.
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
-- Every library album is matched to MusicBrainz before it goes on the list
|
-- Every library album is matched to MusicBrainz before it goes on the list, so
|
||||||
-- (#5569, operator 2026-10-10: "they should all exist in music brainz we
|
-- its metadata comes from MusicBrainz. The listing in 0085 took only albums
|
||||||
-- need to use their meta data or a search system to link them up"). The
|
-- whose tags already named their release group and artist; the rest are now
|
||||||
-- listing in 0085 took only albums whose tags already named their release
|
-- matched by the release their tags name, the artist's discography, or a
|
||||||
-- group and artist; the rest are now matched by the release their tags
|
-- search, and checked against their own tracks.
|
||||||
-- name, the artist's discography, or a search, and checked against their
|
|
||||||
-- own tracks.
|
|
||||||
--
|
--
|
||||||
-- library_album_matches one row per album the listing has decided:
|
-- library_album_matches one row per album the listing has decided:
|
||||||
-- listed (with how it was matched) or unmatched
|
-- listed (with how it was matched) or unmatched
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- The first matching (#5569) required every track's length to fit as well
|
-- The first matching (0086) required every track's length to fit as well
|
||||||
-- as its title, and refused singles and near-complete albums the library
|
-- as its title, and refused singles and near-complete albums the library
|
||||||
-- plainly holds. Matching now takes the titles alone under the album's own
|
-- plainly holds. Matching now takes the titles alone under the album's own
|
||||||
-- name: the albums it refused are matched again.
|
-- name: the albums it refused are matched again.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- Which version of the matching (#5569) decided an album. A refusal by an
|
-- Which version of the matching decided an album. A refusal by an
|
||||||
-- older matcher is tried again by a newer one, so improving the matching
|
-- older matcher is tried again by a newer one, so improving the matching
|
||||||
-- reaches the albums it refused without a migration each time.
|
-- reaches the albums it refused without a migration each time.
|
||||||
ALTER TABLE library_album_matches ADD COLUMN matcher integer NOT NULL DEFAULT 0;
|
ALTER TABLE library_album_matches ADD COLUMN matcher integer NOT NULL DEFAULT 0;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- The Collection's Unmatched section (#5579): a person matches an album the
|
-- The Collection's Unmatched section: a person matches an album the
|
||||||
-- listing could not, by a MusicBrainz link or a search (method 'manual'),
|
-- listing could not, by a MusicBrainz link or a search (method 'manual'),
|
||||||
-- or leaves it off the list for good (outcome 'dismissed': a local band's
|
-- or leaves it off the list for good (outcome 'dismissed': a local band's
|
||||||
-- album MusicBrainz will never have, a mix that is no album).
|
-- album MusicBrainz will never have, a mix that is no album).
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
-- Upgrades (M502 #5524): a held album whose copy is below the quality the
|
-- Upgrades: a held album whose copy is below the quality an admin settles
|
||||||
-- operator settles for, or that holds a video rip, stays wanted for a better
|
-- for, or that holds a video rip, stays wanted for a better
|
||||||
-- copy. The import replaces the old tracks with the new ones.
|
-- copy. The import replaces the old tracks with the new ones.
|
||||||
--
|
--
|
||||||
-- acquisition_settings.upgrade_until the tier a held album is upgraded to;
|
-- acquisition_settings.upgrade_until the tier a held album is upgraded to;
|
||||||
-- '' switches quality upgrades off.
|
-- '' switches quality upgrades off.
|
||||||
-- Lidarr's "Any" profile upgraded up to
|
-- Lidarr's "Any" profile upgraded up to
|
||||||
-- High Quality Lossy (note #5527).
|
-- High Quality Lossy.
|
||||||
-- wants.upgrade why a held album is wanted again:
|
-- wants.upgrade why a held album is wanted again:
|
||||||
-- quality (below upgrade_until) or rip
|
-- quality (below upgrade_until) or rip
|
||||||
-- (a track is a video rip); '' not.
|
-- (a track is a video rip); '' not.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- Minstrel no longer talks to Lidarr (M502 #5525): it finds, downloads,
|
-- Minstrel does not talk to Lidarr: it finds, downloads,
|
||||||
-- checks and files music itself.
|
-- checks and files music itself.
|
||||||
--
|
--
|
||||||
-- lidarr_config the connection's settings: dropped.
|
-- lidarr_config the connection's settings: dropped.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- The admin Overview (M504) reads plays across every user by time: the ones
|
-- The admin Overview reads plays across every user by time: the ones
|
||||||
-- in progress and the last day's count. The only index on play_events leads
|
-- in progress and the last day's count. The only index on play_events leads
|
||||||
-- with user_id, so without this every Overview load scanned the table.
|
-- with user_id, so without this every Overview load scanned the table.
|
||||||
CREATE INDEX play_events_started_idx ON play_events (started_at);
|
CREATE INDEX play_events_started_idx ON play_events (started_at);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- The library's folders, set in Settings (#6129). Until someone saves them
|
-- The library's folders, set in Settings. Until someone saves them
|
||||||
-- there, scan_paths is NULL and MINSTREL_LIBRARY_SCAN_PATHS names the folders,
|
-- there, scan_paths is NULL and MINSTREL_LIBRARY_SCAN_PATHS names the folders,
|
||||||
-- as it always has; from the first save the row is what the server reads.
|
-- as it always has; from the first save the row is what the server reads.
|
||||||
CREATE TABLE library_settings (
|
CREATE TABLE library_settings (
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
-- 0094_duplicate_distinct.up.sql — duplicates settle themselves (Scribe
|
-- 0094_duplicate_distinct.up.sql — duplicates settle themselves, without
|
||||||
-- milestone #498, operator 2026-10-19: "this needs to be automated away").
|
-- waiting on an admin.
|
||||||
--
|
--
|
||||||
-- distinct: one album holds two versions of a song that sound alike ("Lovesick"
|
-- distinct: one album holds two versions of a song that sound alike ("Lovesick"
|
||||||
-- and "Lovesick (Instrumental)"). They are not duplicates, and the resolver
|
-- and "Lovesick (Instrumental)"). They are not duplicates, and the resolver
|
||||||
-- dismisses them itself. mismatch now also covers one album holding the same
|
-- dismisses them itself. mismatch now also covers one album holding the same
|
||||||
-- audio in several track slots, the resolver's wrong-file case.
|
-- audio in several track slots, the resolver's wrong-file case.
|
||||||
-- Rule 36: the new value swaps the constraint in the same migration.
|
-- The new value swaps the constraint in the same migration.
|
||||||
ALTER TABLE duplicate_groups DROP CONSTRAINT duplicate_groups_class_check;
|
ALTER TABLE duplicate_groups DROP CONSTRAINT duplicate_groups_class_check;
|
||||||
ALTER TABLE duplicate_groups ADD CONSTRAINT duplicate_groups_class_check
|
ALTER TABLE duplicate_groups ADD CONSTRAINT duplicate_groups_class_check
|
||||||
CHECK (class IN ('same_release', 'cross_release', 'mismatch', 'distinct', 'review'));
|
CHECK (class IN ('same_release', 'cross_release', 'mismatch', 'distinct', 'review'));
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
-- 0095_tracks_emptied_at.up.sql — a slot the duplicate resolver emptied
|
-- 0095_tracks_emptied_at.up.sql — a slot the duplicate resolver emptied
|
||||||
-- waits for a copy fetched again (Scribe milestone #498).
|
-- waits for a copy fetched again.
|
||||||
--
|
--
|
||||||
-- When the resolver removes a file filed under another song's title and the
|
-- When the resolver removes a file filed under another song's title and the
|
||||||
-- album holds no copy of that song as long as the song is, the row is marked
|
-- album holds no copy of that song as long as the song is, the row is marked
|
||||||
-- missing and stamped here with the same time. Missing-pair folding then pairs
|
-- missing and stamped here with the same time. Missing-pair folding then pairs
|
||||||
-- it by title only with a track added after that time, a copy fetched again,
|
-- it by title only with a track added after that time, a copy fetched again,
|
||||||
-- and not with one already on the album: on 2026-10-20 an emptied "Open the
|
-- and not with one already on the album, which under that title holds
|
||||||
-- Gateway" was folded into a file tagged so that holds another song.
|
-- another song.
|
||||||
--
|
--
|
||||||
-- The stamp counts only while it equals missing_since: a row whose file came
|
-- The stamp counts only while it equals missing_since: a row whose file came
|
||||||
-- back and later went missing again is an ordinary missing row.
|
-- back and later went missing again is an ordinary missing row.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
-- #6177: ListenBrainz similarity scores were stored raw. They are counts (16 to
|
-- Before: ListenBrainz similarity scores were stored raw. They are counts, into
|
||||||
-- 1445 for recordings, 16 to 10,875 for artists on the operator's library), and
|
-- the thousands, and every reader treats similarity as 0 to 1. After: the
|
||||||
-- every reader treats similarity as 0 to 1. Rescale the stored rows relative to
|
-- stored rows are rescaled relative to each seed's best score, as the worker
|
||||||
-- each seed's best score, as the worker now writes them.
|
-- now writes them.
|
||||||
--
|
--
|
||||||
-- A seed's best is taken over everything stored for it: the cached LB answer
|
-- A seed's best is taken over everything stored for it: the cached LB answer
|
||||||
-- and its edges for tracks, matched and unmatched rows for artists. A seed
|
-- and its edges for tracks, matched and unmatched rows for artists. A seed
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
-- #6178: the image's fpcalc exits 3 ("Error decoding audio frame (End of
|
-- The image's fpcalc exits 3 ("Error decoding audio frame (End of file)") on
|
||||||
-- file)") on every file shorter than the span it is asked for, so every track
|
-- every file shorter than the span it is asked for, so every track under two
|
||||||
-- under two minutes failed both prints. The failure was stored as a verdict,
|
-- minutes failed both prints. Before: the failure was stored as a verdict, and
|
||||||
-- and a verdict is not retried until the file changes. fpcalcReadToEnd now
|
-- a verdict is not retried until the file changes. After: fpcalcReadToEnd keeps
|
||||||
-- keeps those prints; clear the failures so the workers take them again.
|
-- those prints; clear the failures so the workers take them again.
|
||||||
|
|
||||||
-- The AcoustID lookup's queue takes a track with no lookup row.
|
-- The AcoustID lookup's queue takes a track with no lookup row.
|
||||||
DELETE FROM track_acoustid_lookups
|
DELETE FROM track_acoustid_lookups
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- #6176: gone, a release its download client would not let Minstrel follow:
|
-- gone: a release its download client would not let Minstrel follow:
|
||||||
-- held outside Minstrel's category, or leaving the client twice. Held back
|
-- held outside Minstrel's category, or leaving the client twice. Held back
|
||||||
-- until expires_at, as stalled is.
|
-- until expires_at, as stalled is.
|
||||||
ALTER TABLE release_blocks DROP CONSTRAINT release_blocks_reason_check;
|
ALTER TABLE release_blocks DROP CONSTRAINT release_blocks_reason_check;
|
||||||
|
|||||||
Reference in New Issue
Block a user