jaibadachiya commited on
Commit
ceb2080
·
verified ·
1 Parent(s): 41f962c

Upload 4 files

Browse files
Files changed (4) hide show
  1. app.py +47 -0
  2. mood_profiles.py +72 -0
  3. recommend.py +107 -0
  4. requirements.txt +6 -0
app.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from recommend import recommend_songs, recommend_by_mood
3
+
4
+ def recommend_from_song(title, year):
5
+ try:
6
+ year = int(year)
7
+ results = recommend_songs([{'name': title, 'year': year}])
8
+ return format_results(results)
9
+ except Exception as e:
10
+ return f"Error: {e}"
11
+
12
+ def recommend_from_mood(mood):
13
+ try:
14
+ results = recommend_by_mood(mood.lower())
15
+ return format_results(results)
16
+ except Exception as e:
17
+ return f"Error: {e}"
18
+
19
+ def format_results(results):
20
+ if isinstance(results, str):
21
+ return results
22
+ if not results:
23
+ return "No recommendations found."
24
+ formatted = ""
25
+ for song in results:
26
+ formatted += f"{song['name']} ({song['year']}) — {song['artists']}\n"
27
+ return formatted
28
+
29
+ song_interface = gr.Interface(
30
+ fn=recommend_from_song,
31
+ inputs=[gr.Textbox(label="Song Name"), gr.Textbox(label="Year")],
32
+ outputs="text",
33
+ title="🎵 Music Recommender (By Song)",
34
+ description="Get song recommendations based on your favorite track."
35
+ )
36
+
37
+ mood_interface = gr.Interface(
38
+ fn=recommend_from_mood,
39
+ inputs=gr.Dropdown(
40
+ choices=["happy", "chill", "party", "sad", "romantic", "focus", "workout", "relax", "aggressive", "uplifting"],
41
+ label="Choose a mood"),
42
+ outputs="text",
43
+ title="🎧 Mood-based Music Recommender",
44
+ description="Get music recommendations that match your mood."
45
+ )
46
+
47
+ gr.TabbedInterface([song_interface, mood_interface], ["By Song", "By Mood"]).launch()
mood_profiles.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ mood_profiles = {
2
+ "happy": {
3
+ "valence": 0.8,
4
+ "energy": 0.7,
5
+ "danceability": 0.7,
6
+ "tempo": 120,
7
+ "acousticness": 0.2
8
+ },
9
+ "chill": {
10
+ "valence": 0.4,
11
+ "energy": 0.3,
12
+ "danceability": 0.4,
13
+ "tempo": 90,
14
+ "acousticness": 0.8
15
+ },
16
+ "party": {
17
+ "valence": 0.7,
18
+ "energy": 0.9,
19
+ "danceability": 0.9,
20
+ "tempo": 125,
21
+ "acousticness": 0.1
22
+ },
23
+ "sad": {
24
+ "valence": 0.2,
25
+ "energy": 0.3,
26
+ "danceability": 0.3,
27
+ "tempo": 80,
28
+ "acousticness": 0.6
29
+ },
30
+ "romantic": {
31
+ "valence": 0.6,
32
+ "energy": 0.4,
33
+ "danceability": 0.5,
34
+ "tempo": 90,
35
+ "acousticness": 0.7
36
+ },
37
+ "focus": {
38
+ "valence": 0.3,
39
+ "energy": 0.2,
40
+ "danceability": 0.3,
41
+ "tempo": 60,
42
+ "acousticness": 0.9
43
+ },
44
+ "workout": {
45
+ "valence": 0.6,
46
+ "energy": 0.95,
47
+ "danceability": 0.85,
48
+ "tempo": 130,
49
+ "acousticness": 0.1
50
+ },
51
+ "relax": {
52
+ "valence": 0.5,
53
+ "energy": 0.3,
54
+ "danceability": 0.4,
55
+ "tempo": 70,
56
+ "acousticness": 0.8
57
+ },
58
+ "aggressive": {
59
+ "valence": 0.2,
60
+ "energy": 0.95,
61
+ "danceability": 0.6,
62
+ "tempo": 140,
63
+ "acousticness": 0.05
64
+ },
65
+ "uplifting": {
66
+ "valence": 0.9,
67
+ "energy": 0.8,
68
+ "danceability": 0.6,
69
+ "tempo": 110,
70
+ "acousticness": 0.3
71
+ }
72
+ }
recommend.py ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import pandas as pd
3
+ from sklearn.pipeline import Pipeline
4
+ from sklearn.preprocessing import StandardScaler
5
+ from sklearn.cluster import KMeans
6
+ from sklearn.metrics import euclidean_distances
7
+ from scipy.spatial.distance import cdist
8
+ from collections import defaultdict
9
+ import spotipy
10
+ from spotipy.oauth2 import SpotifyClientCredentials
11
+
12
+ # Load data
13
+ data = pd.read_csv("data/data.csv")
14
+ number_cols = ['valence', 'year', 'acousticness', 'danceability', 'duration_ms', 'energy', 'explicit',
15
+ 'instrumentalness', 'key', 'liveness', 'loudness', 'mode', 'popularity', 'speechiness', 'tempo']
16
+
17
+ # Spotify credentials (optional: use environment variables for security)
18
+ sp = spotipy.Spotify(auth_manager=SpotifyClientCredentials(
19
+ client_id='e718060be300434d8b02a4451b37ecd7',
20
+ client_secret='4f2506ae10714d9c9f3ba30d2884b476'
21
+ ))
22
+
23
+ # Clustering pipeline for preprocessing
24
+ song_cluster_pipeline = Pipeline([
25
+ ('scaler', StandardScaler()),
26
+ ('kmeans', KMeans(n_clusters=20, random_state=42))
27
+ ])
28
+ song_cluster_pipeline.fit(data[number_cols])
29
+
30
+ def find_song(name, year):
31
+ song_data = defaultdict()
32
+ results = sp.search(q=f'track:{name} year:{year}', limit=1)
33
+ if results['tracks']['items'] == []:
34
+ return None
35
+
36
+ result = results['tracks']['items'][0]
37
+ track_id = result['id']
38
+ audio_features = sp.audio_features(track_id)[0]
39
+
40
+ song_data['name'] = [name]
41
+ song_data['year'] = [year]
42
+ song_data['explicit'] = [int(result['explicit'])]
43
+ song_data['duration_ms'] = [result['duration_ms']]
44
+ song_data['popularity'] = [result['popularity']]
45
+
46
+ for key, value in audio_features.items():
47
+ song_data[key] = value
48
+
49
+ return pd.DataFrame(song_data)
50
+
51
+ def get_song_data(song, spotify_data):
52
+ try:
53
+ song_data = spotify_data[(spotify_data['name'] == song['name']) &
54
+ (spotify_data['year'] == song['year'])].iloc[0]
55
+ return song_data
56
+ except IndexError:
57
+ return find_song(song['name'], song['year'])
58
+
59
+ def get_mean_vector(song_list, spotify_data):
60
+ song_vectors = []
61
+ for song in song_list:
62
+ song_data = get_song_data(song, spotify_data)
63
+ if song_data is None:
64
+ print(f"Warning: {song['name']} not found.")
65
+ continue
66
+ song_vector = song_data[number_cols].values
67
+ song_vectors.append(song_vector)
68
+ song_matrix = np.array(song_vectors)
69
+ return np.mean(song_matrix, axis=0)
70
+
71
+ def flatten_dict_list(dict_list):
72
+ flat_dict = defaultdict(list)
73
+ for entry in dict_list:
74
+ for key, value in entry.items():
75
+ flat_dict[key].append(value)
76
+ return flat_dict
77
+
78
+ def recommend_songs(song_list, spotify_data=data, n_songs=10):
79
+ song_dict = flatten_dict_list(song_list)
80
+ song_center = get_mean_vector(song_list, spotify_data)
81
+
82
+ scaler = song_cluster_pipeline.named_steps['scaler']
83
+ scaled_data = scaler.transform(spotify_data[number_cols])
84
+ scaled_song_center = scaler.transform(song_center.reshape(1, -1))
85
+
86
+ distances = cdist(scaled_song_center, scaled_data, metric='cosine')
87
+ indices = list(np.argsort(distances[0])[:n_songs])
88
+
89
+ recommendations = spotify_data.iloc[indices]
90
+ recommendations = recommendations[~recommendations['name'].isin(song_dict['name'])]
91
+
92
+ return recommendations[['name', 'year', 'artists']].to_dict(orient='records')
93
+
94
+ def recommend_by_mood(mood, spotify_data=data, n_recs=10):
95
+ from mood_profiles import mood_profiles
96
+
97
+ if mood not in mood_profiles:
98
+ print(f"Invalid mood. Choose from: {list(mood_profiles.keys())}")
99
+ return []
100
+
101
+ mood_vector = np.array([mood_profiles[mood][col] for col in ['valence', 'energy', 'danceability', 'tempo', 'acousticness']])
102
+ mood_data = spotify_data[['valence', 'energy', 'danceability', 'tempo', 'acousticness']].copy()
103
+
104
+ distances = cdist([mood_vector], mood_data.values, metric='euclidean')
105
+ indices = np.argsort(distances[0])[:n_recs]
106
+
107
+ return spotify_data.iloc[indices][['name', 'year', 'artists']].to_dict(orient='records')
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ gradio
2
+ spotipy
3
+ scikit-learn
4
+ pandas
5
+ numpy
6
+ scipy