Enrichment & Analysis
Scroll horizontally to explore the full data flow.
Enrichment tools
Section titled “Enrichment tools”Single-track lookups cover Discogs, Beatport, MusicBrainz, and Bandcamp. Results
are cached — subsequent calls return instantly unless force_refresh is set.
The batch surfaces are deliberately narrower:
- CLI
hydrateruns Discogs, Beatport, and audio analysis. - MCP
enrich_tracksruns Discogs, Beatport, and Bandcamp. - MusicBrainz is an individual lookup and targeted backfill source; it is not an
enrich_tracksbatch provider.
lookup_discogs
Section titled “lookup_discogs”Match one specific track or release on Discogs. This is not a general catalog, credits, collaborator, or release-discovery browser.
| Parameter | Type | Required | Description |
|---|---|---|---|
track_id |
string | Track ID — auto-fills artist/title/album from library | |
artist |
string | Artist name (required if no track_id) |
|
title |
string | Track title (required if no track_id) |
|
album |
string | Album/release title for more accurate matching | |
force_refresh |
boolean | Bypass cache (default: false) |
| Returned field | Meaning |
|---|---|
title, year, label |
Matched release identity and basic metadata |
genres, styles |
Discogs genre and style arrays |
url, cover_image |
Matched release and artwork URLs |
fuzzy_match |
Whether the selected result was a fuzzy rather than exact title match |
lookup_beatport
Section titled “lookup_beatport”Match one specific track on Beatport. The result contains one genre value, not a separate sub-genre.
| Parameter | Type | Required | Description |
|---|---|---|---|
track_id |
string | Track ID — auto-fills artist/title from library | |
artist |
string | Artist name (required if no track_id) |
|
title |
string | Track title (required if no track_id) |
|
force_refresh |
boolean | Bypass cache (default: false) |
| Returned field | Meaning |
|---|---|
genre, bpm, key |
Beatport’s genre, tempo, and key for the matched track |
track_name, artists |
Matched title and artist list |
release_date, label |
Optional release date and label |
fuzzy_match |
Whether the selected result was a fuzzy rather than exact title match |
lookup_musicbrainz
Section titled “lookup_musicbrainz”Look up a track on MusicBrainz for year/label data. Returns year from first-release-date and label from the best matching release. Results are cached.
| Parameter | Type | Required | Description |
|---|---|---|---|
track_id |
string | Track ID — auto-fills artist/title from library | |
artist |
string | Artist name (required if no track_id) |
|
title |
string | Track title (required if no track_id) |
|
force_refresh |
boolean | Bypass cache (default: false) |
lookup_bandcamp
Section titled “lookup_bandcamp”Look up a track on Bandcamp for year/label/tags/cover data. Pass a direct Bandcamp /track/ or /album/ url when you already found the release page; this bypasses Bandcamp search and refreshes the artist/title cache entry. Album pages use the provided title to select the track from Bandcamp JSON-LD. Particularly effective for underground/independent electronic music.
| Parameter | Type | Required | Description |
|---|---|---|---|
track_id |
string | Track ID — auto-fills artist/title from library | |
artist |
string | Artist name (required if no track_id) |
|
title |
string | Track title (required if no track_id) |
|
url |
string | Direct Bandcamp /track/ or /album/ URL; bypasses search |
|
force_refresh |
boolean | Bypass cache (default: false) |
enrich_tracks
Section titled “enrich_tracks”Batch enrich tracks via Discogs/Beatport/Bandcamp. Accepts shared search filters to scope which tracks to enrich.
| Parameter | Type | Required | Item values | Description |
|---|---|---|---|---|
track_ids |
string[] | Specific track IDs (highest priority selector) | ||
playlist_id |
string | Enrich tracks in this playlist | ||
max_tracks |
integer | Max pending tracks to admit after cache checks (default: 50, max: 200) |
||
offset |
integer | Index into the stable underlying selector order (default: 0) |
||
providers |
string[] | ["discogs", "beatport", "bandcamp"] |
Providers (default: ["discogs"]) |
|
skip_cached |
boolean | Skip already-cached tracks (default: true) |
||
force_refresh |
boolean | Bypass cache entirely (default: false) |
||
concurrency |
integer | Max concurrent enrichments (default: 4, max: 8) |
With skip_cached=true, cached candidates do not consume max_tracks slots.
Starting at offset, the server checks ordered candidates until it admits the
requested pending work or reaches the end of the scope. A cached no-match is a
completed search; cache errors remain pending.
| Field | Type | Required | Description |
|---|---|---|---|
summary |
object | yes | Counts for the inspected continuation page, including fully cached candidates |
failures |
array | yes | Retryable failures with track, provider, and stage context |
page |
object | yes | Underlying-selector continuation metadata |
| Field | Type | Required | Description |
|---|---|---|---|
matched_tracks |
integer | yes | Total tracks in the logical selector at call time |
start_offset |
integer | yes | Underlying selector index supplied by the caller |
examined_tracks |
integer | yes | Candidates inspected from start_offset |
selected_tracks |
integer | yes | Pending tracks admitted under max_tracks |
fully_cached_skipped |
integer | yes | Complete candidates skipped without spending a work slot |
next_offset |
integer | null | yes | Underlying index after the last examined candidate when more remain; null at the end |
has_more |
boolean | yes | Whether an unexamined underlying candidate remains |
summary.tracks_total is page.examined_tracks; provider totals and cached
counts include fully cached candidates skipped before work admission. The
response remains bounded because only selected pending work can produce
failures or other per-track task output.
Continue from page.next_offset while page.has_more is true. Record failures
while traversing, then retry those explicit track_ids separately. Offsets are
stable only while the selector scope and library ordering are unchanged; after
an import, deletion, or reorder, restart at zero. Keep providers,
skip_cached, and force_refresh unchanged for the whole traversal; changing
any of them changes which earlier candidates count as complete and also
requires a restart at zero. max_tracks=0 reports the matched scope but
intentionally returns a terminal no-op page, so use a positive cap for
traversal.
The response summary reports cache persistence under summary.cache_writes:
| Field | Description |
|---|---|
attempted |
Cache writes received by the single persistence writer |
succeeded |
Writes acknowledged after the enrichment row was stored durably |
failed |
Writes rejected because the cache store could not be opened or updated |
summary.enriched and summary.skipped advance only after the matching
positive or negative cache row is acknowledged as durable. A cache-write
failure increments summary.failed, appears in failures with track and
provider context, and is never also reported as enriched or skipped.
Audio analysis tools
Section titled “Audio analysis tools”The two backends return separate evidence and are cached independently.
Stratum is compiled in; Essentia is optional. Neither backend returns a field
named energy: scoring derives energy from three Essentia fields when all are
present, otherwise it uses a BPM proxy.
Returned and cached evidence
Section titled “Returned and cached evidence”The tables below group every public field family. In a fresh analyzer result,
optional detector values serialize as null when a detector cannot run or a
beat grid is unavailable. Cache freshness rejects results from older schemas;
the live analyze_track_audio response remains the field-level schema
authority.
| Stratum family | Returned fields | Notes |
|---|---|---|
| Tempo and key | bpm, bpm_confidence, key, key_camelot, key_confidence, key_clarity |
Confidence and clarity are evidence, not guarantees that every downstream consumer uses them. |
| Grid provenance | grid_stability, grid_source |
grid_source distinguishes a supplied Rekordbox grid from the HMM fallback. The cache also records a versioned grid/no-grid input fingerprint. |
| Analysis metadata | duration_seconds, processing_time_ms, analyzer_version, flags, warnings |
Cache freshness additionally requires the current Stratum schema version and current audio-file identity. |
| Modulation and decay | mod_centroid, harmonic_proportion, decay_mid_tau, decay_mid_r2, decay_high_tau, decay_high_r2 |
Optional modulation-centroid, harmonic-proportion, and fitted decay evidence. |
| Dub-stab timing | dub_stab_onset_count, dub_stab_onset_rate, dub_stab_rate_basis, dub_stab_histogram, dub_stab_template, dub_stab_template_score |
Optional onset, 32-bin histogram, rate-basis, and template evidence. |
| Kick pattern | kick_pattern, kick_pattern_confidence, kick_kicks_per_bar, kick_onset_count, kick_rate_basis, kick_histogram |
Optional pattern label, confidence, counts, basis, and flattened 4×16 histogram. |
| Structure | sections |
Optional ordered Intro/MainGroove/Breakdown/Outro sections with start/end time, kick-band RMS, and broadband RMS. |
| Essentia family | Returned fields | Notes |
|---|---|---|
| Analysis metadata | analyzer_version |
Cache freshness also requires the current Essentia schema version and audio-file identity; grid changes do not invalidate Essentia. |
| Dynamics and loudness | danceability, loudness_integrated, loudness_range, dynamic_complexity, average_loudness |
Optional descriptors. There is no returned energy field. |
| Tempo and rhythm | bpm_essentia, onset_rate, rhythm_regularity |
Optional independent tempo and onset evidence. |
| Brightness, dissonance, and intensity | spectral_centroid_mean, dissonance_mean, intensity_mean, intensity_var |
Optional scalar spectral/timbral evidence. |
| Detailed timbre and spectrum | mfcc_mean, mfcc_std, spectral_centroid_cv, spectral_flux_mean, spectral_flux_iqr, spectral_contrast_mean |
Optional vectors and summary statistics used selectively by calibration and pool scoring. |
Which evidence is consumed
Section titled “Which evidence is consumed”Returned evidence is broader than the current scoring inputs. In particular, dub-stab, modulation/harmonic, structure, and most kick-pattern fields are available for inspection but do not directly alter transition or pool scores.
| Consumer | Current audio inputs |
|---|---|
Genre classification/audit rule-based summary (classify.rs::AudioProfile) |
Rekordbox/Stratum BPM agreement; detector-consensus fallback is considered only when finite Essentia danceability is at least 1.5 (the Dancefloor or HighEnergy buckets). In that case, when the Stratum and Essentia BPM detectors agree with each other but disagree with Rekordbox (and are not a near double-time match), their mean replaces the Rekordbox BPM. Lower or missing danceability retains the Rekordbox BPM. Other inputs are Stratum duration, key_confidence, and decay_mid_tau; plus Essentia danceability, dynamic complexity, rhythm regularity, spectral centroid mean, and loudness_range. Extracted kick evidence is returned for review but is not a direct current decision input. |
Calibrated genre-affinity profiles (audio_profile.rs), calibration, and affinity scoring |
Rekordbox BPM; Stratum decay_mid_tau, decay_high_tau, and key clarity; Essentia danceability, onset rate, rhythm regularity, centroid mean/CV, dynamic complexity, integrated loudness, spectral flux mean, dissonance mean, MFCC mean/std, and spectral contrast mean. Missing optional families reduce coverage rather than becoming zeroes. |
Transition scoring and build_set |
Stratum key (preferred) and BPM (fallback); derived energy; Essentia brightness, rhythm regularity, and loudness range. Only build_set adds sequence-context modifiers. |
| Pool scoring and discovery | Key, BPM, derived energy, canonical genre/family, Essentia brightness, and rhythm regularity. When every required timbral family is available, an optional distance axis uses MFCC mean/std, spectral contrast mean, spectral centroid variability, and dissonance. Pool scoring does not consume loudness_range. |
| Cache/audit coverage | Analyzer freshness and presence counts; coverage does not imply that every returned field is consumed. |
Derived energy is calculated only when Essentia has danceability,
loudness_integrated, and onset_rate: the normalized inputs are weighted
0.4/0.3/0.3 and clamped to 0–1. If any one is missing—or Essentia is
unavailable—the entire value falls back to the BPM proxy; it is not a partial
Essentia score.
analyze_track_audio
Section titled “analyze_track_audio”Analyze one track. Returns the Stratum evidence families above. The response
always includes an essentia key plus essentia_available,
essentia_cache_hit, and essentia_error context. essentia contains the
separate result when analysis succeeds and is null when Essentia is
unavailable or its analysis fails; optional fields inside a fresh result also
serialize as null when unavailable.
| Parameter | Type | Required | Description |
|---|---|---|---|
track_id |
string | yes | Track ID |
skip_cached |
boolean | Skip if already cached (default: true) |
Cached analysis is validated against the analysis schema plus the canonical audio file identity (path, size, and modification time). Stratum additionally fingerprints the Rekordbox beat grid used for analysis, or a versioned no-grid fallback. A grid change invalidates and recomputes the Stratum result while leaving the Essentia result fresh.
analyze_audio_batch
Section titled “analyze_audio_batch”Batch audio analysis. Accepts shared search filters to scope which tracks to analyze.
| Parameter | Type | Required | Description |
|---|---|---|---|
track_ids |
string[] | Specific track IDs (highest priority selector) | |
playlist_id |
string | Analyze tracks in this playlist | |
max_tracks |
integer | Max pending tracks to admit after freshness checks (default: 20, max: 200) |
|
offset |
integer | Index into the stable underlying selector order (default: 0) |
|
skip_cached |
boolean | Skip already-cached tracks (default: true) |
|
concurrency |
integer | Max concurrent analyses (default: cpus - 2, min: 1, max: 4) |
Current Stratum results are skipped before work slots are assigned. When Essentia is available, a track is complete only when both current analyzer results exist; when it is unavailable, missing Essentia does not keep the track pending.
| Field | Type | Required | Description |
|---|---|---|---|
summary |
object | yes | Counts for the inspected continuation page, including fully current candidates |
results |
object[] | yes | Per-track analysis results |
failures |
array | yes | Retryable failures with track, analyzer, and stage context |
page |
object | yes | Underlying-selector continuation metadata |
essentia_setup_hint |
string | Setup guidance when Essentia is unavailable |
| Field | Type | Required | Description |
|---|---|---|---|
matched_tracks |
integer | yes | Total tracks in the logical selector at call time |
start_offset |
integer | yes | Underlying selector index supplied by the caller |
examined_tracks |
integer | yes | Candidates inspected from start_offset |
selected_tracks |
integer | yes | Pending tracks admitted under max_tracks |
fully_cached_skipped |
integer | yes | Current candidates skipped without spending a work slot |
next_offset |
integer | null | yes | Underlying index after the last examined candidate when more remain; null at the end |
has_more |
boolean | yes | Whether an unexamined underlying candidate remains |
summary.total is page.examined_tracks. Its Stratum cache count, and its
Essentia cache count when Essentia is available, include fully current
candidates skipped before work admission. results remains bounded to selected
pending tracks.
Traverse next_offset while has_more, retain failures, and retry failed
track_ids after the scope traversal. Restart at zero if the selector or
library order changes. Keep Essentia availability unchanged during traversal;
also keep skip_cached unchanged. If either changes, including Essentia being
installed, removed, or newly detectable, restart at zero because earlier tracks
may have changed completion state. A zero cap is an inspection-free terminal
no-op.
setup_essentia no params
Section titled “setup_essentia ”Install or validate Essentia in a local Python venv. Creates a venv at ~/.local/share/reklawdbox/essentia-venv/ if needed. Activation takes effect immediately — no server restart needed.
