huathedev commited on
Commit
2c02a4d
1 Parent(s): 3be857d

Upload 3 files

Browse files

Add usage of Spotify API

01_Recommend_from_Song🎤.py ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import spotipy
2
+ import streamlit as st
3
+ import numpy as np
4
+ from spotipy.oauth2 import SpotifyClientCredentials
5
+ from PIL import Image
6
+ import requests
7
+ import pandas as pd
8
+
9
+ st.set_page_config(
10
+ page_title="Find Songs Similar to Yours🎤", page_icon="🎤", layout="wide"
11
+ )
12
+
13
+ # Spotify API
14
+ SPOTIPY_CLIENT_ID = st.secrets.spot_creds.spot_client_id
15
+ SPOTIPY_CLIENT_SECRET = st.secrets.spot_creds.spot_client_secret
16
+
17
+ sp = spotipy.Spotify(
18
+ auth_manager=SpotifyClientCredentials(
19
+ client_id=SPOTIPY_CLIENT_ID, client_secret=SPOTIPY_CLIENT_SECRET
20
+ )
21
+ )
22
+
23
+ """
24
+ # Analyze Song and Get Recommendations🎤
25
+
26
+ Input a song title and the app will return recommendations as well as the features of the song.
27
+
28
+ Data is obtained using the Python library [Spotipy](https://spotipy.readthedocs.io/en/2.18.0/) that uses [Spotify Web API.](https://developer.spotify.com/documentation/web-api/)
29
+
30
+ """
31
+ song = st.text_input("Enter a song title", value="Somebody Else")
32
+ search = sp.search(q="track:" + song, type="track")
33
+
34
+
35
+ class GetSongInfo:
36
+ def __init__(self, search):
37
+ self.search = search
38
+
39
+ def song_id(self):
40
+ song_id = search["tracks"]["items"][0]["id"] # -gets song id
41
+ return song_id
42
+
43
+ def song_album(self):
44
+ song_album = search["tracks"]["items"][0]["album"][
45
+ "name"
46
+ ] # -gets song album name
47
+ return song_album
48
+
49
+ def song_image(self):
50
+ song_image = search["tracks"]["items"][0]["album"]["images"][0][
51
+ "url"
52
+ ] # -gets song image URL
53
+ return song_image
54
+
55
+ def song_artist_name(self):
56
+ song_artist_name = search["tracks"]["items"][0]["artists"][0][
57
+ "name"
58
+ ] # -gets artist for song
59
+ return song_artist_name
60
+
61
+ def song_name(self):
62
+ song_name = search["tracks"]["items"][0]["name"] # -gets song name
63
+ return song_name
64
+
65
+ def song_preview(self):
66
+ song_preview = search["tracks"]["items"][0]["preview_url"]
67
+ return song_preview
68
+
69
+
70
+ songs = GetSongInfo(song)
71
+
72
+ ###
73
+
74
+
75
+ def url(song):
76
+ url_to_song = "https://open.spotify.com/track/" + songs.song_id()
77
+ st.write(
78
+ f"Link to stream '{songs.song_name()}' by {songs.song_artist_name()} on Spotify: {url_to_song}"
79
+ )
80
+
81
+
82
+ # Set up two-column layout for Streamlit app
83
+ image, stats = st.columns(2)
84
+
85
+ with image:
86
+ try:
87
+ url(song)
88
+ r = requests.get(songs.song_image())
89
+ open("img/" + songs.song_id() + ".jpg", "w+b").write(r.content)
90
+ image_album = Image.open("img/" + songs.song_id() + ".jpg")
91
+ st.image(
92
+ image_album,
93
+ caption=f"{songs.song_artist_name()} - {songs.song_album()}",
94
+ use_column_width="auto",
95
+ )
96
+
97
+ feat = sp.audio_features(tracks=[songs.song_id()])
98
+ features = feat[0]
99
+ p = pd.Series(features).to_frame()
100
+ data_feat = p.loc[
101
+ [
102
+ "acousticness",
103
+ "danceability",
104
+ "energy",
105
+ "liveness",
106
+ "speechiness",
107
+ "valence",
108
+ ]
109
+ ]
110
+ bpm = p.loc[["tempo"]]
111
+ values = bpm.values[0]
112
+ bpms = values.item()
113
+ ticks = np.linspace(0, 1, 11)
114
+
115
+ plot = data_feat.plot.barh(
116
+ xticks=ticks, legend=False, color="limegreen"
117
+ ) # Use Pandas plot
118
+ plot.set_xlabel("Value")
119
+ plot.set_ylabel("Parameters")
120
+ plot.set_title(f"Analysing '{songs.song_name()}' by {songs.song_artist_name()}")
121
+ plot.invert_yaxis()
122
+ st.pyplot(plot.figure)
123
+ st.subheader(f"BPM (Beats Per Minute): {bpms}")
124
+
125
+ st.warning(
126
+ "Note: Audio previews may have very high default volume and will reset after page refresh"
127
+ )
128
+ st.audio(songs.song_preview(), format="audio/wav")
129
+
130
+ except IndexError or NameError:
131
+ st.error(
132
+ "This error is possibly due to the API being unable to find the song. Maybe try to retype it using the song title followed by artist without any hyphens (e.g. In my Blood Shawn Mendes)"
133
+ )
134
+
135
+ # Recommendations
136
+ with stats:
137
+ st.subheader("You might also like")
138
+
139
+ reco = sp.recommendations(
140
+ seed_artists=None, seed_tracks=[songs.song_id()], seed_genres=[], limit=10
141
+ )
142
+
143
+ for i in reco["tracks"]:
144
+ st.write(f"\"{i['name']}\" - {i['artists'][0]['name']}")
145
+ image_reco = requests.get(i["album"]["images"][2]["url"])
146
+ open("img/" + i["id"] + ".jpg", "w+b").write(image_reco.content)
147
+ st.image(Image.open("img/" + i["id"] + ".jpg"))
02_Recommend_from_Genre_Features🎸.py ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+
3
+ # Configure Streamlit page
4
+ st.set_page_config(
5
+ page_title="Find the Song that You Like🎸", page_icon="🎸", layout="wide"
6
+ )
7
+
8
+ import pandas as pd
9
+ import plotly.express as px
10
+ import streamlit.components.v1 as components
11
+ from sklearn.neighbors import NearestNeighbors
12
+
13
+
14
+ @st.cache(allow_output_mutation=True)
15
+ def data_import():
16
+ """Function for loading in cleaned data csv file."""
17
+ df = pd.read_csv("data/clean_data.csv")
18
+ df["genres"] = df.genres.apply(
19
+ lambda x: [i[1:-1] for i in str(x)[1:-1].split(", ")]
20
+ )
21
+ df_explode = df.explode("genres")
22
+ return df_explode
23
+
24
+
25
+ genre_names = [
26
+ "Dance Pop",
27
+ "Electronic",
28
+ "Electropop",
29
+ "Hip Hop",
30
+ "Jazz",
31
+ "K-pop",
32
+ "Latin",
33
+ "Pop",
34
+ "Pop Rap",
35
+ "R&B",
36
+ "Rock",
37
+ ]
38
+ audio_params = [
39
+ "acousticness",
40
+ "danceability",
41
+ "energy",
42
+ "instrumentalness",
43
+ "valence",
44
+ "tempo",
45
+ ]
46
+
47
+ df_explode = data_import()
48
+
49
+
50
+ def match_song(genre, yr_start, yr_end, test_feat):
51
+ """Function for finding similar songs with KNN algorithm."""
52
+ genre = genre.lower()
53
+ genre_data = df_explode[
54
+ (df_explode["genres"] == genre)
55
+ & (df_explode["release_year"] >= yr_start)
56
+ & (df_explode["release_year"] <= yr_end)
57
+ ]
58
+ genre_data = genre_data.sort_values(by="popularity", ascending=False)[:500]
59
+
60
+ # Load KNN from SkLearn
61
+ neigh = NearestNeighbors()
62
+ neigh.fit(genre_data[audio_params].to_numpy())
63
+
64
+ n_neighbors = neigh.kneighbors(
65
+ [test_feat], n_neighbors=len(genre_data), return_distance=False
66
+ )[0]
67
+
68
+ uris = genre_data.iloc[n_neighbors]["uri"].tolist()
69
+ audios = genre_data.iloc[n_neighbors][audio_params].to_numpy()
70
+
71
+ return uris, audios
72
+
73
+
74
+ # Setup page order
75
+ def page():
76
+ title = "Find Your Song🎸"
77
+ st.title(title)
78
+
79
+ st.write(
80
+ "Get recommended songs on Spotify based on genre and key audio parameters."
81
+ )
82
+ st.markdown("##")
83
+
84
+ # Streamlit column layout
85
+ with st.container():
86
+ col1, col2, col3, col4 = st.columns((2, 0.5, 0.5, 0.5))
87
+
88
+ with col3:
89
+ st.markdown("***Select genre:***")
90
+ genre = st.radio("", genre_names, index=genre_names.index("Rock"))
91
+
92
+ with col1:
93
+ st.markdown("***Select audio parameters to customize:***")
94
+ yr_start, yr_end = st.slider(
95
+ "Select the year range", 1908, 2022, (1980, 2022)
96
+ )
97
+ acousticness = st.slider("Acousticness", 0.0, 1.0, 0.5)
98
+ danceability = st.slider("Danceability", 0.0, 1.0, 0.5)
99
+ energy = st.slider("Energy", 0.0, 1.0, 0.5)
100
+ instrumentalness = st.slider("Instrumentalness", 0.0, 1.0, 0.5)
101
+ valence = st.slider("Valence", 0.0, 1.0, 0.45)
102
+ tempo = st.slider("Tempo", 0.0, 244.0, 125.01)
103
+
104
+ pr_page_tracks = 6
105
+ test_feat = [acousticness, danceability, energy, instrumentalness, valence, tempo]
106
+ uris, audios = match_song(genre, yr_start, yr_end, test_feat)
107
+
108
+ tracks = []
109
+ for uri in uris:
110
+ track = """<iframe src="https://open.spotify.com/embed/track/{}" width="280" height="400" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>""".format(
111
+ uri
112
+ )
113
+ tracks.append(track)
114
+
115
+ if "previous_inputs" not in st.session_state:
116
+ st.session_state["previous_inputs"] = [genre, yr_start, yr_end] + test_feat
117
+
118
+ current_inputs = [genre, yr_start, yr_end] + test_feat
119
+ if current_inputs != st.session_state["previous_inputs"]:
120
+ if "start_track_i" in st.session_state:
121
+ st.session_state["start_track_i"] = 0
122
+
123
+ st.session_state["previous_inputs"] = current_inputs
124
+
125
+ if "start_track_i" not in st.session_state:
126
+ st.session_state["start_track_i"] = 0
127
+
128
+ with st.container():
129
+ col1, col2, col3 = st.columns([2, 1, 2])
130
+ if st.button("More Songs"):
131
+ if st.session_state["start_track_i"] < len(tracks):
132
+ st.session_state["start_track_i"] += pr_page_tracks
133
+
134
+ current_tracks = tracks[
135
+ st.session_state["start_track_i"] : st.session_state["start_track_i"]
136
+ + pr_page_tracks
137
+ ]
138
+ current_audios = audios[
139
+ st.session_state["start_track_i"] : st.session_state["start_track_i"]
140
+ + pr_page_tracks
141
+ ]
142
+ if st.session_state["start_track_i"] < len(tracks):
143
+ for i, (track, audio) in enumerate(zip(current_tracks, current_audios)):
144
+ if i % 2 == 0:
145
+ with col1:
146
+ components.html(
147
+ track,
148
+ height=400,
149
+ )
150
+ with st.expander("Display Chart"):
151
+ df = pd.DataFrame(dict(r=audio[:5], theta=audio_params[:5]))
152
+ fig = px.line_polar(
153
+ df, r="r", theta="theta", line_close=True
154
+ )
155
+ fig.update_layout(height=400, width=340)
156
+ st.plotly_chart(fig)
157
+
158
+ else:
159
+ with col3:
160
+ components.html(
161
+ track,
162
+ height=400,
163
+ )
164
+ with st.expander("Display Chart"):
165
+ df = pd.DataFrame(dict(r=audio[:5], theta=audio_params[:5]))
166
+ fig = px.line_polar(
167
+ df, r="r", theta="theta", line_close=True
168
+ )
169
+ fig.update_layout(height=400, width=340)
170
+ st.plotly_chart(fig)
171
+ else:
172
+ st.write("No more songs")
173
+
174
+
175
+ page()
Introduction🎶.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from streamlit import session_state as session
3
+
4
+ # Configure Streamlit page
5
+ st.set_page_config(page_title="Song Recommender🎶", page_icon="🎶")
6
+
7
+ st.title("Song Recommender🎶")
8
+ st.markdown("Click on '**Recommend from Song🎤**' from the side panel to get recommended songs via the Spotify API.")
9
+ st.markdown("**How does '**Recommend from Genre Features🎸**' work?**")
10
+ st.markdown(
11
+ "The songs come from the [Spotify and Genius Track Dataset](https://www.kaggle.com/datasets/saurabhshahane/spotgen-music-dataset) on Kaggle. The [k-Nearest Neighbor algorithm](https://scikit-learn.org/stable/modules/neighbors.html) is used to obtain recommendations, i.e., the top songs which are closest in distance to the set of parameter inputs specified by you."
12
+ )
13
+
14
+ st.markdown("This app will recommend you songs based on the characteristics below.")
15
+ st.markdown(
16
+ """
17
+ **Acousticness**: A metric describing the 'acousticness' of a song. 1.0 represents high confidence the song is acoustic.<br>
18
+
19
+ **Danceability**: Describes a song's suitability for dancing based on combination of elements including tempo, rhythm stability, beat strength, and overall regularity.
20
+ 0.0 is least danceable and 1.0 is most danceable.<br>
21
+
22
+ **Energy**: Measure of intensity and activity. Often, energetic songs feel fast, loud, and noisy.<br>
23
+
24
+ **Liveness**: A metric describing the likelihood that a track is a recording of a live performance.<br>
25
+
26
+ **Speechiness**: How much lyrics the track contains.<br>
27
+
28
+ **Valence**: A metric ranging from 0.0 to 1.0 describing the positivity conveyed by a track.<br>
29
+
30
+ Source: [Spotify Web API](https://developer.spotify.com/documentation/web-api/reference)
31
+ """,
32
+ unsafe_allow_html=True,
33
+ )