Skip to content

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 (sqlite:///...).

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.

Job

Bases: Base

A long-running, recoverable unit of work (import, enrich, etc.).

RuntimeState

Bases: Base

Small persisted cache revisions, shared by API and CLI contexts.

ImportState

Bases: Base

Source-specific import checkpoint, independent of deletable job history.

TrackVector

Bases: Base

A Plex sonic-analysis vector persisted locally (MUS-83).

Stores the raw (un-normalized) 50-dimension vector read from Plex's blobs database so recommendations no longer need that file at query time.

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, every stored vector is a candidate.

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.