Core Runtime and Models¶
MusicSeedContext owns a resolved config, a lazy SQLite engine/session factory, and the local
sonic-vector cache. Pass context= to services to isolate an operation. use_context binds
legacy helpers to an operation; otherwise they use the process default. Changing default config
resets that default context. Background jobs capture a deep copy so a config change cannot
redirect a running job to a different database.
Vectors come from track_vectors, not Plex source files during recommendation. Cached vectors
check runtime_state.sonic_generation, incremented by each committed import batch. See
local runtime for writer claims, import provenance, and recovery.
Context¶
musicseed.context
¶
Runtime context that bundles config with its derived database and sonic resources.
MusicSeed's services used to read three pieces of process-global state
directly: the resolved Config, a database engine/session factory, and a
cached SonicVectors matrix (loaded from the local database). The engine
and session factory are derived from config; hiding all three behind module
singletons made service behavior depend on call order and forced config
changes to reach into reset_engine().
MusicSeedContext groups them into one object that a surface (or a test)
can construct explicitly and hand to a service. The module-level default
context keeps the existing convenience entry points working without passing a
context everywhere.
MusicSeedContext
dataclass
¶
A resolved config plus the lazily-created database and sonic resources.
The session factory and sonic vectors are built on first use and cached on the instance, so a context is cheap to create and safe to reuse across many service calls. It is intentionally not frozen: the lazily-populated fields are implementation details, not part of its identity.
engine
property
¶
The SQLite engine for this context's database (created on first use).
session_factory
property
¶
A sessionmaker bound to this context's engine.
sonic_vectors
property
¶
Plex sonic vectors persisted in this context's database.
Loaded from the local track_vectors table and cached until the
persisted generation changes, including imports by another process.
Returns an empty SonicVectors when none are stored.
session()
¶
Open a database session with commit/rollback/close semantics.
Mirrors musicseed.db.session.get_session but is bound to this
context's engine rather than the process-global default.
reset_sonic_vectors()
¶
Drop this context's cached vectors so the next access reloads them.
get_bound_context()
¶
An operation's context, if explicitly bound (never constructs globals).
use_context(context)
¶
Bind legacy helpers and callbacks to one operation, including in worker threads.
get_context()
¶
Return the default context, building it from the global config on first use.
set_context(context)
¶
Install context as the default context for the process.
reset_context()
¶
Drop the default context so the next access rebuilds it from current config.
Configuration¶
musicseed.config
¶
Configuration loading and management.
DatabaseConfig
¶
Bases: BaseModel
url
property
¶
SQLAlchemy connection URL only — never pass this to sqlite3.connect.
Filesystem callers must use path_expanded (url is a sqlite://
URL, not a path).
PlexConfig
¶
Bases: BaseModel
blobs_db_path_expanded
property
¶
Path to the Plex blobs database, which holds sonic analysis vectors.
load_config(config_path=None)
¶
Load configuration from YAML file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config_path
|
Path | None
|
Path to config file. If None, uses default locations. |
None
|
Returns:
| Type | Description |
|---|---|
Config
|
Loaded Config object with environment variables expanded. |
default_config_path()
¶
Canonical config location used when no existing file was resolved.
get_config()
¶
Get the operation-bound config, falling back to the process default.
get_config_path()
¶
Return the resolved config file path, or None if no file was found.
Used by discovery to derive the "no config file yet" first-run signal.
set_config(config)
¶
Set the global config instance and drop the derived runtime context.
The default MusicSeedContext caches an engine and sonic vectors derived
from config, so replacing the config must invalidate it; the next access
rebuilds from this config.
save_config(config, path=None)
¶
Persist config to the YAML file it was loaded from (or path).
Writes back to the path load_config resolved and records it so later
saves target the same file. Falls back to the canonical default location
when no file has been resolved yet.
Returns:
| Type | Description |
|---|---|
Path
|
The path written to. |
Service DTOs¶
musicseed.services.schemas
¶
JSON-safe DTOs returned by core services.
These are the stable shapes core services hand to surfaces (CLI, API, future MCP). They carry no SQLAlchemy objects, so a caller never depends on ORM loading strategy or session lifetime.
ServiceTrack
¶
Bases: BaseModel
A track projected from the ORM into a stable, JSON-safe shape.
ServiceRecommendation
¶
Bases: BaseModel
A scored recommendation with its track, breakdown, and candidate sources.
to_service_track(track)
¶
Project an ORM Track into a ServiceTrack.
Call while the track's artist/album relationships are still loaded (i.e. inside the session that produced it).
to_service_recommendation(rec)
¶
Project a recommender Recommendation into a ServiceRecommendation.
Exceptions¶
musicseed.exceptions
¶
MusicSeed domain exceptions.
MusicSeedError
¶
Bases: Exception
Base exception for all MusicSeed errors.
ConfigurationError
¶
Bases: MusicSeedError
Required configuration is missing or invalid.
NotFoundError
¶
Bases: MusicSeedError
A requested resource (seed track, file, playlist) could not be found.
JobConflictError
¶
Bases: MusicSeedError
A job of the requested kind is already running.
Database sessions and schema compatibility¶
musicseed.db.session
¶
Database session management.
Pure engine/session factory functions live here; the module-level
get_engine/get_session/reset_engine conveniences delegate to the
default MusicSeedContext (see musicseed.context).
IndexResult
¶
Bases: BaseModel
Result from one index creation statement.
create_engine_for_url(url)
¶
Create a SQLite engine with MusicSeed's connect-time pragmas.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
SQLAlchemy connection URL ( |
required |
Returns:
| Type | Description |
|---|---|
Engine
|
A new engine that enables WAL journaling, foreign keys, and a busy |
Engine
|
timeout on every connection. |
create_session_factory(engine)
¶
Create a session factory bound to engine.
Uses expire_on_commit=False for internal multi-step workflows. Public
services still project scalar DTOs inside their session; this setting does
not make unloaded ORM relationships safe to access after session closure.
get_engine()
¶
Get the default context's SQLite engine (created on first use).
get_session_factory()
¶
Get the default context's session factory.
get_session()
¶
Get a database session from the default context as a context manager.
init_db(context=None)
¶
Initialize the database schema, creating the DB file's parent dir if needed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
context
|
MusicSeedContext | None
|
runtime context to operate on; defaults to the default context. |
None
|
ensure_schema(context=None)
¶
Apply lightweight additive schema updates for existing local databases.
New tables are created via Base.metadata.create_all(checkfirst=True);
additive column migrations are handled per-table via the PRAGMA list.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
context
|
MusicSeedContext | None
|
runtime context to operate on; defaults to the default context. |
None
|
create_indexes(context=None)
¶
Create additional indexes (call after initial data load).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
context
|
MusicSeedContext | None
|
runtime context to operate on; defaults to the default context. |
None
|
Returns:
| Type | Description |
|---|---|
list[IndexResult]
|
Per-index results describing success or failure for each index. |
reset_engine()
¶
Drop the default context (engine, session factory, and sonic cache).
Preserved as a test/config-change hook; with explicit contexts the
preferred reset is to construct and install a fresh MusicSeedContext.
Database models¶
musicseed.db.models
¶
SQLAlchemy database models.
Base
¶
Bases: DeclarativeBase
Base class for all models.
Sonic vectors¶
musicseed.sonic
¶
Plex sonic analysis vectors, imported from Plex and stored locally.
Plex stores a sonic-analysis vector per analyzed track in
com.plexapp.plugins.library.blobs.db. load_sonic_vectors reads those
vectors straight from that database once — it is the import reader used by
services.sonic_vectors.import_plex_sonic — and sonic_vectors_from_mapping
rebuilds the in-memory L2-normalized matrix from the locally persisted
track_vectors table. Recommendations read the local store, so the blobs
database is only needed when importing (or re-importing) vectors.
Both Plex databases are opened read-only and are in WAL mode, so loading vectors neither blocks nor is blocked by a running Plex Media Server.
SonicVectors
¶
An in-memory view of a Plex music library's sonic vectors.
Vectors are stored as one L2-normalized (n_tracks, PLEX_SONIC_DIM) matrix
so similarity search is a single matrix multiplication.
plex_ids
property
¶
Every Plex track id that has a sonic vector.
get(plex_id)
¶
Return the raw vector for a Plex track id, or None if it has none.
nearest(query, limit, *, allowed=None)
¶
Return the Plex ids most cosine-similar to query, best first.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
ndarray
|
the seed embedding (L2-normalized here). |
required |
limit
|
int
|
maximum number of neighbors to return. |
required |
allowed
|
set[int] | None
|
optional set of Plex ids to restrict the search to. Ids
without a stored vector are ignored. When |
None
|
decode_sonic_blob(blob)
¶
Decode one Plex sonic blob (gzipped ASCII CSV) into a float vector.
Returns None when the blob is unreadable or is not the expected dimension.
load_sonic_vectors(plex_db_path, blobs_db_path, library_name)
¶
Read every sonic vector for one Plex music library.
Raises:
| Type | Description |
|---|---|
NotFoundError
|
if either Plex database file is missing or unreadable. |
sonic_vectors_from_mapping(vectors)
¶
Build a SonicVectors matrix from a {plex_id: vector} mapping.
Used to reconstruct the in-memory matrix from the locally persisted
track_vectors table. Every vector must already be PLEX_SONIC_DIM
long (load_sonic_vectors guarantees this via decode_sonic_blob).
get_sonic_vectors()
¶
Get the default context's sonic vector store, loading it on first use.
Thin wrapper over musicseed.context.get_context().sonic_vectors so
callers that don't pass an explicit context keep working during the
context migration.
reset_sonic_vectors()
¶
Drop the default context's cached vectors (testing / config changes).
Plex database snapshots¶
musicseed.plex_db_source
¶
Resolve local Plex files or validated, atomically published SSH snapshots.
Remote imports run a standard-library Python SQLite-backup helper on the host and stream its standalone files over SSH. Never copy live DB/WAL/SHM files. Recommendation runtime and coverage use local data without network requests.
ResolvedPlexDbs
dataclass
¶
Stable local paths; an absent optional blobs database has a missing path.
parse_ssh_target(target)
¶
Split [user@]host:/directory; tolerate pasted shell-escaped spaces.
ssh_file_exists(target, filename, *, password='', port=22, timeout=10.0)
¶
Return presence or an actionable error; expected failures never raise.
resolve_plex_dbs(config, *, refresh=False, ssh_target=None)
¶
Return local files or one stable SSH snapshot generation.
Refresh performs source-side SQLite backups over SSH and atomically replaces the cache pointer only after validation. Coverage never connects to SSH. Old generations are retained for in-flight readers; remove the source cache manually only when no imports/readers are active. Legacy live-copy caches are intentionally ignored. The optional blobs path may not exist.