How Music Assistant Synchronizes Your Library Across Multiple Music Providers
Music Assistant aggregates content from Spotify, YouTube Music, local files, and other sources into a unified library by running a provider-agnostic synchronization routine that reconciles external catalogs with a local SQLite database while preventing data loss when the same media exists across multiple providers.
Music Assistant is an open-source music server designed to consolidate your entire music collection into a single interface. The music-assistant/server repository implements a robust synchronization architecture that enables seamless Music Assistant library sync across multiple providers without duplicating entries or losing user favorites. This system allows users to browse artists, albums, and tracks regardless of whether they originate from streaming services or local storage.
Architecture Overview
The synchronization system operates through four distinct layers, each with specific responsibilities defined in the source code.
Provider Base Class – Located in music_assistant/models/music_provider.py, this layer defines the public sync_library method and internal helpers such as _sync_library_artists, _sync_library_albums, and _sync_library_tracks. These methods handle the actual database operations at lines 690 through 711.
Concrete Providers – Individual services like Spotify (music_assistant/providers/spotify/provider.py) and WebDAV (music_assistant/providers/webdav/provider.py) implement media-specific generators such as get_library_tracks and get_library_artists that yield provider-native IDs and metadata.
Sync Controller – The orchestration layer in music_assistant/controllers/music/controller.py (line 2334) initiates sync requests by calling await provider.sync_library(media_type) for specific media types.
Database Layer – All library items persist in $HOME/.musicassistant/library.db, where each row stores the provider ID, media type, and a JSON-encoded sources array tracking which providers claim each item.
How the Sync Process Works
The library synchronization follows a strict reconciliation pattern that ensures data consistency across multiple music providers.
Entry Point and Media Type Dispatch
When a sync initiates, the system calls MusicProvider.sync_library(media_type) defined at line 690 of music_assistant/models/music_provider.py. This method branches to type-specific internal helpers:
if media_type == MediaType.ARTIST:
cur_db_ids = await self._sync_library_artists()
elif media_type == MediaType.ALBUM:
cur_db_ids = await self._sync_library_albums()
# … extends to TRACK, PLAYLIST, PODCAST, RADIO, AUDIOBOOK
Each helper (lines 699-711) operates as an async generator consumer, pulling the current state from the provider's API and comparing it against existing database records.
Database Reconciliation and Updates
The internal helpers retrieve the current set of IDs from the provider and return the set of database row IDs that correspond to active items. For tracks, this occurs in _sync_library_tracks (referenced around line 1159).
The system performs three critical operations:
- Insertion – New items present on the provider but absent from the database are added via the
library_addmethod. - Metadata refresh – Existing entries receive updated metadata regardless of deletion settings, ensuring the UI displays current information.
- Source tracking – The
sourcesJSON field tracks every provider claiming the item, enabling the unified library view.
Safe Deletion Logic
When an item disappears from a provider, the sync routine checks whether other providers still claim it before removing the favorite status or deleting the entry. This safety mechanism prevents accidental loss of library items that exist across multiple providers.
The behavior is verified in tests/test_library_sync.py, which confirms that:
- Items are unmarked as favorites only when no other providers reference them.
- Items persist in the library if at least one alternative provider still offers them.
Provider Implementation Requirements
To participate in library synchronization, a provider must implement async generators for each supported media type. The Spotify provider demonstrates this pattern:
async def get_library_tracks(self) -> AsyncGenerator[Track]:
async for track_data in self.api.list_saved_tracks():
yield Track(
item_id=track_data["id"],
name=track_data["name"],
# …additional metadata fields
)
The base class automatically consumes these generators during sync_library execution, requiring no additional synchronization code unless the provider needs custom rate-limiting or pagination handling.
Practical Usage Examples
Triggering Provider-Specific Syncs
You can manually synchronize specific media types for any configured provider:
await mass.providers.spotify.sync_library(MediaType.TRACK)
await mass.providers.spotify.sync_library(MediaType.ALBUM)
await mass.providers.spotify.sync_library(MediaType.PLAYLIST)
These calls execute the generic sync_library method, which handles the reconciliation logic automatically.
Home Assistant Service Calls
Force a sync using the Home Assistant integration:
service: music_assistant.sync_library
data:
provider: spotify
media_type: track
Inspecting the Unified Database
View how Music Assistant merges entries from multiple sources:
sqlite3 ~/.musicassistant/library.db "SELECT provider, item_id, sources FROM library_items WHERE media_type='track';"
The sources column displays a JSON array listing every provider that reported each track, demonstrating the multi-provider consolidation in action.
Summary
- Music Assistant library sync operates through a centralized
sync_librarymethod inmusic_assistant/models/music_provider.pythat works uniformly across all provider types. - Provider implementations only need to supply async generators like
get_library_tracks, while the base class handles database reconciliation and conflict resolution. - Deletion safety ensures that removing a provider or losing access does not delete library items that exist on other connected services.
- Metadata caching keeps the local database refreshed with current information from all sources, enabling fast UI rendering and consistent search results.
Frequently Asked Questions
How does Music Assistant prevent duplicate entries when syncing the same album from Spotify and local files?
Music Assistant tracks every provider claiming an item in the sources JSON field of the database. When the same album appears in both Spotify and local storage, the sync routine recognizes it as a single library entry with multiple sources, displaying it once in the UI while maintaining links to both playback locations.
Can I force a library sync for all providers simultaneously?
While the controller typically manages sync scheduling, you can trigger individual provider synchronization using the Home Assistant service music_assistant.sync_library or programmatically calling sync_library() for each media type on each provider instance. There is no single "sync all" command; each provider handles its own media types independently.
What happens to my favorites if I disconnect a music provider?
Favorites remain intact as long as the item exists on at least one other connected provider. The deletion logic in music_assistant/models/music_provider.py checks the sources array before removing favorite status. Only when no providers claim an item does the system mark it as removed or delete the entry entirely.
Where does Music Assistant store the synchronized library data?
The unified library persists in $HOME/.musicassistant/library.db as a SQLite database. This file contains metadata, provider mappings, and source tracking information, while the actual audio files remain on their respective provider systems or local storage paths.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →