Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
213 changes: 64 additions & 149 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,74 +117,34 @@ def get_all_saved_tracks(self):
saved.extend(results['items'])
return saved


def create_playlist(self, playlist_name, num_clusters=5, num_iterations=10):
self.clear_log_file('spotai.log')
print("Creating Playlist......")
start_time = time.time()
top_artists = self.sp.current_user_top_artists(limit=5, time_range='medium_term')['items']

#getting all tracks form user's library (thinking of not using recent_songs not sure)
liked_songs = self.get_all_saved_tracks()
recent_songs = self.get_recent_tracks()
top_songs = self.get_all_top_tracks()


#appending all of these tracks to check if the user already knows them
known_names = {item['track']['name'] for item in liked_songs}
known_names.update({item['track']['name'] for item in recent_songs})
known_names.update({item['name'] for item in top_songs})

known_tracks = {item['track']['id'] for item in liked_songs}
known_tracks.update({item['track']['id'] for item in recent_songs})
known_tracks.update({item['id'] for item in top_songs})

track_ids = [track['id'] for track in top_songs[:200]]
#getting audio features for the tracks
try:
features = self.get_audio_features_for_tracks(track_ids)
except spotipy.exceptions.SpotifyException as e:
return jsonify({'error': str(e)}), 500

# Filter out None values from features
features = [feature for feature in features if feature is not None]

if not features:
return jsonify({'error': "No audio features retrieved for the tracks"}), 500

def recommend_tracks(self, track_ids, num_clusters=5, num_iterations=10, features=None):
features_df = pd.DataFrame(features)
# X = features_df[['danceability', 'energy', 'key', 'loudness', 'mode', 'speechiness', 'acousticness', 'instrumentalness', 'liveness', 'valence', 'tempo']]

plays = Counter(track_ids)
#normalizing the play count

max_plays = max(plays.values())
features_df['play count'] = features_df['id'].apply(lambda x: plays[x] / max_plays)
#selecting the features to be used for clustering

feature_columns = ['danceability', 'energy', 'key', 'loudness', 'mode', 'speechiness', 'acousticness', 'instrumentalness', 'liveness', 'valence', 'tempo', 'play count']
X = features_df[feature_columns]
print("Scaler...")
#scaling the features

scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
print("PCA...")
#applying PCA to reduce the dimensions
pca = PCA(n_components=5)
X_pca = pca.fit_transform(X_scaled)
print("KMeans...")
#applying KMeans clustering

# pca = PCA(n_components=5)
# X_pca = pca.fit_transform(X_scaled)

kmeans = KMeans(n_clusters=num_clusters, random_state=42)
kmeans.fit(X_pca)
kmeans.fit(X_scaled)
features_df['kmeans_cluster'] = kmeans.labels_
centroids_kmeans = kmeans.cluster_centers_
print("GMM...")
#applying GMM clustering

gmm = GaussianMixture(n_components=num_clusters, random_state=42)
gmm.fit(X_pca)
features_df['cluster'] = gmm.predict(X_pca)
gmm.fit(X_scaled)
features_df['cluster'] = gmm.predict(X_scaled)
centroids = gmm.means_


#combining the centroids of KMeans and GMM
kmeans_weights = np.bincount(features_df['kmeans_cluster'])
gmm_weights = np.bincount(features_df['cluster'])

Expand All @@ -193,24 +153,40 @@ def create_playlist(self, playlist_name, num_clusters=5, num_iterations=10):

combined_centroids = (weighted_kmeans_centroids + weighted_gmm_centroids) / 2

# OLD IMPLEMENTATION (TESTING)
# kmeans = KMeans(n_clusters=num_clusters, random_state=42)
# kmeans.fit(X)
# features_df['cluster'] = kmeans.labels_
# centroids = kmeans.cluster_centers_
print("Recommendations...")
#getting the recommendations

recommendations = []
for i in range(num_clusters):
centroid = combined_centroids[i]
cluster_tracks = features_df[features_df['cluster'] == i]
cluster_tracks_pca = X_pca[features_df['cluster'] == i]
cluster_indices = cluster_tracks.index
cluster_tracks_scaled = X_scaled[cluster_indices]
centroid_array = np.array(centroid)

closest_index = np.argmin(np.sum((cluster_tracks_pca - centroid_array) ** 2, axis=1))
closest_id = track_ids[cluster_tracks.index[closest_index]]
recommendations.append(closest_id)
distance = np.linalg.norm(cluster_tracks_scaled - centroid_array, axis=1)
closest_index = np.argmin(distance)

closest_id = cluster_indices[closest_index]
print(closest_id)
close = track_ids[closest_id]
recommendations.append(close)
return recommendations

def get_known_tracks(self, liked_songs, recent_songs, top_songs):
known_names = {item['track']['name'] for item in liked_songs}
known_names.update({item['track']['name'] for item in recent_songs[:100]})
known_names.update({item['name'] for item in top_songs})

known_tracks = {item['track']['id'] for item in liked_songs}
known_tracks.update({item['track']['id'] for item in recent_songs[:100]})
known_tracks.update({item['id'] for item in top_songs})
return known_tracks, known_names

def playlist_create(self, recommendations, liked_songs, top_songs, recent_songs, start_time, playlist_name, num_iterations=10):
known_tracks, known_names = self.get_known_tracks(liked_songs, recent_songs, top_songs)
user_id = self.sp.me()['id']
playlist_description = f"SpotAI Recommendations. Updated on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}."
playlists = self.sp.user_playlists(user_id)['items']
Expand All @@ -229,7 +205,7 @@ def create_playlist(self, playlist_name, num_clusters=5, num_iterations=10):
for x in range(num_iterations):
random_tracks = random.sample(recommendations, min(5, len(recommendations)))
try:
recommended_tracks = self.sp.recommendations(seed_tracks=random_tracks, limit=100)
recommended_tracks = self.sp.recommendations(seed_tracks=random_tracks[:5], limit=100)
except spotipy.exceptions.SpotifyException as e:
return jsonify({'error': str(e)}), 500

Expand All @@ -238,11 +214,36 @@ def create_playlist(self, playlist_name, num_clusters=5, num_iterations=10):

most_common_recommendations = [track_id for track_id, count in rec_counter.most_common(100)]
self.sp.user_playlist_add_tracks(user=user_id, playlist_id=playlist_id, tracks=most_common_recommendations)

end_time = time.time() # Record the end time
duration = end_time - start_time # Calculate the duration
print(f"Playlist generation took {duration:.2f} seconds") # Print the duration
return playlist_id

def create_playlist(self, playlist_name, num_clusters=5, num_iterations=10):
self.clear_log_file('spotai.log')
print("Creating Playlist......")
start_time = time.time()

#getting all tracks form user's library (thinking of not using recent_songs not sure)
liked_songs = self.get_all_saved_tracks()
recent_songs = self.get_recent_tracks()
top_songs = self.get_all_top_tracks()

track_ids = [track['id'] for track in top_songs[:200]]
#getting audio features for the tracks
try:
features = self.get_audio_features_for_tracks(track_ids)
except spotipy.exceptions.SpotifyException as e:
return jsonify({'error': str(e)}), 500

# Filter out None values from features
features = [feature for feature in features if feature is not None]

if not features:
return jsonify({'error': "No audio features retrieved for the tracks"}), 500

recommendations = self.recommend_tracks(track_ids, num_clusters, num_iterations, features)
playlist_id = self.playlist_create(recommendations, liked_songs, top_songs, recent_songs, start_time, playlist_name)
return render_template('playlist.html', playlist_name=playlist_name, playlist_url=f"https://open.spotify.com/playlist/{playlist_id}")

def create_playlist_from_playlist(self, selected_playlist_id, playlist_name, num_clusters=5, num_iterations=10):
Expand Down Expand Up @@ -274,14 +275,6 @@ def create_playlist_from_playlist(self, selected_playlist_id, playlist_name, num
recent_songs = self.get_recent_tracks()
top_songs = self.get_all_top_tracks()

known_names = {item['track']['name'] for item in liked_songs}
known_names.update({item['track']['name'] for item in recent_songs[:100]})
known_names.update({item['name'] for item in top_songs})

known_tracks = {item['track']['id'] for item in liked_songs}
known_tracks.update({item['track']['id'] for item in recent_songs[:100]})
known_tracks.update({item['id'] for item in top_songs})

# track_ids = [track['track']['id'] for track in tracks]

try:
Expand All @@ -292,86 +285,8 @@ def create_playlist_from_playlist(self, selected_playlist_id, playlist_name, num
if not features:
return jsonify({'error': "No audio features retrieved for the tracks"}), 500

features_df = pd.DataFrame(features)
# X = features_df[['danceability', 'energy', 'key', 'loudness', 'mode', 'speechiness', 'acousticness', 'instrumentalness', 'liveness', 'valence', 'tempo']]

plays = Counter(track_ids)

max_plays = max(plays.values())
features_df['play count'] = features_df['id'].apply(lambda x: plays[x] / max_plays)

feature_columns = ['danceability', 'energy', 'key', 'loudness', 'mode', 'speechiness', 'acousticness', 'instrumentalness', 'liveness', 'valence', 'tempo', 'play count']
X = features_df[feature_columns]

scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

pca = PCA(n_components=5)
X_pca = pca.fit_transform(X_scaled)

kmeans = KMeans(n_clusters=num_clusters, random_state=42)
kmeans.fit(X_pca)
features_df['kmeans_cluster'] = kmeans.labels_
centroids_kmeans = kmeans.cluster_centers_

gmm = GaussianMixture(n_components=num_clusters, random_state=42)
gmm.fit(X_pca)
features_df['cluster'] = gmm.predict(X_pca)
centroids = gmm.means_

kmeans_weights = np.bincount(features_df['kmeans_cluster'])
gmm_weights = np.bincount(features_df['cluster'])

weighted_kmeans_centroids = np.dot(kmeans_weights, centroids_kmeans) / np.sum(kmeans_weights)
weighted_gmm_centroids = np.dot(gmm_weights, centroids) / np.sum(gmm_weights)

combined_centroids = (weighted_kmeans_centroids + weighted_gmm_centroids) / 2

# kmeans = KMeans(n_clusters=num_clusters, random_state=42)
# kmeans.fit(X)
# features_df['cluster'] = kmeans.labels_
# centroids = kmeans.cluster_centers_

recommendations = []
for i in range(num_clusters):
centroid = combined_centroids[i]
cluster_tracks = features_df[features_df['cluster'] == i]
cluster_tracks_pca = X_pca[features_df['cluster'] == i]
centroid_array = np.array(centroid)

closest_index = np.argmin(np.sum((cluster_tracks_pca - centroid_array) ** 2, axis=1))
closest_id = track_ids[cluster_tracks.index[closest_index]]
recommendations.append(closest_id)
user_id = self.sp.me()['id']
playlist_description = f"SpotAI Recommendations. Based on {self.sp.playlist(selected_playlist_id)['name']}. Updated on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}."
playlists = self.sp.user_playlists(user_id)['items']
playlist_id = None
for playlist in playlists:
if playlist['name'] == playlist_name:
playlist_id = playlist['id']
self.sp.playlist_replace_items(playlist_id, [])
self.sp.playlist_change_details(playlist_id, description=playlist_description)
break
if not playlist_id:
new_playlist = self.sp.user_playlist_create(user=user_id, name=playlist_name, public=True, description=playlist_description)
playlist_id = new_playlist['id']

rec_counter = Counter()
for x in range(num_iterations):
random_tracks = random.sample(recommendations, min(5, len(recommendations)))
try:
recommended_tracks = self.sp.recommendations(seed_tracks=random_tracks[:5], limit=100)
except spotipy.exceptions.SpotifyException as e:
return jsonify({'error': str(e)}), 500

new_recommendations = [track['id'] for track in recommended_tracks['tracks'] if track['id'] not in known_tracks and track['name'] not in known_names]
rec_counter.update(new_recommendations)

most_common_recommendations = [track_id for track_id, count in rec_counter.most_common(100)]
self.sp.user_playlist_add_tracks(user=user_id, playlist_id=playlist_id, tracks=most_common_recommendations)
end_time = time.time() # Record the end time
duration = end_time - start_time # Calculate the duration
print(f"Playlist generation took {duration:.2f} seconds") # Print the duration
recommendations = self.recommend_tracks(track_ids, num_clusters, num_iterations, features)
playlist_id = self.playlist_create(recommendations, liked_songs, top_songs, recent_songs, start_time, playlist_name)
return render_template('playlist.html', playlist_name=playlist_name, playlist_url=f"https://open.spotify.com/playlist/{playlist_id}")

spot_ai = SpotAI(client_id=CLIENT_ID, client_secret=CLIENT_SECRET, redirect_uri="http://localhost:5000/callback")
Expand Down
1 change: 1 addition & 0 deletions tempfolder/SpotAI
Submodule SpotAI added at be63df