functions
This commit is contained in:
+95
-22
@@ -1,7 +1,13 @@
|
||||
const { onCall, onRequest, HttpsError } = require("firebase-functions/v2/https");
|
||||
const {
|
||||
onCall,
|
||||
onRequest,
|
||||
HttpsError,
|
||||
} = require("firebase-functions/v2/https");
|
||||
const axios = require("axios");
|
||||
const admin = require("firebase-admin");
|
||||
const { logger } = require("firebase-functions/logger");
|
||||
const { ALERT_TYPE, refList } = require("../index");
|
||||
const { sendNotification } = require("./notifications");
|
||||
const { SUNO_API_KEY } = require("../config/keys");
|
||||
const {
|
||||
SUNO_MODEL,
|
||||
@@ -11,9 +17,6 @@ const {
|
||||
SUNO_STATUS_PATH,
|
||||
} = require("../config/suno");
|
||||
|
||||
// Initialiser Firestorey
|
||||
const db = admin.firestore();
|
||||
|
||||
/**
|
||||
* Marque un projet comme échoué suite à une erreur Suno
|
||||
* @param {string} projectId - Identifiant du projet
|
||||
@@ -22,7 +25,9 @@ const db = admin.firestore();
|
||||
async function markProjectMusicFailure(projectId, error) {
|
||||
if (!projectId) return;
|
||||
try {
|
||||
const docRef = db.collection("projects").doc(projectId);
|
||||
const docRef = refList.projects.doc(projectId);
|
||||
const projectSnap = await docRef.get();
|
||||
const projectData = projectSnap?.data() || {};
|
||||
const status = error?.response?.status || error?.status || null;
|
||||
const sunoMessage =
|
||||
error?.response?.data?.msg ||
|
||||
@@ -44,12 +49,44 @@ async function markProjectMusicFailure(projectId, error) {
|
||||
musicError: errorPayload,
|
||||
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
||||
},
|
||||
{ merge: true }
|
||||
{ merge: true },
|
||||
);
|
||||
|
||||
const receiverId =
|
||||
typeof projectData?.userId === "string" && projectData.userId.trim()
|
||||
? projectData.userId.trim()
|
||||
: null;
|
||||
if (receiverId) {
|
||||
const projectTitle =
|
||||
typeof projectData?.title === "string" && projectData.title.trim()
|
||||
? projectData.title.trim()
|
||||
: "ton projet";
|
||||
const message = `La génération de musique pour "${projectTitle}" a échoué.`;
|
||||
try {
|
||||
await sendNotification({
|
||||
sender: "SYSTEM",
|
||||
receiver: receiverId,
|
||||
receiverCollection: "users",
|
||||
title: "Génération de musique échouée",
|
||||
message,
|
||||
data: {
|
||||
type: ALERT_TYPE?.MUSIC_GENERATION_FAILED,
|
||||
projectId,
|
||||
projectTitle,
|
||||
error: errorPayload,
|
||||
},
|
||||
});
|
||||
} catch (notifyError) {
|
||||
console.error(
|
||||
"[markProjectMusicFailure] Failed to send notification:",
|
||||
notifyError,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(
|
||||
"❌ [generateMusic] Impossible de marquer le projet en erreur:",
|
||||
err
|
||||
err,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -80,7 +117,7 @@ function extractVoiceStrings(voice) {
|
||||
}
|
||||
if (typeof voice === "object") {
|
||||
return Object.values(voice).filter(
|
||||
(v) => typeof v === "string" && v.trim()
|
||||
(v) => typeof v === "string" && v.trim(),
|
||||
);
|
||||
}
|
||||
return [];
|
||||
@@ -134,7 +171,7 @@ function detectVocalGender(voiceInput = "") {
|
||||
if (Array.isArray(voiceInput)) {
|
||||
const baseItem = voiceInput.find(
|
||||
(v) =>
|
||||
v && (v.category === "base" || v?.category?.toLowerCase() === "base")
|
||||
v && (v.category === "base" || v?.category?.toLowerCase() === "base"),
|
||||
);
|
||||
const text = baseItem ? baseItem.value || baseItem.text || baseItem : null;
|
||||
if (text) return detectVocalGender(text);
|
||||
@@ -346,7 +383,7 @@ exports.generateMusic = onCall(async ({ data = {} }) => {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${SUNO_API_KEY}`,
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const parsed = response.data;
|
||||
@@ -370,7 +407,7 @@ exports.generateMusic = onCall(async ({ data = {} }) => {
|
||||
} catch (markErr) {
|
||||
console.error(
|
||||
"❌ Erreur lors de la mise à jour du statut de projet:",
|
||||
markErr
|
||||
markErr,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -412,7 +449,7 @@ exports.getSunoStatus = onCall(async ({ data = {} }) => {
|
||||
Authorization: `Bearer ${SUNO_API_KEY}`,
|
||||
},
|
||||
timeout: 30000, // 30 secondes de timeout
|
||||
}
|
||||
},
|
||||
);
|
||||
parsed = response.data;
|
||||
|
||||
@@ -547,16 +584,24 @@ exports.sunoCallback = onRequest({ methods: ["POST"] }, async (req, res) => {
|
||||
|
||||
// 1) Récupérer le projectId associé au taskId
|
||||
let projectId = null;
|
||||
const projSnap = await db
|
||||
.collection("projects")
|
||||
const projSnap = await refList.projects
|
||||
.where("sunoTaskId", "==", taskId)
|
||||
.limit(1)
|
||||
.get();
|
||||
if (projSnap.empty) {
|
||||
throw new Error("Aucun projet trouvé pour ce taskId");
|
||||
}
|
||||
projectId = projSnap.docs[0].id;
|
||||
const { userId } = projSnap.docs[0].data() || {};
|
||||
const projectDoc = projSnap.docs[0];
|
||||
const projectData = projectDoc?.data() || {};
|
||||
projectId = projectDoc.id;
|
||||
const userId =
|
||||
typeof projectData.userId === "string" && projectData.userId.trim()
|
||||
? projectData.userId.trim()
|
||||
: null;
|
||||
const projectTitle =
|
||||
typeof projectData.title === "string" && projectData.title.trim()
|
||||
? projectData.title.trim()
|
||||
: "ton projet";
|
||||
|
||||
if (!projectId) {
|
||||
console.warn("⚠️ [SunoCallback] Aucun projet trouvé pour", { taskId });
|
||||
@@ -567,7 +612,7 @@ exports.sunoCallback = onRequest({ methods: ["POST"] }, async (req, res) => {
|
||||
const audioUrls = tracks
|
||||
.map(
|
||||
(t) =>
|
||||
t.audio_url || t.audioUrl || t.stream_audio_url || t.streamAudioUrl
|
||||
t.audio_url || t.audioUrl || t.stream_audio_url || t.streamAudioUrl,
|
||||
)
|
||||
.filter(Boolean)
|
||||
.slice(0, 2);
|
||||
@@ -582,7 +627,7 @@ exports.sunoCallback = onRequest({ methods: ["POST"] }, async (req, res) => {
|
||||
has_audio_url: !!t.audio_url,
|
||||
has_stream_audio_url: !!t.stream_audio_url,
|
||||
})),
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -607,14 +652,14 @@ exports.sunoCallback = onRequest({ methods: ["POST"] }, async (req, res) => {
|
||||
},
|
||||
});
|
||||
const downloadUrl = `https://firebasestorage.googleapis.com/v0/b/${bucket.name}/o/${encodeURIComponent(
|
||||
path
|
||||
path,
|
||||
)}?alt=media&token=${token}`;
|
||||
console.log("✅ [SunoCallback] Sauvegardé:", path, "URL:", downloadUrl);
|
||||
return { path, url: downloadUrl };
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`❌ [SunoCallback] Échec save piste ${index + 1}:`,
|
||||
e.message
|
||||
e.message,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
@@ -628,19 +673,47 @@ exports.sunoCallback = onRequest({ methods: ["POST"] }, async (req, res) => {
|
||||
// 4) Mettre à jour le statut du projet
|
||||
try {
|
||||
const musicUrls = [p1?.url, p2?.url].filter(Boolean);
|
||||
await db.collection("projects").doc(projectId).set(
|
||||
await refList.projects.doc(projectId).set(
|
||||
{
|
||||
musicStatus: "GENERATED",
|
||||
musicUrls,
|
||||
musicError: admin.firestore.FieldValue.delete(),
|
||||
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
||||
},
|
||||
{ merge: true }
|
||||
{ merge: true },
|
||||
);
|
||||
console.log("🏷️ [SunoCallback] Projet marqué GENERATED", {
|
||||
projectId,
|
||||
musicUrlsCount: musicUrls.length,
|
||||
});
|
||||
|
||||
if (userId) {
|
||||
const successMessage =
|
||||
musicUrls.length > 0
|
||||
? `Ta musique pour "${projectTitle}" est prête.`
|
||||
: `La génération de musique pour "${projectTitle}" est terminée.`;
|
||||
try {
|
||||
await sendNotification({
|
||||
sender: "SYSTEM",
|
||||
receiver: userId,
|
||||
receiverCollection: "users",
|
||||
title: "Musique prête",
|
||||
message: successMessage,
|
||||
data: {
|
||||
type: ALERT_TYPE?.MUSIC_GENERATION_SUCCESS,
|
||||
projectId,
|
||||
projectTitle,
|
||||
musicUrls,
|
||||
taskId,
|
||||
},
|
||||
});
|
||||
} catch (notifError) {
|
||||
console.error(
|
||||
"[sunoCallback] Failed to send success notification:",
|
||||
notifError,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("❌ [SunoCallback] Erreur maj projet:", e.message);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user