MarfinF commited on
Commit
cbf1942
Β·
1 Parent(s): 05853df

Initial push Musik Rekomender project

Browse files
Files changed (4) hide show
  1. DockerFile +9 -0
  2. backend/app.py +163 -0
  3. frontend/index.html +268 -0
  4. requirements.txt +8 -0
DockerFile ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY . .
6
+
7
+ RUN pip install -r requirements.txt
8
+
9
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
backend/app.py ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ from fastapi import FastAPI, WebSocket
3
+ from fastapi.staticfiles import StaticFiles
4
+ from fastapi.responses import FileResponse
5
+ import uvicorn
6
+ import json
7
+ from transformers import pipeline
8
+ from collections import deque
9
+ from collections import defaultdict
10
+ import math
11
+ import sys
12
+
13
+ sys.path.append(".")
14
+
15
+ app = FastAPI()
16
+
17
+ app.mount("/static", StaticFiles(directory="../frontend"), name="static")
18
+
19
+
20
+ # πŸ”Ή Load Emotion Detection Model
21
+ emotion_classifier = pipeline(
22
+ "zero-shot-classification",
23
+ model="MarfinF/marfin_emotion",
24
+ framework="pt"
25
+ )
26
+
27
+ # πŸ”Ή Emotion-to-Mood Mapping
28
+ emotion_to_mood = {
29
+ "senang": "happy",
30
+ "sedih": "sad",
31
+ "marah": "excited",
32
+ "takut": "relaxed",
33
+ "cinta": "romantic"
34
+ }
35
+
36
+ # πŸ”Ή WebSocket Clients
37
+ clients = {}
38
+ chat_history = deque(maxlen=4)
39
+
40
+ # πŸ”Ή Detect Emotion
41
+ def detect_emotion(text):
42
+ labels = ["takut", "marah", "sedih", "senang", "cinta"]
43
+ result = emotion_classifier(text, candidate_labels=labels)
44
+ top_emotion = result['labels'][0]
45
+ top_scores = result['scores'][0]
46
+ return top_emotion, top_scores
47
+
48
+ # πŸ”Ή Get Music Recommendations
49
+ def get_recommendations_by_mood(mood):
50
+ mood_to_genre = {
51
+ "happy": "pop",
52
+ "sad": "acoustic",
53
+ "excited": "rock",
54
+ "relaxed": "jazz",
55
+ "romantic": "rnb",
56
+ "chill": "chill"
57
+ }
58
+ genre = mood_to_genre.get(mood, "pop")
59
+ return [genre]
60
+
61
+ def softmax(scores):
62
+ exp_scores = [math.exp(score) for score in scores]
63
+ total = sum(exp_scores)
64
+ return [exp_score / total for exp_score in exp_scores]
65
+
66
+ # πŸ”Ή Broadcast User List
67
+ async def broadcast_user_list():
68
+ user_list = list(clients.keys())
69
+ message = json.dumps({
70
+ "type": "user_list",
71
+ "users": user_list
72
+ })
73
+ for client in clients.values():
74
+ await client.send_text(message)
75
+
76
+ # πŸ”Ή Periodic Music Recommender every 30 seconds
77
+ async def periodic_recommendation():
78
+ while True:
79
+ user_list = list(clients.keys())
80
+ if len(user_list) >= 2:
81
+ await asyncio.sleep(60)
82
+ if clients: # Only run if someone is connected
83
+ if len(chat_history) > 0:
84
+ # 1. Detect emotion dan ambil (label, score)
85
+ print("chat history")
86
+ print(chat_history)
87
+ emotions = [detect_emotion(msg) for msg in chat_history]
88
+ print("Detected Emotions:", emotions)
89
+
90
+ # 2. Group by emotion + sum score
91
+ emotion_score_sum = defaultdict(float)
92
+ for label, score in emotions:
93
+ emotion_score_sum[label] += score
94
+
95
+ # 3. Softmax
96
+ labels = list(emotion_score_sum.keys())
97
+ scores = list(emotion_score_sum.values())
98
+ softmax_scores = softmax(scores)
99
+
100
+ # 4. Pair label + softmax_score
101
+ softmax_result = list(zip(labels, softmax_scores))
102
+ print("Softmax Result:", softmax_result)
103
+
104
+ # 5. Dominant emotion
105
+ most_common_emotion = max(softmax_result, key=lambda x: x[1])[0]
106
+ print("Dominant Emotion:", most_common_emotion)
107
+ mood = emotion_to_mood.get(most_common_emotion, "chill")
108
+ music_recommendations = get_recommendations_by_mood(mood)
109
+ else:
110
+ music_recommendations = ["chill"] # default if no chat
111
+
112
+ recommendation_response = {
113
+ "username": "AI",
114
+ "message": f"🎢 Recommended Songs: {', '.join(music_recommendations)}",
115
+ "recommendations": music_recommendations
116
+ }
117
+
118
+ for client in clients.values():
119
+ await client.send_text(json.dumps(recommendation_response))
120
+ else:
121
+ await asyncio.sleep(2)
122
+ await broadcast_user_list()
123
+
124
+ # πŸ”Ή Start periodic task
125
+ @app.on_event("startup")
126
+ async def start_recommender():
127
+ asyncio.create_task(periodic_recommendation())
128
+
129
+ # πŸ”Ή WebSocket Endpoint
130
+ @app.websocket("/chat/{username}")
131
+ async def chat_endpoint(websocket: WebSocket, username: str):
132
+ await websocket.accept()
133
+ clients[username] = websocket
134
+ print(f"{username} joined")
135
+
136
+ await broadcast_user_list()
137
+
138
+ try:
139
+ while True:
140
+ data = await websocket.receive_text()
141
+ message_data = json.loads(data)
142
+
143
+ chat_history.append(message_data["message"])
144
+ response = {
145
+ "username": message_data["username"],
146
+ "message": message_data["message"]
147
+ }
148
+
149
+ # Broadcast message to all clients
150
+ for client in clients.values():
151
+ await client.send_text(json.dumps(response))
152
+
153
+ except Exception as e:
154
+ print(f"{username} disconnected: {e}")
155
+ del clients[username]
156
+ await broadcast_user_list()
157
+
158
+ @app.get("/")
159
+ def read_root():
160
+ return FileResponse("../frontend/index.html")
161
+
162
+ if __name__ == "__main__":
163
+ uvicorn.run(app, host="0.0.0.0", port=7860)
frontend/index.html ADDED
@@ -0,0 +1,268 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <title>🎢 Emotion Chat + Music Recommender</title>
6
+ <style>
7
+ body {
8
+ font-family: Arial, sans-serif;
9
+ background: #121212;
10
+ color: white;
11
+ text-align: center;
12
+ }
13
+
14
+ #chat-container {
15
+ width: 50%;
16
+ margin: auto;
17
+ padding: 10px;
18
+ }
19
+
20
+ .message {
21
+ padding: 10px;
22
+ margin: 5px 0;
23
+ border-radius: 10px;
24
+ }
25
+
26
+ .user {
27
+ background: #007bff;
28
+ color: white;
29
+ text-align: right;
30
+ }
31
+
32
+ .bot {
33
+ background: #28a745;
34
+ color: white;
35
+ }
36
+
37
+ input,
38
+ button {
39
+ margin-top: 10px;
40
+ padding: 10px;
41
+ border: none;
42
+ border-radius: 5px;
43
+ }
44
+
45
+ input {
46
+ width: 40%;
47
+ }
48
+
49
+ button {
50
+ background: #ff9800;
51
+ color: white;
52
+ cursor: pointer;
53
+ }
54
+
55
+ audio {
56
+ margin-top: 10px;
57
+ width: 100%;
58
+ }
59
+
60
+ #chat-box {
61
+ display: flex;
62
+ flex-direction: column;
63
+ gap: 8px;
64
+ }
65
+
66
+ .message {
67
+ padding: 10px 15px;
68
+ border-radius: 10px;
69
+ max-width: 70%;
70
+ word-wrap: break-word;
71
+ }
72
+
73
+ .user-message {
74
+ align-self: flex-end;
75
+ background-color: #007bff;
76
+ color: white;
77
+ border-bottom-right-radius: 0;
78
+ }
79
+
80
+ .other-user-message {
81
+ align-self: flex-start;
82
+ background-color: #e0e0e0;
83
+ color: black;
84
+ border-bottom-left-radius: 0;
85
+ }
86
+
87
+ .recommendation-message {
88
+ align-self: center;
89
+ background-color: #28a745;
90
+ color: white;
91
+ font-weight: bold;
92
+ border-radius: 12px;
93
+ padding: 15px;
94
+ text-align: center;
95
+ box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.2);
96
+ }
97
+
98
+ /* MODAL STYLES */
99
+ .modal {
100
+ display: block;
101
+ /* Show on load */
102
+ position: fixed;
103
+ z-index: 1;
104
+ left: 0;
105
+ top: 0;
106
+ width: 100%;
107
+ height: 100%;
108
+ background-color: rgba(0, 0, 0, 0.6);
109
+ display: flex;
110
+ justify-content: center;
111
+ align-items: center;
112
+ }
113
+
114
+ .modal-content {
115
+ background: white;
116
+ padding: 20px;
117
+ border-radius: 8px;
118
+ text-align: center;
119
+ color: black;
120
+ width: 300px;
121
+ }
122
+
123
+ .modal-content input {
124
+ width: 80%;
125
+ padding: 8px;
126
+ margin-top: 10px;
127
+ }
128
+
129
+ .modal-content button {
130
+ background: #007bff;
131
+ color: white;
132
+ padding: 8px 15px;
133
+ margin-top: 10px;
134
+ cursor: pointer;
135
+ border-radius: 5px;
136
+ border: none;
137
+ }
138
+ </style>
139
+ </head>
140
+
141
+ <body>
142
+ <h1>🎡 Smart MP Rekomend</h1>
143
+
144
+ <!-- Chat Box -->
145
+ <div id="chat-container">
146
+ <div id="user-list" style="margin-bottom: 10px; text-align: left;"></div>
147
+ <div id="chat-box"></div>
148
+ <br>
149
+ <input type="text" id="message" placeholder="Type your message..." disabled>
150
+ <button onclick="sendMessage()" disabled>Send</button>
151
+ <audio hidden="true" id="music-player" controls></audio>
152
+ </div>
153
+
154
+ <!-- MODAL FOR USERNAME INPUT -->
155
+ <div id="username-modal" class="modal">
156
+ <div class="modal-content">
157
+ <h2>Enter Your Name</h2>
158
+ <input type="text" id="username" placeholder="Your Name">
159
+ <br>
160
+ <button onclick="connectChat()">Join Chat</button>
161
+ </div>
162
+ </div>
163
+
164
+ <script>
165
+ let ws;
166
+ let username = "";
167
+ let musicStatus = "";
168
+ let delayedMusic = "";
169
+
170
+ function connectChat() {
171
+ username = document.getElementById("username").value.trim();
172
+ if (!username) {
173
+ alert("Please enter a username!");
174
+ return;
175
+ }
176
+
177
+ ws = new WebSocket(`${location.protocol === 'https:' ? 'wss' : 'ws'}://${location.host}/chat/${username}`);
178
+
179
+ document.getElementById("music-player").addEventListener("play", () => {
180
+ musicStatus = "playing"
181
+ });
182
+ document.getElementById("music-player").addEventListener("ended", () => {
183
+ musicStatus = "stop"
184
+ if (delayedMusic != "") playCurrentMusic(delayedMusic)
185
+ });
186
+
187
+ ws.onmessage = function (event) {
188
+ const data = JSON.parse(event.data);
189
+ const chatBox = document.getElementById("chat-box");
190
+
191
+ if (data.type === "user_list") {
192
+ const userListDiv = document.getElementById("user-list");
193
+ userListDiv.innerHTML = `<strong>πŸ‘₯ Online:</strong> ${data.users.join(", ")}`;
194
+ return;
195
+ }
196
+
197
+ if (data.username === username) {
198
+ // Udah di-render pas kirim, skip
199
+ return;
200
+ }
201
+
202
+ if (data.recommendations) {
203
+ if (musicStatus != "playing") {
204
+ playCurrentMusic(data.recommendations[0])
205
+ } else {
206
+ delayedMusic = data.recommendations[0]
207
+ }
208
+ } else {
209
+ let messageDiv = document.createElement("div");
210
+ messageDiv.classList.add("message", "other-user-message");
211
+ messageDiv.innerHTML = `<strong>${data.username}</strong>: ${data.message}`;
212
+ chatBox.appendChild(messageDiv);
213
+ }
214
+
215
+ chatBox.scrollTop = chatBox.scrollHeight;
216
+ };
217
+
218
+ ws.onopen = function () {
219
+ document.getElementById("message").disabled = false;
220
+ document.getElementById("message").nextElementSibling.disabled = false;
221
+
222
+ // Hide modal
223
+ document.getElementById("username-modal").style.display = "none";
224
+ };
225
+
226
+ ws.onerror = function () {
227
+ alert("WebSocket connection error! Ensure server is running.");
228
+ };
229
+
230
+ ws.onclose = function () {
231
+ alert("Connection closed!");
232
+ };
233
+ }
234
+
235
+ function playCurrentMusic(recommendationSong) {
236
+ const chatBox = document.getElementById("chat-box");
237
+ delayedMusic = ""
238
+ let recommendationDiv = document.createElement("div");
239
+ recommendationDiv.classList.add("recommendation-message");
240
+ recommendationDiv.innerHTML = `🎡 Recommended Song: "${recommendationSong}" 🎢`;
241
+ chatBox.appendChild(recommendationDiv);
242
+ document.getElementById("music-player").src = "https://sounds.pond5.com/inspirational-motivational-uplifting-acoustic-positive-music-102770115_nw_prev.m4a"
243
+ document.getElementById("music-player").play()
244
+ }
245
+
246
+ function sendMessage() {
247
+ let inputField = document.getElementById("message");
248
+ let messageText = inputField.value.trim();
249
+ let chatBox = document.getElementById("chat-box");
250
+
251
+ if (messageText === "" || !ws || ws.readyState !== WebSocket.OPEN) return;
252
+
253
+ // Send message to WebSocket server
254
+ ws.send(JSON.stringify({ username, message: messageText }));
255
+
256
+ // Show user message in chat
257
+ let messageDiv = document.createElement("div");
258
+ messageDiv.classList.add("message", "user-message");
259
+ messageDiv.innerHTML = `<strong>${username}</strong>: ${messageText}`;
260
+ chatBox.appendChild(messageDiv);
261
+
262
+ inputField.value = "";
263
+ chatBox.scrollTop = chatBox.scrollHeight;
264
+ }
265
+ </script>
266
+ </body>
267
+
268
+ </html>
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn
3
+ transformers
4
+ torch
5
+ numpy
6
+ asyncio
7
+ python-multipart
8
+ aiofiles