|
|
|
import numpy as np |
|
import pandas as pd |
|
from sklearn.metrics.pairwise import cosine_similarity |
|
import gradio as gr |
|
import gdown |
|
import os |
|
|
|
def download_dataset(): |
|
if not os.path.exists('song_dataset.csv'): |
|
file_id = "1MKqJmWQ1PxKHDkpIdbQEeTz_ohpjOsYPyVp6esEZsq4" |
|
url = f"https://drive.google.com/uc?id={file_id}" |
|
|
|
gdown.download(url, 'temp_dataset.xlsx', quiet=False) |
|
|
|
temp_df = pd.read_excel('temp_dataset.xlsx') |
|
temp_df.to_csv('song_dataset.csv', index=False) |
|
|
|
os.remove('temp_dataset.xlsx') |
|
|
|
|
|
download_dataset() |
|
|
|
|
|
df = pd.read_csv('song_dataset.csv') |
|
|
|
|
|
interaction_matrix = df.pivot_table( |
|
index='user', |
|
columns='song', |
|
values='play_count', |
|
fill_value=0 |
|
) |
|
|
|
def search_songs(query): |
|
if not query or len(query) < 2: |
|
return gr.update(choices=[]) |
|
try: |
|
matches = song_choices[ |
|
song_choices['display'].str.lower().str.contains(query.lower(), regex=False) |
|
]['display'].tolist() |
|
return gr.update(choices=matches[:10]) |
|
except Exception as e: |
|
print(f"Search error: {e}") |
|
return gr.update(choices=[]) |
|
|
|
def add_to_selection(new_song, current_selections): |
|
if not current_selections: |
|
current_selections = [] |
|
|
|
if new_song and new_song not in current_selections and len(current_selections) < 5: |
|
current_selections.append(new_song) |
|
|
|
return gr.update(choices=current_selections, value=current_selections) |
|
|
|
def make_recommendations(selected_songs, n_recommendations=5): |
|
if not selected_songs: |
|
return "Please select at least one song to get recommendations." |
|
|
|
temp_user_profile = pd.Series(0, index=interaction_matrix.columns) |
|
for song in selected_songs: |
|
song_id = song_choices[song_choices['display'] == song]['song'].iloc[0] |
|
temp_user_profile[song_id] = 1 |
|
|
|
user_sim = cosine_similarity([temp_user_profile], interaction_matrix)[0] |
|
|
|
unheard_songs = list(set(interaction_matrix.columns) - |
|
set([song_choices[song_choices['display'] == song]['song'].iloc[0] |
|
for song in selected_songs])) |
|
|
|
pred_ratings = [] |
|
for song in unheard_songs: |
|
pred = np.sum(user_sim * interaction_matrix[song]) / np.sum(np.abs(user_sim)) |
|
pred_ratings.append((song, pred)) |
|
|
|
ratings = np.array([r[1] for r in pred_ratings]) |
|
if len(ratings) > 0: |
|
min_rating, max_rating = ratings.min(), ratings.max() |
|
if max_rating > min_rating: |
|
normalized_ratings = 1 + 4 * (ratings - min_rating) / (max_rating - min_rating) |
|
else: |
|
normalized_ratings = [3.0] * len(ratings) |
|
else: |
|
return "No recommendations found for the selected songs." |
|
|
|
recommendations = list(zip([r[0] for r in pred_ratings], normalized_ratings)) |
|
recommendations = sorted(recommendations, key=lambda x: x[1], reverse=True)[:n_recommendations] |
|
|
|
output = "" |
|
for song_id, rating in recommendations: |
|
song_details = df[df['song'] == song_id].iloc[0] |
|
output += f"Title: {song_details['title']}\n" |
|
output += f"Artist: {song_details['artist_name']}\n" |
|
output += f"Year: {song_details['year']}\n" |
|
output += f"Rating: {rating:.2f}/5.00\n" |
|
output += "-" * 50 + "\n" |
|
|
|
return output |
|
|
|
|
|
song_choices = df[['song', 'title', 'artist_name']].drop_duplicates() |
|
song_choices['display'] = song_choices['title'] + " - " + song_choices['artist_name'] |
|
song_list = song_choices['display'].tolist() |
|
|
|
|
|
with gr.Blocks(title="Wanna Be Spotify") as iface: |
|
gr.Markdown("# 🎵 Wanna Be Spotify") |
|
gr.Markdown("Search and select up to 5 songs you've enjoyed to get personalized recommendations!") |
|
|
|
with gr.Row(): |
|
search_box = gr.Textbox( |
|
label="Search for songs", |
|
placeholder="Type song or artist name...", |
|
show_label=True |
|
) |
|
|
|
with gr.Row(): |
|
search_results = gr.Radio( |
|
choices=[], |
|
label="Search Results", |
|
interactive=True, |
|
show_label=True |
|
) |
|
|
|
with gr.Row(): |
|
selected_songs = gr.Dropdown( |
|
choices=[], |
|
label="Selected Songs", |
|
interactive=True, |
|
multiselect=True, |
|
max_choices=5, |
|
show_label=True |
|
) |
|
|
|
with gr.Row(): |
|
recommendations = gr.Textbox( |
|
label="Recommendations", |
|
interactive=False, |
|
lines=10, |
|
show_label=True |
|
) |
|
|
|
submit_btn = gr.Button("Get Recommendations") |
|
|
|
search_box.change( |
|
fn=search_songs, |
|
inputs=[search_box], |
|
outputs=[search_results] |
|
) |
|
|
|
search_results.change( |
|
fn=add_to_selection, |
|
inputs=[search_results, selected_songs], |
|
outputs=[selected_songs] |
|
) |
|
|
|
submit_btn.click( |
|
fn=make_recommendations, |
|
inputs=[selected_songs], |
|
outputs=[recommendations] |
|
) |
|
|
|
|
|
iface.launch() |