auto timestamp lyricks when song selected
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
exports.SUNO_API_BASE = "https://api.sunoapi.org";
|
||||||
|
exports.SUNO_API_PATH = "/api/v1/generate";
|
||||||
|
exports.SUNO_STATUS_PATH = "/api/v1/generate/record-info";
|
||||||
|
exports.SUNO_TIMESTAMPED_LYRICS_PATH =
|
||||||
|
"/api/v1/generate/get-timestamped-lyrics";
|
||||||
|
exports.SUNO_MODEL = "V4_5";
|
||||||
|
exports.SUNO_CALLBACK_URL =
|
||||||
|
"https://us-central1-musicland-d33f9.cloudfunctions.net/music-sunoCallback";
|
||||||
+4
-1
@@ -4,9 +4,12 @@ const admin = require("firebase-admin");
|
|||||||
// Avoid bundling a service account key and hardcoding project/bucket.
|
// Avoid bundling a service account key and hardcoding project/bucket.
|
||||||
admin.initializeApp();
|
admin.initializeApp();
|
||||||
|
|
||||||
// Les fonctions IA ont été déplacées vers helpers/gemini
|
exports.refList = {
|
||||||
|
projects: admin.firestore().collection("projects"),
|
||||||
|
};
|
||||||
|
|
||||||
// Exporter toutes les fonctions
|
// Exporter toutes les fonctions
|
||||||
exports.music = require("./src/music");
|
exports.music = require("./src/music");
|
||||||
exports.lyrics = require("./src/lyrics");
|
exports.lyrics = require("./src/lyrics");
|
||||||
exports.cover = require("./src/cover");
|
exports.cover = require("./src/cover");
|
||||||
|
exports.projects = require("./src/project");
|
||||||
|
|||||||
+98
-5
@@ -1,6 +1,14 @@
|
|||||||
const { onCall } = require("firebase-functions/v2/https");
|
const { onCall } = require("firebase-functions/v2/https");
|
||||||
const { z } = require("genkit");
|
const { z } = require("genkit");
|
||||||
const { generateAI, analyseLyrics } = require("../helpers/gemini");
|
const { generateAI, analyseLyrics } = require("../helpers/gemini");
|
||||||
|
const {
|
||||||
|
SUNO_API_BASE,
|
||||||
|
SUNO_TIMESTAMPED_LYRICS_PATH,
|
||||||
|
} = require("../config/suno");
|
||||||
|
const { SUNO_API_KEY } = require("../config/keys");
|
||||||
|
const axios = require("axios");
|
||||||
|
const admin = require("firebase-admin");
|
||||||
|
const { refList } = require("../index");
|
||||||
|
|
||||||
exports.generateLyrics = onCall({}, async ({ auth = {}, data = {} }) => {
|
exports.generateLyrics = onCall({}, async ({ auth = {}, data = {} }) => {
|
||||||
try {
|
try {
|
||||||
@@ -101,14 +109,25 @@ exports.analyseLyricsToxicity = onCall({}, async ({ data = {} }) => {
|
|||||||
lyrics: z
|
lyrics: z
|
||||||
.union([
|
.union([
|
||||||
z.string(),
|
z.string(),
|
||||||
z.object({ couplet: z.string().optional(), refrain: z.string().optional() }),
|
z.object({
|
||||||
z.array(z.object({ type: z.string().optional(), lyrics: z.string().optional() })),
|
couplet: z.string().optional(),
|
||||||
|
refrain: z.string().optional(),
|
||||||
|
}),
|
||||||
|
z.array(
|
||||||
|
z.object({
|
||||||
|
type: z.string().optional(),
|
||||||
|
lyrics: z.string().optional(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
])
|
])
|
||||||
.describe("Paroles à analyser (formats supportés)"),
|
.describe("Paroles à analyser (formats supportés)"),
|
||||||
});
|
});
|
||||||
|
|
||||||
const parsed = requestSchema.parse(data || {});
|
const parsed = requestSchema.parse(data || {});
|
||||||
const result = await analyseLyrics({ title: parsed.title || "", lyrics: parsed.lyrics });
|
const result = await analyseLyrics({
|
||||||
|
title: parsed.title || "",
|
||||||
|
lyrics: parsed.lyrics,
|
||||||
|
});
|
||||||
|
|
||||||
// Prépare un message lisible pour le front
|
// Prépare un message lisible pour le front
|
||||||
const highCats = Object.entries(result.categories || {})
|
const highCats = Object.entries(result.categories || {})
|
||||||
@@ -140,8 +159,82 @@ exports.analyseLyricsToxicity = onCall({}, async ({ data = {} }) => {
|
|||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
errorCode: "ANALYSE_FAILED",
|
errorCode: "ANALYSE_FAILED",
|
||||||
message:
|
message: "Erreur lors de l'analyse de la toxicité. Réessayez plus tard.",
|
||||||
"Erreur lors de l'analyse de la toxicité. Réessayez plus tard.",
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère les timestamps (aligned words) pour une génération Suno
|
||||||
|
* Attend: { projectId: string }
|
||||||
|
*/
|
||||||
|
async function getSunoTimestamps(projectId) {
|
||||||
|
try {
|
||||||
|
const { sunoTaskId, songIndex } = (
|
||||||
|
await refList.projects.doc(projectId).get()
|
||||||
|
).data();
|
||||||
|
if (!sunoTaskId) {
|
||||||
|
throw new Error("TaskId manquant");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (songIndex < 0) {
|
||||||
|
throw new Error("musicIndex invalide");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!projectId || typeof projectId !== "string" || !projectId.trim()) {
|
||||||
|
throw new Error("projectId manquant ou invalide");
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("🔎 Récupération des timestamps Suno", {
|
||||||
|
sunoTaskId,
|
||||||
|
songIndex,
|
||||||
|
});
|
||||||
|
|
||||||
|
let response;
|
||||||
|
let parsed;
|
||||||
|
|
||||||
|
response = await axios.post(
|
||||||
|
`${SUNO_API_BASE}${SUNO_TIMESTAMPED_LYRICS_PATH}`,
|
||||||
|
{ taskId: sunoTaskId, musicIndex: songIndex },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: `Bearer ${SUNO_API_KEY}`,
|
||||||
|
},
|
||||||
|
timeout: 30000,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
parsed = response.data;
|
||||||
|
console.log("📊 Réponse timestamps Suno API:", parsed?.code, parsed?.msg);
|
||||||
|
|
||||||
|
const dataToReturn = parsed?.data || parsed || {};
|
||||||
|
|
||||||
|
await refList.projects.doc(projectId).set(
|
||||||
|
{
|
||||||
|
musicTimestamps: {
|
||||||
|
[songIndex]: dataToReturn,
|
||||||
|
},
|
||||||
|
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
||||||
|
},
|
||||||
|
{ merge: true },
|
||||||
|
);
|
||||||
|
console.log("💾 Timestamps sauvegardés dans Firestore", {
|
||||||
|
projectId,
|
||||||
|
songIndex,
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error("❌ Erreur interne getSunoTimestamps:", error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: {
|
||||||
|
message: error.message,
|
||||||
|
type: "INTERNAL_ERROR",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getSunoTimestamps = getSunoTimestamps;
|
||||||
|
|||||||
+21
-148
@@ -3,23 +3,17 @@ const axios = require("axios");
|
|||||||
const admin = require("firebase-admin");
|
const admin = require("firebase-admin");
|
||||||
const { logger } = require("firebase-functions/logger");
|
const { logger } = require("firebase-functions/logger");
|
||||||
const { SUNO_API_KEY } = require("../config/keys");
|
const { SUNO_API_KEY } = require("../config/keys");
|
||||||
|
const {
|
||||||
|
SUNO_MODEL,
|
||||||
|
SUNO_CALLBACK_URL,
|
||||||
|
SUNO_API_BASE,
|
||||||
|
SUNO_API_PATH,
|
||||||
|
SUNO_STATUS_PATH,
|
||||||
|
} = require("../config/suno");
|
||||||
|
|
||||||
// Initialiser Firestore
|
// Initialiser Firestorey
|
||||||
const db = admin.firestore();
|
const db = admin.firestore();
|
||||||
|
|
||||||
/**
|
|
||||||
* ===== CONFIG GLOBALE =====
|
|
||||||
*/
|
|
||||||
const SUNO_API_BASE = "https://api.sunoapi.org";
|
|
||||||
const SUNO_API_PATH = "/api/v1/generate";
|
|
||||||
const SUNO_STATUS_PATH = "/api/v1/generate/record-info";
|
|
||||||
const SUNO_TIMESTAMPED_LYRICS_PATH = "/api/v1/generate/get-timestamped-lyrics";
|
|
||||||
const SUNO_MODEL = "V4_5";
|
|
||||||
// const SUNO_MODEL = "V3_5";
|
|
||||||
const SUNO_CALLBACK_URL =
|
|
||||||
"https://us-central1-musicland-d33f9.cloudfunctions.net/music-sunoCallback";
|
|
||||||
// "https://music-sunocallback-ifdrkc2c3a-uc.a.run.app";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Limite la longueur d'une chaîne
|
* Limite la longueur d'une chaîne
|
||||||
* @param {string} str - La chaîne à limiter
|
* @param {string} str - La chaîne à limiter
|
||||||
@@ -45,7 +39,9 @@ function extractVoiceStrings(voice) {
|
|||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
}
|
}
|
||||||
if (typeof voice === "object") {
|
if (typeof voice === "object") {
|
||||||
return Object.values(voice).filter((v) => typeof v === "string" && v.trim());
|
return Object.values(voice).filter(
|
||||||
|
(v) => typeof v === "string" && v.trim(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -57,14 +53,17 @@ function voiceTagsFromUserChoice(voiceInput = "") {
|
|||||||
const v = String(txt || "").toLowerCase();
|
const v = String(txt || "").toLowerCase();
|
||||||
|
|
||||||
// Gender / ensemble
|
// Gender / ensemble
|
||||||
if (/féminin|feminin|féminine|feminine/.test(v)) tags.push("Female lead vocal");
|
if (/féminin|feminin|féminine|feminine/.test(v))
|
||||||
|
tags.push("Female lead vocal");
|
||||||
if (/masculin|masculine/.test(v)) tags.push("Male lead vocal");
|
if (/masculin|masculine/.test(v)) tags.push("Male lead vocal");
|
||||||
if (/deux voix|duo|deux chanteurs/.test(v)) tags.push("Duet (male and female voices)");
|
if (/deux voix|duo|deux chanteurs/.test(v))
|
||||||
|
tags.push("Duet (male and female voices)");
|
||||||
if (/choeu?r|gospel/.test(v)) tags.push("Gospel choir vocals");
|
if (/choeu?r|gospel/.test(v)) tags.push("Gospel choir vocals");
|
||||||
|
|
||||||
// Delivery/techniques
|
// Delivery/techniques
|
||||||
if (/rap\b/.test(v)) tags.push("Rap vocal");
|
if (/rap\b/.test(v)) tags.push("Rap vocal");
|
||||||
if (/slam|parl[ée]e|chant parl[ée]/.test(v)) tags.push("Spoken word / slam");
|
if (/slam|parl[ée]e|chant parl[ée]/.test(v))
|
||||||
|
tags.push("Spoken word / slam");
|
||||||
if (/raconte|narrat/.test(v)) tags.push("Narration / spoken narrator");
|
if (/raconte|narrat/.test(v)) tags.push("Narration / spoken narrator");
|
||||||
if (/cri|scream|rugueu|growl/.test(v)) tags.push("Screamed vocals");
|
if (/cri|scream|rugueu|growl/.test(v)) tags.push("Screamed vocals");
|
||||||
if (/sprech/.test(v)) tags.push("Sprechgesang (sung-spoken) style");
|
if (/sprech/.test(v)) tags.push("Sprechgesang (sung-spoken) style");
|
||||||
@@ -93,7 +92,10 @@ function voiceTagsFromUserChoice(voiceInput = "") {
|
|||||||
function detectVocalGender(voiceInput = "") {
|
function detectVocalGender(voiceInput = "") {
|
||||||
// If array of objects with category, prefer 'base'
|
// If array of objects with category, prefer 'base'
|
||||||
if (Array.isArray(voiceInput)) {
|
if (Array.isArray(voiceInput)) {
|
||||||
const baseItem = voiceInput.find((v) => v && (v.category === "base" || v?.category?.toLowerCase() === "base"));
|
const baseItem = voiceInput.find(
|
||||||
|
(v) =>
|
||||||
|
v && (v.category === "base" || v?.category?.toLowerCase() === "base"),
|
||||||
|
);
|
||||||
const text = baseItem ? baseItem.value || baseItem.text || baseItem : null;
|
const text = baseItem ? baseItem.value || baseItem.text || baseItem : null;
|
||||||
if (text) return detectVocalGender(text);
|
if (text) return detectVocalGender(text);
|
||||||
// Fallback: scan all
|
// Fallback: scan all
|
||||||
@@ -430,135 +432,6 @@ exports.getSunoStatus = onCall(async ({ data = {} }) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Récupère les timestamps (aligned words) pour une génération Suno
|
|
||||||
* Attend: { taskId: string, musicIndex: number, projectId: string }
|
|
||||||
*/
|
|
||||||
exports.getSunoTimestamps = onCall(async ({ data = {} }) => {
|
|
||||||
try {
|
|
||||||
const { taskId, musicIndex, projectId } = data || {};
|
|
||||||
|
|
||||||
if (!taskId) {
|
|
||||||
throw new Error("TaskId manquant");
|
|
||||||
}
|
|
||||||
|
|
||||||
const index = Number(musicIndex);
|
|
||||||
if (!Number.isFinite(index) || index < 0) {
|
|
||||||
throw new Error("musicIndex invalide");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!projectId || typeof projectId !== "string" || !projectId.trim()) {
|
|
||||||
throw new Error("projectId manquant ou invalide");
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("🔎 Récupération des timestamps Suno", {
|
|
||||||
taskId,
|
|
||||||
musicIndex: index,
|
|
||||||
});
|
|
||||||
|
|
||||||
let response;
|
|
||||||
let parsed;
|
|
||||||
|
|
||||||
try {
|
|
||||||
response = await axios.post(
|
|
||||||
`${SUNO_API_BASE}${SUNO_TIMESTAMPED_LYRICS_PATH}`,
|
|
||||||
{ taskId, musicIndex: index },
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
Authorization: `Bearer ${SUNO_API_KEY}`,
|
|
||||||
},
|
|
||||||
timeout: 30000,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
parsed = response.data;
|
|
||||||
console.log("📊 Réponse timestamps Suno API:", parsed?.code, parsed?.msg);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("❌ Erreur API Suno Timestamps:", {
|
|
||||||
message: error.message,
|
|
||||||
status: error.response?.status,
|
|
||||||
statusText: error.response?.statusText,
|
|
||||||
data: error.response?.data,
|
|
||||||
config: {
|
|
||||||
url: error.config?.url,
|
|
||||||
headers: error.config?.headers
|
|
||||||
? { ...error.config.headers, Authorization: "[redacted]" }
|
|
||||||
: undefined,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const errorStatus = error.response?.status || "UNKNOWN";
|
|
||||||
const errorMessage =
|
|
||||||
error.response?.data?.msg ||
|
|
||||||
error.response?.data?.message ||
|
|
||||||
error.response?.statusText ||
|
|
||||||
error.message;
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
taskId,
|
|
||||||
musicIndex: index,
|
|
||||||
error: {
|
|
||||||
status: errorStatus,
|
|
||||||
message: errorMessage,
|
|
||||||
type: "API_ERROR",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const dataToReturn = parsed?.data || parsed || {};
|
|
||||||
|
|
||||||
// Sauvegarde obligatoire dans le document projet
|
|
||||||
let saved = false;
|
|
||||||
try {
|
|
||||||
await db
|
|
||||||
.collection("projects")
|
|
||||||
.doc(projectId)
|
|
||||||
.set(
|
|
||||||
{
|
|
||||||
musicTimestamps: {
|
|
||||||
[musicIndex]: dataToReturn,
|
|
||||||
},
|
|
||||||
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
||||||
},
|
|
||||||
{ merge: true },
|
|
||||||
);
|
|
||||||
saved = true;
|
|
||||||
console.log("💾 Timestamps sauvegardés dans Firestore", {
|
|
||||||
projectId,
|
|
||||||
index,
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
console.error(
|
|
||||||
"❌ Échec sauvegarde timestamps Firestore:",
|
|
||||||
e?.message || e,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
taskId,
|
|
||||||
musicIndex: index,
|
|
||||||
projectId,
|
|
||||||
saved,
|
|
||||||
musicTimestamps: dataToReturn,
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("❌ Erreur interne getSunoTimestamps:", error);
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
taskId: data?.taskId,
|
|
||||||
musicIndex: data?.musicIndex,
|
|
||||||
error: {
|
|
||||||
message: error.message,
|
|
||||||
type: "INTERNAL_ERROR",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Removed Firestore onUpdate trigger for timestamps: using callable instead.
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cloud Function pour recevoir les callbacks de l'API Suno
|
* Cloud Function pour recevoir les callbacks de l'API Suno
|
||||||
* Cette fonction est appelée par l'API Suno lorsque la génération
|
* Cette fonction est appelée par l'API Suno lorsque la génération
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
const { onDocumentWritten } = require("firebase-functions/firestore");
|
||||||
|
const { getSunoTimestamps } = require("./lyrics");
|
||||||
|
|
||||||
|
exports.onProjectUpdate = onDocumentWritten(
|
||||||
|
"projects/{projectId}",
|
||||||
|
async (projectSnap) => {
|
||||||
|
try {
|
||||||
|
const { projectId } = projectSnap.params;
|
||||||
|
|
||||||
|
const currentData = projectSnap?.data?.after?.data() || null;
|
||||||
|
const previousData = projectSnap?.data?.before?.data() || null;
|
||||||
|
|
||||||
|
if (!previousData?.songUrl && currentData?.songUrl) {
|
||||||
|
await getSunoTimestamps(projectId);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
@@ -27,9 +27,9 @@ if (!firebase?.apps?.filter(({ name_ }) => name_ === "[DEFAULT]").length) {
|
|||||||
persistence: getReactNativePersistence(AsyncStorage),
|
persistence: getReactNativePersistence(AsyncStorage),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (__DEV__) {
|
// if (__DEV__) {
|
||||||
firebase.functions().useEmulator("localhost", 5001);
|
// firebase.functions().useEmulator("localhost", 5001);
|
||||||
}
|
// }
|
||||||
console.log("Firebase init");
|
console.log("Firebase init");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,10 +34,10 @@ const SongReady = () => {
|
|||||||
1: { pos: 0, dur: 0 },
|
1: { pos: 0, dur: 0 },
|
||||||
});
|
});
|
||||||
const player0 = useAudioPlayer(
|
const player0 = useAudioPlayer(
|
||||||
musicUrls[0] ? { uri: musicUrls[0] } : undefined
|
musicUrls[0] ? { uri: musicUrls[0] } : undefined,
|
||||||
);
|
);
|
||||||
const player1 = useAudioPlayer(
|
const player1 = useAudioPlayer(
|
||||||
musicUrls[1] ? { uri: musicUrls[1] } : undefined
|
musicUrls[1] ? { uri: musicUrls[1] } : undefined,
|
||||||
);
|
);
|
||||||
const wasPlayingBeforeSeek = useRef({ 0: false, 1: false });
|
const wasPlayingBeforeSeek = useRef({ 0: false, 1: false });
|
||||||
|
|
||||||
@@ -120,26 +120,6 @@ const SongReady = () => {
|
|||||||
songUrl: url,
|
songUrl: url,
|
||||||
updatedAt: firebase.firestore.FieldValue.serverTimestamp(),
|
updatedAt: firebase.firestore.FieldValue.serverTimestamp(),
|
||||||
});
|
});
|
||||||
// Déclenche la génération des timestamps immédiatement après avoir fixé la songUrl
|
|
||||||
try {
|
|
||||||
console.log("task id is : ", selectedProject?.sunoTaskId);
|
|
||||||
const taskId = selectedProject?.sunoTaskId || null;
|
|
||||||
if (taskId) {
|
|
||||||
const callable = firebase
|
|
||||||
.functions()
|
|
||||||
.httpsCallable("music-getSunoTimestamps");
|
|
||||||
// Fire-and-forget (ne bloque pas l'UI)
|
|
||||||
callable({
|
|
||||||
taskId,
|
|
||||||
musicIndex: Number(selectedIndex) || 0,
|
|
||||||
projectId,
|
|
||||||
}).catch((e) =>
|
|
||||||
console.log("getSunoTimestamps (non-blocking)", e?.message)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log("getSunoTimestamps init error", e?.message);
|
|
||||||
}
|
|
||||||
await player0?.pause?.();
|
await player0?.pause?.();
|
||||||
await player1?.pause?.();
|
await player1?.pause?.();
|
||||||
navigate(Routes.FlowSelection);
|
navigate(Routes.FlowSelection);
|
||||||
@@ -157,7 +137,7 @@ const SongReady = () => {
|
|||||||
player1?.pause?.();
|
player1?.pause?.();
|
||||||
} catch {}
|
} catch {}
|
||||||
};
|
};
|
||||||
}, [player0, player1])
|
}, [player0, player1]),
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -241,7 +221,7 @@ const SongReady = () => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(
|
console.log(
|
||||||
"SongReady pause on seek start",
|
"SongReady pause on seek start",
|
||||||
e?.message
|
e?.message,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|||||||
Reference in New Issue
Block a user