MusicMind / app.py
gagan3012's picture
Update app.py
78af5ae verified
import streamlit as st
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.neighbors import NearestNeighbors
import requests
import os
# Function to get Spotify access token
def get_spotify_token(client_id, client_secret):
url = "https://accounts.spotify.com/api/token"
headers = {
"Content-Type": "application/x-www-form-urlencoded"
}
data = {
"grant_type": "client_credentials"
}
response = requests.post(url, headers=headers, data=data, auth=(client_id, client_secret))
if response.status_code == 200:
return response.json().get("access_token")
return None
# Function to fetch Spotify track details
def fetch_spotify_track(query, token):
url = "https://api.spotify.com/v1/search"
headers = {
"Authorization": f"Bearer {token}"
}
params = {
"q": query,
"type": "track",
"limit": 1
}
response = requests.get(url, headers=headers, params=params)
if response.status_code == 200:
data = response.json()
if data["tracks"]["items"]:
track = data["tracks"]["items"][0]
return {
"thumbnail": track["album"]["images"][0]["url"], # Get the largest available thumbnail
"spotify_link": track["external_urls"]["spotify"] # Spotify track link
}
return {"thumbnail": None, "spotify_link": None}
# Page config
st.set_page_config(
page_title="MusicMind - Smart Music Recommendations",
page_icon="🎵",
layout="wide"
)
# Custom CSS
st.markdown("""
<style>
.main {
background-color: #f9f9f9;
}
.stButton button {
background-color: #76818e;
color: white;
border-radius: 20px;
padding: 10px 25px;
border: none;
transition: background-color 0.3s;
}
.stButton button:hover {
background-color: #5348d4;
}
.recommendation-card {
background-color: #76818e;
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
margin: 10px 0;
transition: transform 0.3s;
}
.recommendation-card:hover {
transform: translateY(-5px);
}
.recommendation-card h3 {
font-family: 'Poppins', sans-serif;
color: #e7d7c1;
}
.recommendation-card p {
font-family: 'Roboto', sans-serif;
color: #262730;
}
.recommendation-card a {
text-decoration: none;
color: #e7d7c1;
font-weight: 600;
display: inline-block;
margin-top: 10px;
transition: color 0.3s;
}
.youtube-link {
background-color: #ff4b4b;
color: white !important;
padding: 8px 16px;
border-radius: 20px;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 8px;
font-family: 'Roboto', sans-serif;
font-weight: 500;
transition: all 0.3s ease;
}
.youtube-link:before {
content: "▶";
font-size: 0.8em;
}
.youtube-link:hover {
background-color: #cc0000;
transform: scale(1.05);
box-shadow: 0 4px 12px rgba(255, 0, 0, 0.2);
}
.spotify-link {
background-color: #1db954;
color: white !important;
padding: 8px 16px;
border-radius: 20px;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 8px;
font-family: 'Roboto', sans-serif;
font-weight: 500;
transition: all 0.3s ease;
}
.spotify-link:before {
content: "🎧";
font-size: 0.8em;
}
.spotify-link:hover {
background-color: #cc0000;
transform: scale(1.05);
box-shadow: 0 4px 12px rgba(255, 0, 0, 0.2);
}
</style>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&family=Roboto:wght@400;500&display=swap" rel="stylesheet">
""", unsafe_allow_html=True)
# Load and prepare data
@st.cache_data
def load_data():
df = pd.read_csv("song_dataset.csv")
return df
df = load_data()
@st.cache_resource
def run_imps(df):
required_columns = ['user', 'song', 'play_count', 'title', 'artist_name', 'release']
if not all(col in df.columns for col in required_columns):
raise ValueError(f"Dataset must contain the following columns: {required_columns}")
df = df.drop_duplicates(subset=['song', 'title', 'artist_name', 'release'])
df['combined_features'] = (df['title'] + " " + df['artist_name'] + " " + df['release']).fillna("")
# Content-Based Filtering
tfidf = TfidfVectorizer(max_features=5000, stop_words='english')
tfidf_matrix = tfidf.fit_transform(df['combined_features'])
nn = NearestNeighbors(n_neighbors=10, metric='cosine', algorithm='auto')
nn.fit(tfidf_matrix)
# Collaborative Filtering
user_song_matrix = df.pivot_table(index='user', columns='song', values='play_count', fill_value=0)
knn_cf = NearestNeighbors(n_neighbors=10, metric='cosine', algorithm='auto')
knn_cf.fit(user_song_matrix)
return df, tfidf, tfidf_matrix, nn, user_song_matrix, knn_cf
df = load_data()
df, tfidf, tfidf_matrix, nn, user_song_matrix, knn_cf = run_imps(df)
# Content-based recommendation function
def content_based_recommend(song_title, top_n=5):
try:
idx = df[df['title'] == song_title].index[0]
distances, indices = nn.kneighbors(tfidf_matrix[idx], n_neighbors=top_n + 1)
song_indices = indices.flatten()[1:]
return df.iloc[song_indices][['title', 'artist_name', 'release']].drop_duplicates()
except IndexError:
return pd.DataFrame(columns=['title', 'artist_name', 'release'])
# Collaborative recommendation function using KNN
def collaborative_recommend(user_id, top_n=5):
if user_id not in user_song_matrix.index:
return pd.DataFrame(columns=['title', 'artist_name', 'release'])
# Get the nearest neighbors for the user
user_index = user_song_matrix.index.get_loc(user_id)
distances, indices = knn_cf.kneighbors(user_song_matrix.iloc[user_index].values.reshape(1, -1), n_neighbors=top_n + 1)
# Collect recommendations from neighbors
neighbors = indices.flatten()[1:]
listened_songs = user_song_matrix.loc[user_id][user_song_matrix.loc[user_id] > 0].index
recommendations = {}
for neighbor in neighbors:
neighbor_songs = user_song_matrix.iloc[neighbor]
for song, play_count in neighbor_songs.items():
if song not in listened_songs and play_count > 0:
recommendations[song] = recommendations.get(song, 0) + play_count
# Sort songs by aggregated scores
recommended_songs = sorted(recommendations.items(), key=lambda x: x[1], reverse=True)[:top_n]
recommended_song_ids = [song for song, _ in recommended_songs]
return df[df['song'].isin(recommended_song_ids)][['title', 'artist_name', 'release']].drop_duplicates()
# Hybrid Recommendation
def hybrid_recommendv2(user_id, song_titles, top_n=5):
collab_recs = collaborative_recommend(user_id, top_n)
content_recs = pd.DataFrame()
for song_title in song_titles:
content_recs = pd.concat([content_recs, content_based_recommend(song_title, top_n)], ignore_index=True)
hybrid_recs = pd.concat([collab_recs, content_recs]).drop_duplicates().sample(frac=1).reset_index(drop=True)
return hybrid_recs.head(top_n)
# Sidebar and Main UI
with st.sidebar:
st.header("🎯 Customize Your Recommendations")
user_id = st.selectbox(
"Select User ID",
options=df['user'].unique(),
index=0
)
user_songs = df[df['user'] == user_id]['title'].unique()
song_title = st.multiselect(
"Select Songs You Like",
options=user_songs,
default=user_songs[:1] if len(user_songs) > 0 else None
)
top_n = st.slider("Number of Recommendations", min_value=1, max_value=10, value=5)
get_recs = st.button("Get Recommendations! 🎶")
if get_recs:
st.header("🎵 Your Recommendations")
recommendations = hybrid_recommendv2(user_id, song_title, top_n)
SPOTIFY_CLIENT_ID = os.getenv("SPOTIFY_CLIENT_ID", None)
SPOTIFY_CLIENT_SECRET = os.getenv("SPOTIFY_CLIENT_SECRET", None)
# Get Spotify token
spotify_token = get_spotify_token(SPOTIFY_CLIENT_ID, SPOTIFY_CLIENT_SECRET)
if recommendations.empty:
st.error("No recommendations found. Try selecting different songs or users.")
else:
st.balloons()
for idx, row in recommendations.iterrows():
youtube_link = f"https://www.youtube.com/results?search_query={row['title']}+{row['artist_name']}"
spotify_data = fetch_spotify_track(f"{row['title']} {row['artist_name']}", spotify_token)
spotify_link = spotify_data.get("spotify_link", "#")
thumbnail_url = spotify_data.get("thumbnail")
# st.markdown(f"""
# <div class="recommendation-card">
# <h3>{row['title']}</h3>
# <p><strong>Artist:</strong> {row['artist_name']}</p>
# <p><strong>Album:</strong> {row['release']}</p>
# <a href="{youtube_link}" target="_blank" class="youtube-link">
# Watch on YouTube
# </a>
# </div>
# """, unsafe_allow_html=True)
st.markdown(f"""
<div class="recommendation-card" style="display: flex; align-items: center; gap: 20px;">
{"<img src='" + thumbnail_url + "' alt='Song Thumbnail' style='width:150px; height:150px; border-radius:10px; object-fit: cover;'>" if thumbnail_url else ""}
<div>
<h3>{row['title']}</h3>
<p><strong>Artist:</strong> {row['artist_name']}</p>
<p><strong>Album:</strong> {row['release']}</p>
<a href="{youtube_link}" target="_blank" class="youtube-link">Watch on YouTube</a>
<a href="{spotify_link}" target="_blank" class="spotify-link">Play on Spotify</a>
</div>
</div>
""", unsafe_allow_html=True)