async def update_spotify_playlist(tracks: Iterable, playlist_uri: str, sp: Spotter, insert_top: bool = False): """Add the ids from track iterable to the playlist, insert_top bool does it on top of all the items, for fridays""" index = 0 for album_ids in grouper(100, tracks): album_ids = clean(album_ids) album_ids = [a["id"] for a in album_ids] args = (sp.userdata["id"], playlist_uri, album_ids) if insert_top: args = args + (index, ) # type: ignore try: await sp.client.user_playlist_add_tracks(*args) index += len(album_ids) - 1 await sleep(0.2) except Exception as exc: _LOGGER.exception( f"{sp.userdata['id']} fail on POST playlist items {album_ids}") push_sentry_error(exc, sp.userdata["id"], sp.userdata["display_name"])
async def send_message(fb_id: str, fb_alert: FBAlert, data: str): """send fb user text message""" fb_token = {"access_token": fb_alert.key} # max message size is 2000 for msg_chunk in grouper(2000, data): clean_chunk = [a for a in msg_chunk if a is not None] contents = { "recipient": { "id": fb_id }, "message": { "text": "".join(clean_chunk) }, "tag": "ACCOUNT_UPDATE", } async with aiohttp.ClientSession() as session: async with session.post(fb_alert.url, json=contents, params=fb_token) as res: if res.status != 200: try: res.raise_for_status() except aiohttp.ClientError as exc: _LOGGER.exception(f"{res} {res.content}") push_sentry_error(exc, fb_id)
async def _update_user_artists( credentials: SpotifyKeys, user: User, force_update: bool, dry_run: bool ): """task to update user followed artists and artist albums""" user_config = WeeklyPlaylistUpdateConfig(**user.config[_CONFIG_ID]) if not user_config.enabled: return try: async with spotify_client(credentials, user) as sp: new_follows, lost_follows = await sync_user_followed_artists( user, sp, dry_run ) followed_artists = await user.artists.filter() _LOGGER.info( f"{user} : follows +{len(new_follows)} -{len(lost_follows)}" f"; now updating artists ({len(followed_artists)})" ) albums_nr, updated_nr = await get_new_releases( sp, followed_artists, force_update ) if updated_nr: _LOGGER.info(f"fetched {albums_nr} albums for {updated_nr} artists") except Exception as exc: _LOGGER.exception(f"{user} failed to update artists") push_sentry_error(exc, sp.userdata["id"], sp.userdata["display_name"])
async def _handle_update_user_playlist(credentials: SpotifyKeys, user: User, dry_run: bool, fb_alert: FBAlert): user_config = WeeklyPlaylistUpdateConfig(**user.config[_CONFIG_ID]) if not user_config.enabled: return try: async with spotify_client(credentials, user) as sp: stats = await update_user_playlist(user, sp, dry_run) if fb_alert.notify and stats.has_new_tracks(): await send_message(user.fb_id, fb_alert, stats.describe()) except SpotifyException as exc: _LOGGER.warning(f"{user} failed to update playlist") push_sentry_error(exc, user.username, user.display_name)
async def update_artist_albums( sp: Spotter, artist: Artist, dry_run: bool = False) -> Tuple[Artist, List[Album]]: """update artist albums by adding them to the db""" albums = await fetch_albums(sp, artist) processed_albums = [] for album in albums: if album["album_type"] == "compilation": # these types of albums are just a collection of already knows songs # places in a new compilation album; gonna skipe those for a while # until more control of the flow continue if not is_in_artists_list(artist, album): # Some releases are not directly related to the artist himself. # It happens that if an artist has some feature songs on an album # for a different artist, it will be returned to the artist in cause # as well, so that this function checks that, and returns only feature # songs if it's the case. tracks = await get_featuring_songs(sp, artist, album) processed_albums.extend(tracks) else: processed_albums.append(album) args_items = [(album, artist) for album in processed_albums] task_results = await run_tasks(Config.ALBUMS_TASKS_AMOUNT, args_items, handle_album_sync) try: await artist.update_synced_at() except Exception as exc: _LOGGER.exception( f"{sp.userdata['id']} failed to update {artist} synced_at") push_sentry_error(exc, sp.userdata["id"], sp.userdata["display_name"]) new_inserts = [a for created, a in task_results if created] return artist, new_inserts
async def fetch_albums(sp: Spotter, artist: Artist, retry: bool = True) -> List[dict]: """fetches artist albums from spotify""" try: data = await sp.client.artist_albums( artist.spotify_id, limit=50, album_type="album,single,appears_on") albums = await fetch_all_albums(sp, data) except SpotifyException as exc: _LOGGER.warning(f"failed fetch artist albums for `{artist}`: {exc}") return [] except Exception as exc: if not retry: _LOGGER.exception( f"{sp.userdata['id']} failed to fetch all {artist} albums") push_sentry_error(exc, sp.userdata["id"], sp.userdata["display_name"]) raise _LOGGER.warning( f"{sp.userdata['id']} attempt another fetch all {artist} albums") albums = await fetch_albums(sp, artist, retry=False) return albums