Sync data between local and remote libraries
- In this example, you will:
Search for local tracks on a music streaming service and assign unique remote IDs to tags in your local tracks
Get tags and images for a track from a music stream service and save it them to your local track file
Create remote playlists from your local playlists
Note
This guide will use Spotify, but any supported music streaming service can be used in generally the same way. Just modify the imports and classes as required.
Set up logging
Set up logging to ensure you can see all info reported by the later operations.
Libraries log info about loaded objects to the custom STAT
level.
import logging
import sys
from musify.logger import STAT
logging.basicConfig(format="%(message)s", level=STAT, stream=sys.stdout)
Sync data
Define a helper function to search for tracks and check the results:
from collections.abc import Collection from musify.libraries.core.collection import MusifyCollection from musify.libraries.remote.core.factory import RemoteObjectFactory from musify.processors.search import RemoteItemSearcher from musify.processors.check import RemoteItemChecker from musify.processors.match import ItemMatcher async def match_albums_to_remote(albums: Collection[MusifyCollection], factory: RemoteObjectFactory) -> None: """Match the items in the given ``albums`` to the remote API's database and assign URIs to them.""" matcher = ItemMatcher() searcher = RemoteItemSearcher(matcher=matcher, object_factory=factory) async with searcher: await searcher(albums) checker = RemoteItemChecker(matcher=matcher, object_factory=factory) async with checker: await checker(albums)
Define a helper function to load the matched tracks, get tags from the music streaming service, and save the tags to the file:
Note
By default, URIs are saved to the
comments
tag.from musify.libraries.local.collection import LocalAlbum async def sync_albums(albums: list[LocalAlbum], factory: RemoteObjectFactory) -> None: """Sync the local ``albums`` with tag data from the api in the given ``factory``""" async with factory.api: for album in albums: for local_track in album: remote_track = await factory.track.load(local_track.uri, api=factory.api) local_track.title = remote_track.title local_track.artist = remote_track.artist local_track.date = remote_track.date local_track.genres = remote_track.genres local_track.image_links = remote_track.image_links # alternatively, just merge all tags local_track |= remote_track # save the track here or... await local_track.save(replace=True, dry_run=False) # ...save all tracks on the album at once here await album.save_tracks(replace=True, dry_run=False)
Define a helper function to sync the local playlist with a remote playlist once all tracks in a playlist have URIs assigned:
from musify.libraries.local.library import LocalLibrary from musify.libraries.remote.core.library import RemoteLibrary async def sync_local_playlist_with_remote(name: str, local_library: LocalLibrary, remote_library: RemoteLibrary): """Sync ``local_library`` playlist with given ``name`` to its matching ``remote_library`` playlist.""" async with api: await remote_library.load_playlists() local_playlist = local_library.playlists[name] remote_playlist = remote_library.playlists[name] # sync the object with Spotify and pretty print info about the reloaded remote playlist await remote_playlist.sync(items=local_playlist, kind="new", reload=True, dry_run=False) print(remote_playlist)
Set up and load a remote API object and local library with a wrangler attached:
from musify.libraries.remote.spotify.api import SpotifyAPI api = SpotifyAPI( client_id="<YOUR CLIENT ID>", client_secret="<YOUR CLIENT SECRET>", scope=[ "user-library-read", "user-follow-read", "playlist-read-collaborative", "playlist-read-private", "playlist-modify-public", "playlist-modify-private" ], # providing a `token_file_path` will save the generated token to your system # for quicker authorisations in future token_file_path="<PATH TO JSON TOKEN>" )
from musify.libraries.local.library import LocalLibrary import asyncio local_library = LocalLibrary( library_folders=["<PATH TO YOUR LIBRARY FOLDER>", ...], playlist_folder="<PATH TO YOUR PLAYLIST FOLDER>", # this wrangler will be needed to interpret matched URIs as valid remote_wrangler=api.wrangler, ) asyncio.run(local_library.load()) albums = local_library.albums
Set up the remote library and run the program:
from musify.libraries.remote.spotify.library import SpotifyLibrary remote_library = SpotifyLibrary(api=api) playlist = "<YOUR PLAYLIST'S NAME>" # case sensitive asyncio.run(match_albums_to_remote(albums, remote_library.factory)) asyncio.run(sync_albums(albums, remote_library.factory)) asyncio.run(sync_local_playlist_with_remote(playlist, local_library, remote_library))