Skip to content

Music Recommendation Domain Notes

MusicSeed recommendations are built from signals already available in a personal Plex library plus lightweight external enrichment.

Core Entities

  • Artist: performer or credited artist imported from Plex.
  • Album: release container imported from Plex.
  • Track: playable recording with metadata, file path, identifiers, tags, popularity, and Plex references.
  • Mood, style, genre: Plex tag dimensions used as soft recommendation signals.
  • Play history and track stats: listening behavior used for novelty and discovery.
  • Playlist: selected recommendation output intended for Plex.

Recommendation Signals

  • Sonic similarity: cosine similarity between the seed profile's average Plex sonic vector and candidate vectors.
  • Popularity proximity: closeness to the seed track popularity, not a generic popularity boost.
  • Style alignment: overlap between seed and candidate styles.
  • Genre alignment: overlap between seed and candidate genres.
  • Era proximity: release year closeness.
  • Novelty: favors less-played tracks using local play counts.

Mood was removed from scoring and candidate generation. Plex mood tags remain in the database and are visible in status, but they introduce noise rather than a reliable ranking signal and have been excluded from SeedProfile, Weights, ScoreBreakdown, and build_candidate_pool().

Missing signals should degrade gracefully. A track without a popularity value or sonic vector should not crash the recommendation flow; it should receive neutral or lower component scores depending on the scoring function.

Popularity

Popularity is a supporting signal, not the main product. It should help distinguish candidates inside the owner's collection, not turn recommendations into a global chart.

Preferred source order:

  1. ListenBrainz recording popularity by MusicBrainz recording MBID.
  2. Spotify popularity from matched tracks when ListenBrainz is unavailable or insufficient.

ListenBrainz raw counts are normalized into Track.popularity_score on a 0-1 scale. Spotify popularity is a 0-100 provider value. Scoring converts the best available value to a comparable 0-100 scale before computing proximity to the seed profile.

Sonic Vectors

Sonic similarity uses Plex's own sonic analysis vectors. Plex stores one 50-dimensional vector per analyzed track in com.plexapp.plugins.library.blobs.db; MusicSeed reads them straight from that database at query time (core/src/musicseed/sonic.py) into an in-memory, L2-normalized matrix keyed by plex_id. Nearest-neighbor search is a single numpy matmul — trivially fast at personal-library scale — so there is no vector index and no stored copy that could drift out of date. MusicSeed does not generate its own embeddings (the Essentia pipeline was removed) and never reads audio files.

Coverage is Plex's responsibility. A track Plex hasn't analyzed simply has no vector and receives a neutral 0.5 sonic score. If the Plex blobs database itself is unavailable, recommend fails with NotFoundError rather than degrading silently. Check coverage with sonic-probe; trigger analysis with sonic-refresh.

Use sonic similarity as one signal among several. A recommendation should still produce reasonable results for tracks without vectors by falling back to tags, era, popularity, and novelty.

Diversity

Artist diversity is a selection constraint, not a score component. The current recommender limits the number of selected tracks per artist after scoring. This makes the ranking easier to explain: scores measure fit, constraints shape the final playlist.

Matching Expectations

External catalog matching is inherently imperfect. Prefer precision over coverage for a personal library:

  • MBID-based ListenBrainz lookups are safer than text search.
  • Spotify search matches should be conservative.
  • Ambiguous seed text should ask the user to choose a more specific seed or use --seed-id.
  • Avoid silently choosing among multiple plausible seed matches.

Recommendation Quality Checks

When changing recommendation logic, inspect:

  • Does --explain still make sense to a human?
  • Are seed tracks excluded from candidates?
  • Does the candidate pool include more tracks than the requested playlist length?
  • Are missing metadata values handled without exceptions?
  • Does artist diversity still apply after scoring?
  • Do weights normalize correctly when users adjust them?