forked from Mrinank-Bhowmick/python-beginner-projects
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
set up spotify tools to use for getting tracks recommendation, initia…
…l frontend stuff for testing
- Loading branch information
1 parent
b58e810
commit 29d54ec
Showing
9 changed files
with
343 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Forcing light theme on streamlit app | ||
[theme] | ||
base="light" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
) |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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" | ||
|
@@ -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] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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": [], | ||
}, | ||
} |