Spaces:
Build error
Build error
File size: 5,381 Bytes
20c8fc2 d5decf5 20c8fc2 |
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 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
import cors from "cors";
import dotenv from "dotenv";
import express from "express";
import { lipSync } from "./modules/lip-sync.mjs";
import customAPI from "./modules/customAPI.mjs";
import path from "path";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
dotenv.config();
const app = express();
app.use(express.json());
app.use(cors());
// Serve static files from the frontend build directory
app.use(express.static(path.join(__dirname, "../frontend/dist")));
const port = process.env.PORT || 3000;
// Helper function to select animation based on message content
const selectAnimation = (message) => {
const talkingAnimations = ["TalkingOne", "TalkingTwo", "Talking"];
// Check for emotional keywords to use specific animations
const lowerMessage = message.toLowerCase();
if (lowerMessage.includes("happy") || lowerMessage.includes("great") || lowerMessage.includes("wonderful")) {
return "HappyIdle";
}
if (lowerMessage.includes("sad") || lowerMessage.includes("sorry") || lowerMessage.includes("unfortunate")) {
return "SadIdle";
}
if (lowerMessage.includes("angry") || lowerMessage.includes("upset")) {
return "Angry";
}
if (lowerMessage.includes("surprised") || lowerMessage.includes("wow") || lowerMessage.includes("amazing")) {
return "Surprised";
}
// Default to random talking animation
return talkingAnimations[Math.floor(Math.random() * talkingAnimations.length)];
};
// Helper function to select facial expression
const selectFacialExpression = (message) => {
const lowerMessage = message.toLowerCase();
if (lowerMessage.includes("happy") || lowerMessage.includes("great") || lowerMessage.includes("wonderful")) {
return "smile";
}
if (lowerMessage.includes("sad") || lowerMessage.includes("sorry")) {
return "sad";
}
if (lowerMessage.includes("angry") || lowerMessage.includes("upset")) {
return "angry";
}
if (lowerMessage.includes("surprised") || lowerMessage.includes("wow")) {
return "surprised";
}
if (lowerMessage.includes("crazy") || lowerMessage.includes("wild")) {
return "crazy";
}
// Random default expressions
const defaultExpressions = ["smile", "default", "funnyFace"];
return defaultExpressions[Math.floor(Math.random() * defaultExpressions.length)];
};
// Health check endpoint
app.get("/health", (req, res) => {
res.json({ status: "ok", timestamp: new Date().toISOString() });
});
app.get("/voices", async (req, res) => {
// Return empty or dummy voices if frontend requests it
res.send([]);
});
app.post("/tts", async (req, res) => {
const userMessage = req.body.message;
const userId = req.body.userId;
if (!userMessage) {
res.send({ messages: [] });
return;
}
try {
console.log(`Received message: ${userMessage} from user: ${userId}`);
// 1. Get Chat Response from Custom API
const botResponseText = await customAPI.chat(userMessage, userId);
console.log(`Bot response: ${botResponseText}`);
// 2. Construct Message Object
// Intelligently select animations and facial expressions based on message content
const selectedAnimation = selectAnimation(botResponseText);
const selectedExpression = selectFacialExpression(botResponseText);
console.log(`Selected animation: ${selectedAnimation}, expression: ${selectedExpression}`);
const messages = [
{
text: botResponseText,
facialExpression: selectedExpression,
animation: selectedAnimation,
},
];
// 3. Generate Audio & LipSync
const syncedMessages = await lipSync({ messages });
res.send({ messages: syncedMessages });
} catch (error) {
console.error("Error in /tts:", error);
res.status(500).send({ error: "Internal Server Error" });
}
});
app.post("/sts", async (req, res) => {
const base64Audio = req.body.audio;
const userId = req.body.userId;
if (!base64Audio) {
res.status(400).send({ error: "No audio provided" });
return;
}
const audioData = Buffer.from(base64Audio, "base64");
try {
// 1. Convert Speech to Text
const userMessage = await customAPI.stt(audioData);
console.log(`User said (STT): ${userMessage}`);
// 2. Get Chat Response
const botResponseText = await customAPI.chat(userMessage, userId);
console.log(`Bot response: ${botResponseText}`);
// 3. Construct Message Object
const selectedAnimation = selectAnimation(botResponseText);
const selectedExpression = selectFacialExpression(botResponseText);
console.log(`Selected animation: ${selectedAnimation}, expression: ${selectedExpression}`);
const messages = [
{
text: botResponseText,
facialExpression: selectedExpression,
animation: selectedAnimation,
},
];
// 4. Generate Audio & LipSync
const syncedMessages = await lipSync({ messages });
res.send({ messages: syncedMessages });
} catch (error) {
console.error("Error in /sts:", error);
res.status(500).send({ error: "Internal Server Error" });
}
});
// Catch-all route to serve the frontend index.html for any non-API requests
app.get("*", (req, res) => {
res.sendFile(path.join(__dirname, "../frontend/dist/index.html"));
});
app.listen(port, () => {
console.log(`Jack is listening on port ${port}`);
});
|