Cedddy commited on
Commit
b016061
Β·
verified Β·
1 Parent(s): b0824e2

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +340 -0
app.py ADDED
@@ -0,0 +1,340 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # NLP + Emotion model
2
+ from transformers import AutoTokenizer, AutoModelForSequenceClassification,AutoModelForSeq2SeqLM, MarianTokenizer, MarianMTModel
3
+ import lyricsgenius
4
+ import torch
5
+ import numpy as np
6
+ import joblib
7
+ import gradio as gr
8
+ import matplotlib.pyplot as plt
9
+ import warnings
10
+ from langdetect import detect
11
+ import os
12
+
13
+
14
+ warnings.filterwarnings("ignore")
15
+
16
+
17
+
18
+ # πŸ”‘ Replace these with your real tokens
19
+ GENIUS_API_TOKEN = os.getenv("GENIUS_API_TOKEN")
20
+ HF_TOKEN = os.getenv("HF_TOKEN")
21
+
22
+ # Initialize Genius API
23
+ genius = lyricsgenius.Genius(GENIUS_API_TOKEN)
24
+
25
+ # Load emotion model from Hugging Face
26
+ model_name = "bhadresh-savani/bert-base-uncased-emotion"
27
+ tokenizer = AutoTokenizer.from_pretrained(model_name, use_auth_token=HF_TOKEN)
28
+ model = AutoModelForSequenceClassification.from_pretrained(model_name, use_auth_token=HF_TOKEN)
29
+ model.eval()
30
+
31
+
32
+ # Load Meta's NLLB model
33
+ nllb_tokenizer = AutoTokenizer.from_pretrained("facebook/nllb-200-1.3B")
34
+ nllb_model = AutoModelForSeq2SeqLM.from_pretrained("facebook/nllb-200-1.3B")
35
+
36
+
37
+ # Load ML-based model
38
+ #emotion_model = joblib.load("emotion_model.pkl")
39
+ #song_encoder = joblib.load("song_encoder.pkl")
40
+ #emotion_decoder = joblib.load("emotion_decoder.pkl")
41
+
42
+ # Genius API
43
+ genius = lyricsgenius.Genius(GENIUS_API_TOKEN)
44
+
45
+ def translate_to_english(text):
46
+ try:
47
+ language = detect(text)
48
+ if language == 'en':
49
+ print("βœ… Detected English β€” no translation needed.")
50
+ return text
51
+ elif language == 'tl' or language == 'fil':
52
+ print("πŸ” Detected Tagalog β€” translating to English...")
53
+ else:
54
+ print(f"🌐 Detected '{language}' β€” attempting translation anyway.")
55
+ except Exception as e:
56
+ print(f"⚠️ Language detection failed: {e}. Proceeding with translation.")
57
+
58
+ # Translate using NLLB
59
+ inputs = nllb_tokenizer(
60
+ text, return_tensors="pt", truncation=True, padding=True, max_length=512
61
+ )
62
+ inputs["forced_bos_token_id"] = nllb_tokenizer.lang_code_to_id["eng_Latn"]
63
+ translated_tokens = nllb_model.generate(**inputs, max_length=512)
64
+ return nllb_tokenizer.decode(translated_tokens[0], skip_special_tokens=True)
65
+
66
+
67
+ def get_translated_lyrics(title, artist):
68
+ try:
69
+ song = genius.search_song(title, artist)
70
+ if song and song.lyrics:
71
+ print("πŸ“„ Original Lyrics Snippet:\n", song.lyrics[:300], "\n")
72
+ return translate_to_english(song.lyrics)
73
+ else:
74
+ print("⚠️ No lyrics found for:", title)
75
+ except Exception as e:
76
+ print("Genius error:", e)
77
+ return None
78
+
79
+
80
+ def get_emotion_distribution(text):
81
+ inputs = tokenizer(text, return_tensors="pt", truncation=True, padding=True)
82
+ with torch.no_grad():
83
+ outputs = model(**inputs)
84
+ probs = torch.nn.functional.softmax(outputs.logits, dim=-1)[0]
85
+ labels = model.config.id2label
86
+ return {labels[i]: float(probs[i]) for i in range(len(probs))}
87
+
88
+
89
+ def average_emotions(song_emotions):
90
+ emotion_keys = list(song_emotions[0].keys())
91
+ vectors = [[song.get(k, 0) for k in emotion_keys] for song in song_emotions]
92
+ avg_vector = np.mean(vectors, axis=0)
93
+ return dict(zip(emotion_keys, avg_vector))
94
+
95
+
96
+ def display_emotion_pie_chart(emotion_scores, top_n=6, min_threshold=0.01):
97
+ sorted_emotions = sorted(emotion_scores.items(), key=lambda x: x[1], reverse=True)
98
+ filtered = [(e, s) for e, s in sorted_emotions if s > min_threshold][:top_n]
99
+ if not filtered:
100
+ return None
101
+ labels = [e.title() for e, _ in filtered]
102
+ sizes = [s for _, s in filtered]
103
+ explode = [0.05 if i == 0 else 0 for i in range(len(sizes))]
104
+ colors = plt.cm.Pastel1.colors[:len(labels)]
105
+ fig, ax = plt.subplots(figsize=(6, 6))
106
+ ax.pie(sizes, labels=labels, autopct=lambda p: f"{p:.1f}%" if p > 3 else "",
107
+ startangle=140, explode=explode, colors=colors,
108
+ textprops=dict(color="black", fontsize=12),
109
+ wedgeprops=dict(width=0.5, edgecolor='white'))
110
+ ax.set_title("🎭 Emotion Composition", fontsize=14, fontweight='bold')
111
+ plt.tight_layout()
112
+ return fig
113
+
114
+
115
+ def analyze_lyrics(song_title, artist_name):
116
+ if not song_title or not song_title.strip():
117
+ return "Please enter a song title.", None
118
+
119
+ translated = get_translated_lyrics(song_title, artist_name)
120
+ if not translated:
121
+ return "❌ Lyrics not found or translation failed.", None
122
+
123
+ emotions = get_emotion_distribution(translated)
124
+ summary = f"🧠 Dominant Emotion: {max(emotions, key=emotions.get).upper()}"
125
+ pie = display_emotion_pie_chart(emotions)
126
+ return summary, pie
127
+
128
+
129
+ def analyze_multiple_songs(song1, artist1, song2, artist2, song3, artist3):
130
+ all_emotions = []
131
+
132
+ for title, artist in [(song1, artist1), (song2, artist2), (song3, artist3)]:
133
+ if not title or not title.strip():
134
+ print(f"⚠️ Skipping empty input: {title}")
135
+ continue
136
+ lyrics = get_translated_lyrics(title, artist)
137
+ if lyrics:
138
+ emotions = get_emotion_distribution(lyrics)
139
+ all_emotions.append(emotions)
140
+
141
+ if not all_emotions:
142
+ return "❌ No valid songs found.", None
143
+
144
+ avg_emotions = average_emotions(all_emotions)
145
+ dominant_emotion = max(avg_emotions, key=avg_emotions.get).lower()
146
+
147
+ # 🧠 Store mapped mood for recommendation
148
+ general_mood = goemotion_to_general.get(dominant_emotion, "calm")
149
+ detected_mood["mood"] = general_mood
150
+
151
+ summary = f"🧠 Dominant Emotion: {dominant_emotion.upper()} β†’ Mood: {general_mood.upper()}"
152
+ pie = display_emotion_pie_chart(avg_emotions)
153
+ return summary, pie
154
+
155
+
156
+ import pandas as pd
157
+ import matplotlib.pyplot as plt
158
+ from collections import Counter
159
+
160
+ # Load pseudo-labeled songs
161
+ labeled_df = pd.read_csv("labeled_songs.csv")
162
+ label_map = {0: "sad", 1: "happy", 2: "energetic", 3: "calm"}
163
+ inverse_map = {v: k for k, v in label_map.items()}
164
+ labeled_df["predicted_emotion_name"] = labeled_df["predicted_emotion"].map(label_map)
165
+
166
+ # Mood-shift rules
167
+ mood_shift_map = {
168
+ "sad": "happy",
169
+ "happy": "calm",
170
+ "calm": "energetic",
171
+ "energetic": "calm"
172
+ }
173
+
174
+ # Track last detected mood
175
+ detected_mood = {"mood": None}
176
+
177
+ # Mapping from GoEmotion to general moods
178
+ goemotion_to_general = {
179
+ "admiration": "happy", "amusement": "happy", "anger": "sad", "annoyance": "energetic",
180
+ "approval": "happy", "caring": "calm", "confusion": "sad", "curiosity": "energetic",
181
+ "desire": "energetic", "disappointment": "sad", "disapproval": "energetic", "disgust": "energetic",
182
+ "embarrassment": "sad", "excitement": "energetic", "fear": "sad", "gratitude": "happy",
183
+ "grief": "sad", "joy": "happy", "love": "happy", "nervousness": "energetic", "optimism": "happy",
184
+ "pride": "happy", "realization": "calm", "relief": "calm", "remorse": "sad", "sadness": "sad",
185
+ "surprise": "energetic", "neutral": "calm"
186
+ }
187
+
188
+ # Analyze 3 songs, get dominant emotion, and store mapped general mood
189
+ def analyze_and_store_lyrics(song1, artist1, song2, artist2, song3, artist3):
190
+ summary, fig = analyze_multiple_songs(song1, artist1, song2, artist2, song3, artist3)
191
+
192
+ # Extract dominant GoEmotion from summary
193
+ if "Playlist Mood:" in summary:
194
+ raw_goemotion = summary.split(":")[1].strip().lower()
195
+ detected_mood["mood"] = goemotion_to_general.get(raw_goemotion, "calm")
196
+ summary += f"\nπŸ—‚οΈ Mapped Mood: {detected_mood['mood'].upper()}"
197
+
198
+ return summary, fig
199
+
200
+ # Recommend songs with the same mood as detected
201
+ def recommend_similar_mood_songs():
202
+ mood = detected_mood["mood"]
203
+ if not mood:
204
+ return pd.DataFrame([{"⚠️": "Analyze songs first"}])
205
+
206
+ filtered = labeled_df[labeled_df["predicted_emotion_name"] == mood]
207
+ if filtered.empty:
208
+ return pd.DataFrame([{"⚠️": f"No songs found for mood '{mood}'"}])
209
+
210
+ return filtered.sample(n=min(5, len(filtered)))[["name", "artists", "predicted_emotion_name"]]
211
+
212
+ # Recommend songs with a shifted mood
213
+ def recommend_mood_shift_songs():
214
+ mood = detected_mood["mood"]
215
+ if not mood:
216
+ return pd.DataFrame([{"⚠️": "Analyze songs first"}])
217
+
218
+ target = mood_shift_map.get(mood)
219
+ if not target:
220
+ return pd.DataFrame([{"⚠️": f"No shift rule for '{mood}'"}])
221
+
222
+ filtered = labeled_df[labeled_df["predicted_emotion_name"] == target]
223
+ if filtered.empty:
224
+ return pd.DataFrame([{"⚠️": f"No songs found for mood shift to '{target}'"}])
225
+
226
+ return filtered.sample(n=min(5, len(filtered)))[["name", "artists", "predicted_emotion_name"]]
227
+
228
+ import gradio as gr
229
+ css = """
230
+ @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;500&display=swap');
231
+
232
+ body, h1, h2, h3, h4, h5, h6, p, label, button, input, textarea {
233
+ font-family: 'Poppins', sans-serif !important;
234
+ color: #222 !important;
235
+ font-size: 16px !important;
236
+ }
237
+
238
+ body {
239
+ background: linear-gradient(to right, #e0f7fa, #f3e5f5);
240
+ margin: 0;
241
+ padding: 0;
242
+ }
243
+
244
+ .gradio-container, .gradio-interface, .gradio-box {
245
+ background-color: rgba(255, 255, 255, 0.9) !important;
246
+ border-radius: 16px !important;
247
+ padding: 24px !important;
248
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
249
+ }
250
+
251
+ .gr-textbox, .gr-input, input, textarea {
252
+ background-color: #fff !important;
253
+ color: #222 !important;
254
+ border: 1px solid #ccc !important;
255
+ border-radius: 12px !important;
256
+ padding: 12px !important;
257
+ font-size: 16px !important;
258
+ }
259
+
260
+ .gr-textbox:focus, .gr-input:focus {
261
+ border-color: #a78bfa !important;
262
+ }
263
+
264
+ .track-btn, .gr-button {
265
+ background: linear-gradient(135deg, #cfd9df, #e2ebf0) !important;
266
+ border: none !important;
267
+ color: #333 !important;
268
+ font-weight: 600 !important;
269
+ border-radius: 10px !important;
270
+ padding: 12px 24px !important;
271
+ font-size: 16px !important;
272
+ transition: background 0.4s ease, transform 0.2s ease !important;
273
+ margin-top: 16px !important;
274
+ }
275
+
276
+ .track-btn:hover, .gr-button:hover {
277
+ background: linear-gradient(135deg, #e2ebf0, #cfd9df) !important;
278
+ transform: scale(1.02);
279
+ }
280
+
281
+ label {
282
+ font-weight: 500 !important;
283
+ font-size: 14px !important;
284
+ color: #444 !important;
285
+ }
286
+ """
287
+
288
+
289
+ # Define your Gradio interface
290
+ with gr.Blocks(css=css, title="Lyric Mood Tracker") as app:
291
+ gr.Markdown(
292
+ "<h1 style='text-align: center; font-weight: 500; color: #4A4A4A;'>🌿 Lyric Mood Tracker</h1>"
293
+ "<p style='text-align: center; color: #666; font-size: 16px;'>Understand the emotional landscape of your favorite songs</p>"
294
+ )
295
+
296
+ with gr.Row():
297
+ with gr.Column():
298
+ song1 = gr.Textbox(label="🎡 Song 1 Title")
299
+ artist1 = gr.Textbox(label="🎀 Artist 1")
300
+ with gr.Column():
301
+ song2 = gr.Textbox(label="🎡 Song 2 Title")
302
+ artist2 = gr.Textbox(label="🎀 Artist 2")
303
+ with gr.Column():
304
+ song3 = gr.Textbox(label="🎡 Song 3 Title")
305
+ artist3 = gr.Textbox(label="🎀 Artist 3")
306
+
307
+ run_btn = gr.Button("πŸ” Analyze Mood", elem_classes=["track-btn"])
308
+ output_text = gr.Textbox(label="🧠 Mood Summary")
309
+ output_plot = gr.Plot(label="🎭 Emotion Pie Chart")
310
+
311
+ with gr.Row():
312
+ rec_similar_btn = gr.Button("🎯 Recommend Similar Mood Songs", elem_classes=["track-btn"])
313
+ rec_shift_btn = gr.Button("πŸ”„ Recommend Mood-Shift Songs", elem_classes=["track-btn"])
314
+
315
+ similar_output = gr.Dataframe(label="🎧 Similar Mood Recommendations")
316
+ shift_output = gr.Dataframe(label="πŸͺ„ Mood Shift Recommendations")
317
+
318
+ # All functions now use all 3 songs as inputs
319
+ run_btn.click(
320
+ analyze_multiple_songs,
321
+ inputs=[song1, artist1, song2, artist2, song3, artist3],
322
+ outputs=[output_text, output_plot]
323
+ )
324
+
325
+ run_btn.click(
326
+ analyze_and_store_lyrics,
327
+ inputs=[song1, artist1, song2, artist2, song3, artist3],
328
+ outputs=[output_text, output_plot]
329
+ )
330
+
331
+ run_btn.click(
332
+ analyze_lyrics,
333
+ inputs=[song1, artist1, song2, artist2, song3, artist3],
334
+ outputs=[output_text, output_plot]
335
+ )
336
+
337
+ rec_similar_btn.click(recommend_similar_mood_songs, outputs=similar_output)
338
+ rec_shift_btn.click(recommend_mood_shift_songs, outputs=shift_output)
339
+
340
+ app.launch(share=True)