diff --git a/backend/src/db/migrations.ts b/backend/src/db/migrations.ts index b03c6c0..114cf77 100644 --- a/backend/src/db/migrations.ts +++ b/backend/src/db/migrations.ts @@ -851,4 +851,40 @@ export const MIGRATIONS: Migration[] = [ ); `, }, + { + // Every library scan used to reset a disliked track from HIDDEN back to + // LIBRARY, so tracks the listener had rejected kept returning in the Vibe. + // The scanner no longer does that; this puts the tracks it already undid + // back where the dislike left them. A track whose dislike was deliberately + // restored has no `dislikes` row and is untouched here. + // `20260707_backfill_claims` wrote tag-derived credits once, and nothing + // wrote them afterwards: the scanner filled `track_artists` but left the + // claim spine to MusicBrainz. Every track added since, and every track + // MusicBrainz does not know, therefore had no edge in `claim_fusion` and + // was unreachable by all but one Vibe generator. The scanner now writes + // these itself; this catches up the tracks it already missed. + id: '20260810_backfill_tag_credits_since_first_scan', + sql: ` + INSERT INTO claims (subject_type, subject_id, predicate, object_type, object_id, source, confidence) + SELECT 'track', ta.track_id, + CASE WHEN ta.role = 'main' THEN 'credited_main_on' ELSE 'featured_on' END, + 'artist', ta.artist_id, 'tag', 1.0 + FROM track_artists ta + JOIN tracks t ON t.id = ta.track_id + WHERE t.state IN ('LIBRARY', 'RECOMMENDED') + ON CONFLICT (subject_type, subject_id, predicate, object_type, object_id, source, user_id) + DO NOTHING; + `, + }, + { + id: '20260810_rehide_resurrected_dislikes', + sql: ` + UPDATE tracks t + SET state = 'HIDDEN' + FROM dislikes d + WHERE d.track_id = t.id + AND d.deleted_at IS NULL + AND t.state IN ('LIBRARY', 'RECOMMENDED'); + `, + }, ];