Upload 3 files
Browse files- app.py +113 -0
- final_music_data.pkl +3 -0
- scaler.pkl +3 -0
app.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import pandas as pd
|
| 3 |
+
import joblib
|
| 4 |
+
import os
|
| 5 |
+
|
| 6 |
+
# 1. Page Configuration
|
| 7 |
+
st.set_page_config(page_title="BgemBox Music Engine", layout="wide", page_icon="🎵")
|
| 8 |
+
|
| 9 |
+
# Custom CSS to improve font size and table padding
|
| 10 |
+
st.markdown("""
|
| 11 |
+
<style>
|
| 12 |
+
.main .block-container {padding-top: 2rem;}
|
| 13 |
+
th {background-color: #f0f2f6 !important; font-size: 16px !important;}
|
| 14 |
+
td {font-size: 15px !important;}
|
| 15 |
+
</style>
|
| 16 |
+
""", unsafe_allow_html=True)
|
| 17 |
+
|
| 18 |
+
# 2. Load Scaler and Data
|
| 19 |
+
@st.cache_resource
|
| 20 |
+
def load_assets():
|
| 21 |
+
# 1. Veriyi yükle
|
| 22 |
+
data = pd.read_pickle("final_music_data.pkl")
|
| 23 |
+
|
| 24 |
+
# 2. Scaler'ı yükle
|
| 25 |
+
scaler = joblib.load("scaler.pkl")
|
| 26 |
+
|
| 27 |
+
# 3. Kümeleme sütunlarını kontrol et (Return'den ÖNCE olmalı)
|
| 28 |
+
if 'sub_cluster' not in data.columns:
|
| 29 |
+
if 'cluster' in data.columns:
|
| 30 |
+
data['sub_cluster'] = data['cluster']
|
| 31 |
+
else:
|
| 32 |
+
# st.error burada çalışmayabilir, konsola yazdıralım
|
| 33 |
+
print("Critical Error: No clustering columns found!")
|
| 34 |
+
|
| 35 |
+
# İki nesneyi birden döndür
|
| 36 |
+
return data, scaler
|
| 37 |
+
|
| 38 |
+
try:
|
| 39 |
+
# Fonksiyon iki değer döndürdüğü için ikisini de ayrı ayrı almalıyız
|
| 40 |
+
df, music_scaler = load_assets()
|
| 41 |
+
except Exception as e:
|
| 42 |
+
st.error(f"Asset Loading Error: {e}")
|
| 43 |
+
st.stop()
|
| 44 |
+
|
| 45 |
+
# 3. Sidebar Configuration
|
| 46 |
+
st.sidebar.title("Music Engine Settings")
|
| 47 |
+
st.sidebar.markdown("---")
|
| 48 |
+
st.sidebar.write("This AI-powered engine clusters music based on technical audio features like BPM, Energy, and Acousticness.")
|
| 49 |
+
st.sidebar.info("Developed by Elif | 2026")
|
| 50 |
+
|
| 51 |
+
# 4. Cluster Labels
|
| 52 |
+
cluster_names = {
|
| 53 |
+
8.0: "🌟 Mainstream Pop Hits",
|
| 54 |
+
1.0: "🎸 Classic Rock & Dynamic Rhythms",
|
| 55 |
+
2.0: "🎹 Alternative & Indie Vibes",
|
| 56 |
+
7.0: "📜 Nostalgic Oldies",
|
| 57 |
+
3.0: "🌊 Chill & Low-Fi Moods",
|
| 58 |
+
4.0: "⚡ High-Energy / Gym Motivation",
|
| 59 |
+
0.0: "💎 Unique Rare Finds",
|
| 60 |
+
5.0: "💎 Unique Rare Finds",
|
| 61 |
+
6.0: "💎 Unique Rare Finds"
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
# 5. Main UI
|
| 65 |
+
st.title("🎵 BgemBox Music Recommendation System")
|
| 66 |
+
st.subheader("Discover music through Data Science")
|
| 67 |
+
st.markdown("---")
|
| 68 |
+
|
| 69 |
+
tab1, tab2 = st.tabs(["Search by Artist", "Discover by Mood"])
|
| 70 |
+
|
| 71 |
+
# TAB 1: Recommendation by Artist
|
| 72 |
+
with tab1:
|
| 73 |
+
st.write("### Find Similar Artists")
|
| 74 |
+
artist_list = sorted(df['Artist'].unique())
|
| 75 |
+
selected_artist = st.selectbox("Select an Artist you like:", artist_list)
|
| 76 |
+
|
| 77 |
+
if st.button("Recommend Similar Music"):
|
| 78 |
+
artist_data = df[df['Artist'] == selected_artist].iloc[0]
|
| 79 |
+
cluster_id = artist_data['sub_cluster']
|
| 80 |
+
|
| 81 |
+
friendly_name = cluster_names.get(cluster_id, "Similar Style Tracks")
|
| 82 |
+
st.success(f"Artist **{selected_artist}** belongs to the **{friendly_name}** segment.")
|
| 83 |
+
|
| 84 |
+
recommendations = df[(df['sub_cluster'] == cluster_id) & (df['Artist'] != selected_artist)]
|
| 85 |
+
recommendations = recommendations.sort_values(by='Popularity', ascending=False).head(5)
|
| 86 |
+
|
| 87 |
+
st.write("#### Recommended for you:")
|
| 88 |
+
# Used st.table for better readability and tighter columns
|
| 89 |
+
st.table(recommendations[['Artist', 'Top Genre', 'Popularity']])
|
| 90 |
+
|
| 91 |
+
# TAB 2: Discover by Mood
|
| 92 |
+
with tab2:
|
| 93 |
+
st.write("### How are you feeling today?")
|
| 94 |
+
selected_mood_name = st.selectbox("Select a Mood:", list(cluster_names.values()))
|
| 95 |
+
|
| 96 |
+
mood_id = [k for k, v in cluster_names.items() if v == selected_mood_name][0]
|
| 97 |
+
|
| 98 |
+
if st.button("Generate Playlist"):
|
| 99 |
+
mood_list = df[df['sub_cluster'] == mood_id]
|
| 100 |
+
|
| 101 |
+
if not mood_list.empty:
|
| 102 |
+
# Taking a random sample of 10
|
| 103 |
+
playlist = mood_list.sample(min(len(mood_list), 10))
|
| 104 |
+
st.write(f"#### Your {selected_mood_name} Playlist:")
|
| 105 |
+
|
| 106 |
+
# Use st.table here instead of dataframe for better font size and compact columns
|
| 107 |
+
st.table(playlist[['Artist', 'Top Genre', 'Popularity', 'Energy', 'Beats Per Minute (BPM)']])
|
| 108 |
+
else:
|
| 109 |
+
st.warning("This specific cluster is currently empty or has only one track.")
|
| 110 |
+
|
| 111 |
+
# 6. Footer
|
| 112 |
+
st.markdown("---")
|
| 113 |
+
st.caption("© 2026 Data Science Specialization Project")
|
final_music_data.pkl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:46783ba5dca1b249c97f1eaf06162acb1243c4826fc397d07498406c760709ee
|
| 3 |
+
size 227193
|
scaler.pkl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c5a84ecceac33f1e27c541c08fd306700339c6bb4879089864100c73cd965df5
|
| 3 |
+
size 8567
|