arcade-mcp/toolkits/spotify/arcade_spotify/tools/tracks.py
Eric Gustin ab889f9f1d
Lint all toolkits (#183)
# PR Description
* Adds/updates the following files to all toolkits:
    - `.pre-commit-config.yaml`
    - `.ruff.toml`
    - `LICENSE`
    - `Makefile`
    - `pyproject.toml`
* Lint all toolkits such that they pass `make check` and `make test` (a
total doozy). This includes adding some unit tests and evals.
* Github workflow for testing toolkits before merge into main (courtesy
of @sdreyer)
* Added a QOL improvement for tool developers for when they need to get
the context's auth token.
* Minor updates to `arcade new` template.
2024-12-20 09:49:45 -08:00

138 lines
4.9 KiB
Python

from typing import Annotated, Optional
from arcade.sdk import ToolContext, tool
from arcade.sdk.auth import Spotify
from arcade_spotify.tools.utils import (
get_url,
send_spotify_request,
)
@tool(requires_auth=Spotify())
async def get_track_from_id(
context: ToolContext,
track_id: Annotated[str, "The Spotify ID of the track"],
) -> Annotated[dict, "Information about the track"]:
"""Get information about a track"""
url = get_url("tracks_get_track", track_id=track_id)
response = await send_spotify_request(context, "GET", url)
response.raise_for_status()
return dict(response.json())
@tool(requires_auth=Spotify())
async def get_recommendations(
context: ToolContext,
seed_artists: Annotated[
list[str], "A list of Spotify artist IDs to seed the recommendations with"
],
seed_genres: Annotated[
list[str], "A list of Spotify genre IDs to seed the recommendations with"
],
seed_tracks: Annotated[
list[str], "A list of Spotify track IDs to seed the recommendations with"
],
limit: Annotated[int, "The maximum number of recommended tracks to return"] = 5,
target_acousticness: Annotated[
Optional[float],
"The target acousticness of the recommended tracks (between 0 and 1)",
] = None,
target_danceability: Annotated[
Optional[float],
"The target danceability of the recommended tracks (between 0 and 1)",
] = None,
target_duration_ms: Annotated[
Optional[int],
"The target duration of the recommended tracks in milliseconds",
] = None,
target_energy: Annotated[
Optional[float],
"The target energy of the recommended tracks (between 0 and 1)",
] = None,
target_instrumentalness: Annotated[
Optional[float],
"The target instrumentalness of the recommended tracks (between 0 and 1)",
] = None,
target_key: Annotated[
Optional[int],
"The target key of the recommended tracks (0-11)",
] = None,
target_liveness: Annotated[
Optional[float],
"The target liveness of the recommended tracks (between 0 and 1)",
] = None,
target_loudness: Annotated[
Optional[float],
"The target loudness of the recommended tracks (in decibels)",
] = None,
target_mode: Annotated[
Optional[int],
"The target mode of the recommended tracks (0 or 1)",
] = None,
target_popularity: Annotated[
Optional[int],
"The target popularity of the recommended tracks (0-100)",
] = None,
target_speechiness: Annotated[
Optional[float],
"The target speechiness of the recommended tracks (between 0 and 1)",
] = None,
target_tempo: Annotated[
Optional[float],
"The target tempo of the recommended tracks (in beats per minute)",
] = None,
target_time_signature: Annotated[
Optional[int],
"The target time signature of the recommended tracks",
] = None,
target_valence: Annotated[
Optional[float],
"The target valence of the recommended tracks (between 0 and 1)",
] = None,
) -> Annotated[dict, "A list of recommended tracks"]:
"""Get track (song) recommendations based on seed artists, genres, and tracks
If a provided target value is outside of the expected range,
it will clamp to the nearest valid value.
"""
url = get_url("tracks_get_recommendations")
params = {
"seed_artists": seed_artists,
"seed_genres": seed_genres,
"seed_tracks": seed_tracks,
"limit": limit,
"target_acousticness": target_acousticness,
"target_danceability": target_danceability,
"target_duration_ms": target_duration_ms,
"target_energy": target_energy,
"target_instrumentalness": target_instrumentalness,
"target_key": target_key,
"target_liveness": target_liveness,
"target_loudness": target_loudness,
"target_mode": target_mode,
"target_popularity": target_popularity,
"target_speechiness": target_speechiness,
"target_tempo": target_tempo,
"target_time_signature": target_time_signature,
"target_valence": target_valence,
}
params = {k: v for k, v in params.items() if v is not None}
response = await send_spotify_request(context, "GET", url, params=params)
response.raise_for_status()
return dict(response.json())
@tool(requires_auth=Spotify())
async def get_tracks_audio_features(
context: ToolContext,
track_ids: Annotated[list[str], "A list of Spotify track (song) IDs"],
) -> Annotated[dict, "A list of audio features for the tracks"]:
"""Get audio features for a list of tracks (songs)"""
url = get_url("tracks_get_audio_features")
params = {"ids": ",".join(track_ids)}
response = await send_spotify_request(context, "GET", url, params=params)
response.raise_for_status()
return dict(response.json())