Skip to content

Commit

Permalink
set up spotify tools to use for getting tracks recommendation, initia…
Browse files Browse the repository at this point in the history
…l frontend stuff for testing
  • Loading branch information
polskiTran committed Apr 4, 2024
1 parent b58e810 commit 29d54ec
Show file tree
Hide file tree
Showing 9 changed files with 343 additions and 4 deletions.
6 changes: 5 additions & 1 deletion projects/Mood2SpotifyRec/.env.example
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY"
GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY"

SPOTIPY_CLIENT_ID="YOUR_SPOTIFY_CLIENT_ID"
SPOTIPY_CLIENT_SECRET="YOUR_SPOTIFY_CLIENT_SECRET"
SPOTIPY_REDIRECT_URI="YOUR_SPOTIFY_REDIRECT_URI"
3 changes: 3 additions & 0 deletions projects/Mood2SpotifyRec/.streamlit/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Forcing light theme on streamlit app
[theme]
base="light"
23 changes: 22 additions & 1 deletion projects/Mood2SpotifyRec/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,25 @@ Have poetry installed. Run

```terminal
poetry install
```
```

## Get spotify client id and secret

follow this tutorial: https://support.heateor.com/get-spotify-client-id-client-secret/

> Note that for the redirect URI, enter "http://localhost:8888/callback"
## Running the webapp

in command line, export your client id and secret

```terminal
export SPOTIPY_CLIENT_ID="your client id goes here"
export SPOTIPY_CLIENT_SECRET="your client secret goes here"
```

then run

```terminal
streamlit run main.py
```
29 changes: 29 additions & 0 deletions projects/Mood2SpotifyRec/SeedData.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
class SeedData:
def __init__(
self,
seed_genres=None,
seed_artists=None,
target_valence=None,
target_energy=None,
target_danceability=None,
target_acousticness=None,
):
self.seed_genres = seed_genres if seed_genres is not None else []
self.seed_artists = seed_artists if seed_artists is not None else []
self.target_valence = target_valence
self.target_energy = target_energy
self.target_danceability = target_danceability
self.target_acousticness = target_acousticness

def get_test_seed_data(self):
return SeedData(
seed_genres=["indie", "rock"],
seed_artists=[
"4NHQUGzhtTLFvgF5SZesLK"
], # Example artist ID for Tame Impala
# seed_tracks=["0c6xIDDpzE81m2q797ordA"], # Example track ID
target_valence=0.5,
target_energy=0.8,
target_danceability=0.7,
target_acousticness=0.3,
)
89 changes: 88 additions & 1 deletion projects/Mood2SpotifyRec/main.py
Original file line number Diff line number Diff line change
@@ -1 +1,88 @@
# streamlit file goes here
import streamlit as st
import streamlit.components.v1 as components
import spotify
import SeedData

st.title("Mood 2 Spotify Recommendation")

# Inputs for Client ID and Client Secret
client_id = st.text_input("Client ID", "")
client_secret = st.text_input("Client Secret", "")
sp = spotify.SpotifyTools(client_id, client_secret)
access_token = sp.get_access_token()


# Section for Audio Features
# st.subheader("Get Audio Features")
# track_id = st.text_input("Spotify Track ID", "")
# if st.button("Get Audio Features"):
# if not client_id or not client_secret or not track_id:
# st.warning("Please fill in all fields for audio features")
# else:
# if access_token:
# analysis = sp.get_audio_features(track_id)
# st.json(analysis)
# else:
# st.error("Failed to retrieve access token for audio features")

# Section for Artist Search
# st.subheader("Search for an Artist")
# artist_name = st.text_input("Artist Name", "")
# if st.button("Search Artist"):
# if not client_id or not client_secret or not artist_name:
# st.warning("Please fill in all fields.")
# else:
# artist_info = sp.search_artist_details(artist_name)
# if artist_info and artist_info["image_url"]:
# st.image(artist_info["image_url"], caption=f"Artist: {artist_info['name']}")
# else:
# st.error("Artist not found or has no images.")

# Test for available genres
# st.subheader("Available Genres: ")
# if st.button("Get Recommendations"):
# if not client_id or not client_secret:
# st.warning("Please fill in all fields.")
# else:
# if access_token:
# recommended_genre_seeds = sp.get_genre_recommendations("indie")
# st.write(f"{recommended_genre_seeds}")
# else:
# st.error("Failed to retrieve access token for recommendations")

# Section for track recommendations
st.subheader("Get TEST Track Recommendations")
test_seed_data = SeedData.SeedData().get_test_seed_data()
st.write(test_seed_data)
if st.button("Get TEST Tracks Recommendations"):
if not client_id or not client_secret:
st.warning("Please fill in all fields.")
else:
if access_token:
recommended_tracks = sp.get_test_track_recommendation()
track_uris = [track["uri"] for track in recommended_tracks["tracks"]]
# Display the tracks as clickable links
for track in recommended_tracks["tracks"]:
track_name = track["name"]
artists = ", ".join(
artist["name"] for artist in track["album"]["artists"]
)
spotify_url = track["external_urls"]["spotify"]
st.write(f"[{track_name} by {artists}]({spotify_url})")

else:
st.error("Failed to retrieve access token for recommendations")

# Show embed TEST recommendation playlist
if st.button("Show playlist"):
user_id = sp.user_id
playlist_id = sp.add_recommend_tracks_to_playlist(
"Mood2Spotify Playlist", test_seed_data
)
# Your playlist embed URL
playlist_embed_url = "https://open.spotify.com/embed/playlist/" + playlist_id
# Embed the playlist using an iframe
components.html(
f"""<iframe src="{playlist_embed_url}" width=100% height=700 frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>""",
height=700,
)
16 changes: 15 additions & 1 deletion projects/Mood2SpotifyRec/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions projects/Mood2SpotifyRec/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ description = "Quick app to use the user's mood input as generation data to a Sp
authors = ["menamerai <[email protected]>", "polskiXO <[email protected]>"]
license = "MIT"
readme = "README.md"
package-mode = false


[tool.poetry.dependencies]
python = "^3.10"
Expand All @@ -13,6 +15,7 @@ google-generativeai = "^0.4.1"
requests = "^2.31.0"
pytest = "^8.1.1"
spotipy = "^2.23.0"
python-dotenv = "^1.0.1"


[build-system]
Expand Down
142 changes: 142 additions & 0 deletions projects/Mood2SpotifyRec/spotify.py
Original file line number Diff line number Diff line change
@@ -1 +1,143 @@
# spotify stuff goes here: aka spotify calls, playlist creation, music matching, etc.

import SeedData
import requests
import spotipy
from spotipy.oauth2 import SpotifyOAuth


class SpotifyTools:
def __init__(self, client_id, client_secret):
if client_id is None or client_secret is None:
raise ValueError(
"Follow README tutorial to create a Spotify Developer account and get the client_id and client_secret."
)
self.client_id = client_id
self.client_secret = client_secret
self.access_token = self.get_access_token()
self.sp = spotipy.Spotify(auth=self.access_token)
self.scope = "playlist-read-private playlist-read-collaborative playlist-modify-private playlist-modify-public"
self.sp_oauth = SpotifyOAuth(
client_id=self.client_id,
client_secret=self.client_secret,
redirect_uri="http://localhost:8888/callback",
scope=self.scope,
)
self.sp_authenticated = spotipy.Spotify(auth_manager=self.sp_oauth)
self.user_id = self.set_user_id()

def set_user_id(self):
"""Set the user id for the authenticated user
Returns:
str: user id
"""
sp_user_info = self.sp_authenticated.current_user()
sp_user_id = sp_user_info["id"]
return sp_user_id

def get_access_token(self):
"""Get access token for spotify API call
Returns:
text (str): spotify access token
"""
auth_url = "https://accounts.spotify.com/api/token"
auth_response = requests.post(
auth_url,
{
"grant_type": "client_credentials",
"client_id": self.client_id,
"client_secret": self.client_secret,
},
)
auth_response_data = auth_response.json()
return auth_response_data.get("access_token")

# def get_audio_features(self, track_id):
# base_url = "https://api.spotify.com/v1/audio-features/"
# headers = {
# "Authorization": f"Bearer {self.access_token}",
# }
# response = requests.get(f"{base_url}{track_id}", headers=headers)
# data = response.json()
# # Filter the response to include only the desired attributes
# filtered_data = {
# k: v
# for k, v in data.items()
# if k in ("valence", "energy", "instrumentalness", "loudness", "tempo")
# }
# return filtered_data

# def search_artist_details(self, artist_name):
# # Search for the artist
# result = self.sp.search(q=artist_name, type="artist")
# items = result["artists"]["items"]
# if not items:
# return None

# # Assuming we take the first artist and their first image
# artist_info = {
# "id": items[0]["id"],
# "name": items[0]["name"],
# "image_url": items[0]["images"][0]["url"] if items[0]["images"] else None,
# }

# return artist_info

def get_available_genre(self):
"""Get available genres for recommendations
Returns:
_type_: _description_
"""
sp = spotipy.Spotify(auth=self.access_token)
recommended_genre_seeds = sp.recommendation_genre_seeds()
return recommended_genre_seeds

def get_tracks_recommendations(self, seed_data):
"""Tracks recommendations using Spotify API based on seed data
Args:
seed_data (dict): eed data provided by the LLM model
Returns:
dict: recommended tracks
"""
recommended_tracks = self.sp.recommendations(
limit=10,
seed_genres=seed_data.seed_genres,
seed_artists=seed_data.seed_artists,
target_valence=seed_data.target_valence,
target_energy=seed_data.target_energy,
target_danceability=seed_data.target_danceability,
target_acousticness=seed_data.target_acousticness,
)
return recommended_tracks

# TODO: remove when done
def get_test_track_recommendation(self):
testSeedData = SeedData.SeedData().get_test_seed_data()
return self.get_tracks_recommendations(testSeedData)
# print(recommended_track)
# print(type(recommended_track))

def add_recommend_tracks_to_playlist(self, playlist_name, seed_data):
"""create a playlist in user's spotify account and add recommended tracks to it
Args:
playlist_name (str): name the playlist
seed_data (dict): seed data provided by the LLM model
Returns:
str: generated playlist id
"""
recommended_tracks = self.get_tracks_recommendations(seed_data)
track_ids = [track["id"] for track in recommended_tracks["tracks"]]
playlist = self.sp_authenticated.user_playlist_create(
user=self.user_id, name=playlist_name, public=False
)
playlist_id = playlist["id"]
self.sp_authenticated.user_playlist_add_tracks(
user=self.user_id, playlist_id=playlist_id, tracks=track_ids
)
return playlist_id
36 changes: 36 additions & 0 deletions projects/Mood2SpotifyRec/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"collaborative": False,
"description": "",
"external_urls": {
"spotify": "https://open.spotify.com/playlist/6g1NnUu2y0fvCyRqLQb3Q6"
},
"followers": {"href": None, "total": 0},
"href": "https://api.spotify.com/v1/playlists/6g1NnUu2y0fvCyRqLQb3Q6",
"id": "6g1NnUu2y0fvCyRqLQb3Q6",
"images": [],
"primary_color": None,
"name": "Mood2Spotify Playlist",
"type": "playlist",
"uri": "spotify:playlist:6g1NnUu2y0fvCyRqLQb3Q6",
"owner": {
"href": "https://api.spotify.com/v1/users/8c2c8c0854d34baa9db2f794d33799ba",
"id": "8c2c8c0854d34baa9db2f794d33799ba",
"type": "user",
"uri": "spotify:user:8c2c8c0854d34baa9db2f794d33799ba",
"display_name": None,
"external_urls": {
"spotify": "https://open.spotify.com/user/8c2c8c0854d34baa9db2f794d33799ba"
},
},
"public": False,
"snapshot_id": "NDIyLGZhYTFmMWY4NzdjNmRmMWM1YzBiNWQ3YzMyNDdkNjU2NTA0OTJkYjM=",
"tracks": {
"limit": 100,
"next": None,
"offset": 0,
"previous": None,
"href": "https://api.spotify.com/v1/playlists/6g1NnUu2y0fvCyRqLQb3Q6/tracks",
"total": 0,
"items": [],
},
}

0 comments on commit 29d54ec

Please sign in to comment.