feat(tags): folksonomy tag enricher — pluggable provider chain (#1490 Step 2)
Milestone #160 Option 1, Step 2. New internal/tags package that enriches the taste profile's tag facet beyond raw ID3 genre, built so adding a source later is "implement TrackTagProvider + Register()" — no enricher, settings, or schema change (operator directive). Mirrors the internal/coverart pattern: - Provider interface + package registry (Register/AllProviders/ByID); TrackTagProvider fetch capability + TestableProvider for the admin test. - DB-backed SettingsService over new tag_provider_settings + tag_sources_meta (migration 0043) — enable/key/version, boot reconciliation, and a provider-hash bump that re-opens 'none' rows when the compiled-in provider set changes. - Slim self-contained httpClient (rate-limit + retry + User-Agent), kept local so tag enrichment never depends on coverart internals. Providers: - MusicBrainz: keyless, default-ON, recording tags by MBID (rule #26 baseline). - Last.fm: keyed, default-OFF, track.getTopTags by artist+track — opt-in once a key is supplied. Enricher uses MERGE semantics (differs from coverart's first-success-wins for a single image): unions tags across every enabled provider, caps to top-K by weight, and stamps tag_source musicbrainz|lastfm|mixed|none. Writes are transactional (atomic replace of track_tags). Unit-tested without a DB: registry mechanics, provider JSON parsing + weight normalization via httptest, and the pure merge/top-K/source-label helpers. Wiring (startup + on-scan), integration tests, and the taste union (Step 3) + Settings UI (Step 4) come next. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
-- Tag-enrichment provider settings queries (milestone #160, #1490 Step 2).
|
||||
-- The tags.SettingsService consumes these — parallel to the cover-art
|
||||
-- SettingsService (coverart_settings.sql). Keeping the two independent
|
||||
-- means a new tag source is added without touching the art settings.
|
||||
|
||||
-- name: ListTagProviderSettings :many
|
||||
-- All rows, for boot reconciliation and the admin GET handler.
|
||||
SELECT provider_id, enabled, api_key, display_order, created_at, updated_at
|
||||
FROM tag_provider_settings
|
||||
ORDER BY display_order, provider_id;
|
||||
|
||||
-- name: UpsertTagProviderSettings :exec
|
||||
-- INSERT a default row for a newly-registered provider, or refresh an
|
||||
-- existing row's display_order on boot. Does NOT overwrite enabled /
|
||||
-- api_key on conflict — the operator's settings persist across restarts.
|
||||
INSERT INTO tag_provider_settings (provider_id, enabled, display_order)
|
||||
VALUES ($1, $2, $3)
|
||||
ON CONFLICT (provider_id) DO UPDATE
|
||||
SET display_order = EXCLUDED.display_order,
|
||||
updated_at = now();
|
||||
|
||||
-- name: UpdateTagProviderSettings :exec
|
||||
-- Admin PATCH. $2 sets enabled. Pass FALSE for the $3 apiKeyChanged flag
|
||||
-- to leave api_key untouched; TRUE with empty string clears it, TRUE with
|
||||
-- a value sets it.
|
||||
UPDATE tag_provider_settings
|
||||
SET enabled = COALESCE($2, enabled),
|
||||
api_key = CASE WHEN $3::boolean THEN $4 ELSE api_key END,
|
||||
updated_at = now()
|
||||
WHERE provider_id = $1;
|
||||
|
||||
-- name: GetCurrentTagSourcesVersion :one
|
||||
SELECT current_version FROM tag_sources_meta WHERE id = true;
|
||||
|
||||
-- name: BumpTagSourcesVersion :one
|
||||
-- Increment + return the new version. Called only when the enabled set
|
||||
-- changes (not on key edits).
|
||||
UPDATE tag_sources_meta
|
||||
SET current_version = current_version + 1
|
||||
WHERE id = true
|
||||
RETURNING current_version;
|
||||
|
||||
-- name: GetTagProvidersHash :one
|
||||
SELECT last_registered_providers_hash
|
||||
FROM tag_sources_meta
|
||||
WHERE id = true;
|
||||
|
||||
-- name: BumpTagVersionAndSetProvidersHash :one
|
||||
-- Atomically increment the version and store the new registered-provider
|
||||
-- hash. Called at boot when the compiled-in provider set changed, so
|
||||
-- 'none' rows become eligible for a retry through the new chain.
|
||||
UPDATE tag_sources_meta
|
||||
SET current_version = current_version + 1,
|
||||
last_registered_providers_hash = $1
|
||||
WHERE id = true
|
||||
RETURNING current_version;
|
||||
Reference in New Issue
Block a user