Spaces:
Sleeping
Sleeping
import asyncio | |
from fastapi import FastAPI, WebSocket | |
from fastapi.staticfiles import StaticFiles | |
from fastapi.responses import FileResponse | |
import uvicorn | |
import json | |
from transformers import pipeline | |
from collections import deque | |
from collections import defaultdict | |
import math | |
import sys | |
import random | |
import os | |
from fastapi.middleware.cors import CORSMiddleware | |
sys.path.append(".") | |
app = FastAPI() | |
reset_timer = asyncio.Event() | |
# app.mount("/", StaticFiles(directory="frontend", html=True), name="frontend") | |
app.add_middleware( | |
CORSMiddleware, | |
allow_origins=["*"], # Bisa diubah ke domain spesifik untuk keamanan | |
allow_credentials=True, | |
allow_methods=["*"], | |
allow_headers=["*"], | |
) | |
emotion_classifier = pipeline( | |
"zero-shot-classification", | |
model="MarfinF/marfin_emotion", | |
force_download=True | |
) | |
clients = {} | |
chat_history = deque(maxlen=4) | |
mood_to_genre = { | |
"senang": { | |
"genre": "pop", | |
"word": "Poppin pops!" | |
}, | |
"sedih": { | |
"genre": "acoustic", | |
"word": "Playing acoustic" | |
}, | |
"marah": { | |
"genre": "rock", | |
"word": "Rock 'n play!!" | |
}, | |
"cinta": { | |
"genre": "romantic", | |
"word": "Playing romantic" | |
}, | |
"chill": { | |
"genre": "chill", | |
"word": "Chill!" | |
} | |
} | |
genre_to_song = { | |
"pop": ["https://sounds.pond5.com/inspirational-motivational-uplifting-acoustic-positive-music-102770115_nw_prev.m4a"], | |
"acoustic": ["https://sounds.pond5.com/sad-piano-music-063450274_nw_prev.m4a"], | |
"rock": ["https://sounds.pond5.com/powerful-gritty-action-extreme-rock-music-136693652_nw_prev.m4a"], | |
"romantic": ["https://sounds.pond5.com/bloom-sweet-tender-delicate-romantic-music-158563013_nw_prev.m4a"], | |
"chill": ["https://sounds.pond5.com/fall-chill-music-088328584_nw_prev.m4a"] | |
} | |
def detect_emotion(text): | |
labels = ["marah", "sedih", "senang", "cinta"] | |
result = emotion_classifier(text, candidate_labels=labels) | |
top_emotion = result['labels'][0] | |
top_scores = result['scores'][0] | |
return top_emotion, top_scores | |
def get_recommendations_by_mood(genre): | |
songs = genre_to_song.get(genre, []) | |
random.shuffle(songs) | |
return songs[:3] # Return top 3 shuffled songs | |
def softmax(scores): | |
exp_scores = [math.exp(score) for score in scores] | |
total = sum(exp_scores) | |
return [exp_score / total for exp_score in exp_scores] | |
# ๐น Broadcast User List | |
async def broadcast_user_list(): | |
user_list = list(clients.keys()) | |
message = json.dumps({ | |
"type": "user_list", | |
"users": user_list | |
}) | |
for client in clients.values(): | |
await client.send_text(message) | |
# ๐น Periodic Music Recommender every 30 seconds | |
async def periodic_recommendation(): | |
while True: | |
user_list = list(clients.keys()) | |
if len(user_list) >= 2: | |
sleep_task = asyncio.create_task(asyncio.sleep(60)) # Start sleep | |
try: | |
await asyncio.wait_for(sleep_task, timeout=60) # Wait 60s unless interrupted | |
except asyncio.TimeoutError: | |
pass # Timer completed without reset | |
except asyncio.CancelledError: | |
continue # Timer was reset, restart loop | |
if reset_timer.is_set(): # If reset happened, restart countdown | |
continue | |
if clients: # Only run if someone is connected | |
if len(chat_history) > 0: | |
# 1. Detect emotion dan ambil (label, score) | |
print("chat history") | |
print(chat_history) | |
emotions = [detect_emotion(msg) for msg in chat_history] | |
print("Detected Emotions:", emotions) | |
# 2. Group by emotion + sum score | |
emotion_score_sum = defaultdict(float) | |
for label, score in emotions: | |
emotion_score_sum[label] += score | |
# 3. Softmax | |
labels = list(emotion_score_sum.keys()) | |
scores = list(emotion_score_sum.values()) | |
softmax_scores = softmax(scores) | |
# 4. Pair label + softmax_score | |
softmax_result = list(zip(labels, softmax_scores)) | |
print("Softmax Result:", softmax_result) | |
# 5. Dominant emotion | |
most_common_emotion = max(softmax_result, key=lambda x: x[1])[0] | |
print("Dominant Emotion:", most_common_emotion) | |
music = mood_to_genre.get(most_common_emotion, mood_to_genre["chill"]) | |
music_recommendations = get_recommendations_by_mood(music["genre"]) | |
word = music["word"] | |
else: | |
music_recommendations = get_recommendations_by_mood("chill") | |
word = "Chill!" | |
recommendation_response = { | |
"recommendations": music_recommendations, | |
"genre": word | |
} | |
for client in clients.values(): | |
await client.send_text(json.dumps(recommendation_response)) | |
else: | |
await asyncio.sleep(2) | |
await broadcast_user_list() | |
await reset_recommendation_timer() | |
async def reset_recommendation_timer(): | |
"""Call this function when you want to reset the timer to 60 seconds.""" | |
if reset_timer.is_set(): # Check if the timer is running before resetting | |
reset_timer.clear() # Clear the event before setting it again | |
reset_timer.set() # Trigger the reset | |
async def start_recommender(): | |
asyncio.create_task(periodic_recommendation()) | |
async def chat_endpoint(websocket: WebSocket, username: str): | |
await websocket.accept() | |
clients[username] = websocket | |
print(f"{username} joined") | |
await broadcast_user_list() | |
try: | |
while True: | |
data = await websocket.receive_text() | |
message_data = json.loads(data) | |
chat_history.append(message_data["message"]) | |
response = { | |
"username": message_data["username"], | |
"message": message_data["message"] | |
} | |
# Broadcast message to all clients | |
for client in clients.values(): | |
await client.send_text(json.dumps(response)) | |
except Exception as e: | |
print(f"{username} disconnected: {e}") | |
del clients[username] | |
await broadcast_user_list() | |
def read_root(): | |
print("frontend") | |
return FileResponse("frontend/index.html") | |
if __name__ == "__main__": | |
uvicorn.run(app, host="0.0.0.0", port=7860) | |