feat: MusicBrainz BPM enrichment + improved AI prompts

- lookupRecordingWithTags, extractBpmFromTags, extractTimeSigFromTags, getMusicBrainzRecording added to MB client
- upsertSong preserves existing BPM via COALESCE on conflict
- updateSongBpm helper for async enrichment writes
- AnalysisInput gains confirmedBpm / confirmedTimeSigNum fields
- POST /api/analyze fetches confirmed BPM from DB then MB tags before generation
- All three AI providers use confirmedBpm as authoritative and build enriched userMessage
- POST /api/tracks auto-registration now fetches tags via getMusicBrainzRecording
- Updated User-Agent and MB client fallback URL to Gitea

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
AJ Avezzano
2026-04-03 19:25:04 -04:00
parent 7ba4381bff
commit 5e686fc9c4
8 changed files with 223 additions and 26 deletions

View File

@@ -92,8 +92,8 @@ export async function upsertSong(song: Omit<SongRow, "created_at" | "updated_at"
title = EXCLUDED.title,
artist = EXCLUDED.artist,
duration_seconds = EXCLUDED.duration_seconds,
acousticbrainz_bpm = EXCLUDED.acousticbrainz_bpm,
acousticbrainz_time_sig_num = EXCLUDED.acousticbrainz_time_sig_num,
acousticbrainz_bpm = COALESCE(EXCLUDED.acousticbrainz_bpm, songs.acousticbrainz_bpm),
acousticbrainz_time_sig_num = COALESCE(EXCLUDED.acousticbrainz_time_sig_num, songs.acousticbrainz_time_sig_num),
source = EXCLUDED.source`,
[
song.mbid,
@@ -107,6 +107,26 @@ export async function upsertSong(song: Omit<SongRow, "created_at" | "updated_at"
);
}
/**
* Updates only the BPM and time-signature fields of a song row.
* No-ops if the song doesn't exist.
* Used by the async MusicBrainz tag enrichment path.
*/
export async function updateSongBpm(
mbid: string,
bpm: number | null,
timeSigNum: number | null
): Promise<void> {
await query(
`UPDATE songs
SET acousticbrainz_bpm = COALESCE($2, acousticbrainz_bpm),
acousticbrainz_time_sig_num = COALESCE($3, acousticbrainz_time_sig_num),
updated_at = NOW()
WHERE mbid = $1`,
[mbid, bpm, timeSigNum]
);
}
// ─── Tempo map queries ────────────────────────────────────────────────────────
export interface TempoMapRow {