File size: 5,580 Bytes
a2d0e0e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# Streamlit application

# Import necessary libraries
import streamlit as st
import pandas as pd
from openai import OpenAI
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity

# Import data
music_data = pd.read_csv("Spotify_Youtube.csv")

# Specify api key for OpenAIs API
client = OpenAI(api_key="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")

# Function that calls OpenAIs API
def parse_user_input(user_input):
    response = client.chat.completions.create(
      model="gpt-3.5-turbo",
      messages=[
        {
          "role": "system",
          "content": """You will be provided with an input: '{user_input}', and your task is to determine the following:

          - Valence: a number that is equal to the mood. Positive moods are closer to 1 and negative moods are closer to 0.

          - Number of songs: the number of songs the user requests.

          - Tempo: the tempo of the songs.

          - Danceability: the danceability of the songs.



          Provide this information in the following format with each value separated by a space:

          'valence number_of_songs tempo danceability'

          Example: '0.5 20 120 0.8'

          """
        },
        {
          "role": "user",
          "content": user_input
        },
        {
          "role": "assistant",
          "content": "0.5 20, 120, 0.8"
        }
      ],
      temperature=0.5,
      max_tokens=64,
      top_p=1
    )
    return response.choices[0].message.content


# Function create new dataframe from music dataframe based on valence, number of tracks, tempo an danceability
def get_tracks_by_artist_and_danceability(music_data, valence, num_tracks, tempo, danceability):
    filtered_tracks = music_data[
        (music_data['Valence'].between(valence - 0.1, valence + 0.1)) &
        (music_data['Tempo'].between(tempo - 30, tempo + 30)) &
        (music_data['Danceability'].between(danceability - 0.2, danceability + 0.2))
    ]
    return filtered_tracks.head(num_tracks)[['Track', 'Artist']]

# Function the recommends tracks by using tfidVectoricer and cosine_similarities
def recommend_tracks(track_names, track_ids, top_k=20):
    vectorizer = TfidfVectorizer()
    tfidf_matrix = vectorizer.fit_transform(track_names)
    similarities = cosine_similarity(tfidf_matrix)
    avg_similarity = similarities.mean(axis=0)
    top_indices = avg_similarity.argsort()[-top_k:][::-1]
    return track_ids.iloc[top_indices]

# Streamlit Application

logo = "music_logo.png"

# Sidebar
with st.sidebar:
  st.image(logo, width=100)
  st.header("Navigation")
tab_selection = st.sidebar.radio("Go to", ["Music Generator", "Browse Music", "About Us"])

# Music generator page
if tab_selection == "Music Generator":
  st.header("Mood Playlist Generator", divider='rainbow')
  st.write("Enter your music preferences in a detailed format and recieve a personalized playlist based on your mood:")
  user_prompt = st.text_input("Example: 'I want 20 happy songs with a lot of tempo that i can dance to!'")

  if st.button("Generate Playlist"):
      try:
        with st.spinner("Processing your request..."):
          parsed_input = parse_user_input(user_prompt)
          #st.write(f"Parsed input: {parsed_input}")

          # Extract valence and number of songs from the parsed input
          valence, num_tracks, tempo, danceability = parsed_input.split()
          valence = float(valence)
          num_tracks = int(num_tracks)
          tempo = int(tempo)
          danceability = (float(danceability))

          #st.write(f"Number of tracks: {num_tracks}, Valence: {valence}, Tempo: {tempo}, Danceability: {danceability}")

          tracks = get_tracks_by_artist_and_danceability(music_data, valence, num_tracks, tempo, danceability)
          #st.write(f"Found {len(tracks)} tracks.")

          if tracks.empty:
            st.write("No tracks found. Please try a different query.")
          else:
            track_names = tracks['Track'].tolist()
            track_ids = tracks[['Track', 'Artist']]
            #st.write("Track names:", track_names)

            recommended_tracks = recommend_tracks(track_names, track_ids, top_k=int(num_tracks))
            st.write("Here are your recommended playlist:")
            st.table(recommended_tracks)
            st.button("Add playlist to Spotify")
      except ValueError:
        st.write("Error: Unable to parse the input. Please make sure the format is correct.")

# Browse music page
elif tab_selection == "Browse Music":
  st.header("Browse Music", divider='rainbow')
  st.write("Explore the music data used for generating your playlists.")
  df = pd.read_csv("Spotify_Youtube.csv")
  st.dataframe(df)

# About us page
elif tab_selection == "About Us":
  st.header("About Us", divider='rainbow')
  st.write("""

    This App is developed by 4 ambitious university students, whose goals are to create playlists and music experiences which you can emotionally connect with.

    """)
  st.image("group_photo.jpg", caption="Our Team", use_column_width=True)

# Add custom CSS
st.markdown(
    """

    <style>

    .css-18e3th9 {

        padding-top: 2rem;

    }

    .css-1d391kg {

        padding-top: 1.5rem;

    }

    .css-1d2jrlv {

        padding-bottom: 1rem;

    }

    .css-1v3fvcr {

        padding-top: 1rem;

        padding-bottom: 1rem;

    }

    </style>

    """,
    unsafe_allow_html=True
)