fix prompt and thumbnail path

This commit is contained in:
Thomas Demirdjian
2025-10-01 15:48:23 +02:00
parent 0bc186413b
commit 66b41e8b73
3 changed files with 109 additions and 83 deletions
+49 -30
View File
@@ -24,40 +24,59 @@ exports.generateLyrics = onCall({}, async ({ auth = {}, data = {} }) => {
console.log("Function generate lyrics start with data", data); console.log("Function generate lyrics start with data", data);
const system = const system = `
`
Tu es un parolier expert en chanson française moderne et populaire. Tu es un parolier expert en chanson française moderne et populaire.
Tu écris des chansons marquantes, accessibles, sur des thématiques ` + Tu écris des chansons marquantes, accessibles, sur des thématiques d'innovation, d'esprit d'équipe et de créativité digitale.
`d'innovation, d'esprit d'équipe et de créativité digitale. Ton écriture doit toucher à la fois l'humain, le collectif et la passion d'entreprendre.
Ton ` + Analyse attentivement les informations placées entre balises XML-like.
`écriture doit toucher à la fois l'humain, le collectif et la ` + Génère un titre accrocheur et mémorable en plus des paroles, tout en respectant strictement la structure demandée.
`passion d'entreprendre. Retourne uniquement un JSON conforme au schéma fourni, sans ajouter d'autres textes.
Tu dois générer un titre accrocheur et ` +
`mémorable pour la chanson en plus des paroles.
Respecte ` +
`précisément la structure demandée et fournis le résultat dans un ` +
`format JSON clair, respectant strictement le schéma demandé.
`.trim(); `.trim();
// Construction du prompt utilisateur const structureTags = (Array.isArray(structure) ? structure : [])
const prompt = .map((part, index) => {
` const content = part || "";
Objectif : ${objective} return ` <SECTION ordre="${index + 1}">${content}</SECTION>`;
Contexte : ${context} })
Émotion : ${emotion} .join("\n");
Style musical : ${style}
Audience cible : ${audience}
Structure de la chanson : ${structure.join(" => ")}
Schéma de rimes : ${rhymes}
Génère un titre accrocheur et les paroles de la chanson en suivant ` + const fallbackStructureTags = [
`strictement cette structure. ' <SECTION ordre="1">couplet</SECTION>',
Le titre doit être court, ` + ' <SECTION ordre="2">refrain</SECTION>',
`mémorable et refléter l'esprit de la musique. ].join("\n");
Pour chaque ` +
`partie ("couplet" ou "refrain"), écris 4 à 16 lignes adaptées, ` + // Construction du prompt utilisateur avec des balises explicites
`expressives et cohérentes avec le style et l'émotion souhaitée. const prompt = `
`.trim(); <BRIEF>
<OBJECTIF>${objective || ""}</OBJECTIF>
<CONTEXTE>${context || ""}</CONTEXTE>
<EMOTION>${emotion || ""}</EMOTION>
<STYLE_MUSICAL>${style || ""}</STYLE_MUSICAL>
<AUDIENCE>${audience || ""}</AUDIENCE>
<SCHEMA_DE_RIMES>${rhymes || ""}</SCHEMA_DE_RIMES>
</BRIEF>
<STRUCTURE_DEMANDEE>
${structureTags || fallbackStructureTags}
</STRUCTURE_DEMANDEE>
<CONTRAINTES>
<TITRE>
Génère un titre court, mémorable et aligné avec l'esprit de la musique.
</TITRE>
<PAROLES>
Pour chaque section demandée, rédige entre 4 et 16 lignes expressives.
Respecte strictement l'ordre et le type de chaque section (couplet, refrain, etc.).
</PAROLES>
<STYLE>
Adapte le vocabulaire et le ton aux informations fournies dans le brief et la structure.
</STYLE>
<FORMAT>
Retourne uniquement un objet JSON valide selon le schéma.
Utilise les retours à la ligne (\n) pour séparer les phrases afin de conserver le découpage.
</FORMAT>
</CONTRAINTES>
`.trim();
// Définition du schéma zod/genkit pour la validation du retour // Définition du schéma zod/genkit pour la validation du retour
const lyricsSchema = z.object({ const lyricsSchema = z.object({
+3 -1
View File
@@ -29,7 +29,9 @@ exports.generateVideoThumbnail = onObjectFinalized(
if (/_thumb9x16\.jpg$/i.test(objectName)) return; if (/_thumb9x16\.jpg$/i.test(objectName)) return;
const bucket = admin.storage().bucket(bucketName); const bucket = admin.storage().bucket(bucketName);
const playbackMatch = objectName.match(/^musics\/([^/]+)\/playback\.mp4$/i); const playbackMatch = objectName.match(
/^users\/[^/]+\/projects\/([^/]+)\/playback\.mp4$/i,
);
const projectId = playbackMatch ? playbackMatch[1] : null; const projectId = playbackMatch ? playbackMatch[1] : null;
// Use unique folder under /tmp to avoid name collisions // Use unique folder under /tmp to avoid name collisions
+57 -52
View File
@@ -1,5 +1,5 @@
import { BlurView } from 'expo-blur'; import { BlurView } from "expo-blur";
import React from 'react'; import React from "react";
import { import {
Image, Image,
Platform, Platform,
@@ -7,38 +7,39 @@ import {
StyleSheet, StyleSheet,
Text, Text,
View, View,
} from 'react-native'; } from "react-native";
import useMinuit from 'react-native-minuit/src/hooks/useMinuit.js'; import useMinuit from "react-native-minuit/src/hooks/useMinuit.js";
import { responsiveHeight } from 'react-native-responsive-dimensions'; import { responsiveHeight } from "react-native-responsive-dimensions";
import { background, img } from '../../assets'; import { background, img } from "../../assets";
import MusicLandHeader from '../../components/MusicLandHeader'; import MusicLandHeader from "../../components/MusicLandHeader";
import { projectsRef, serverTimestamp } from '../../config/firebase'; import { projectsRef, serverTimestamp } from "../../config/firebase";
import { uploadFileToFirebase } from '../../helpers/uploadToFirebase'; import { uploadFileToFirebase } from "../../helpers/uploadToFirebase";
import Page from '../../layouts/Page'; import Page from "../../layouts/Page";
import { Routes } from '../../navigation'; import { Routes } from "../../navigation";
import { goBack, navigate } from '../../navigation/NavigationService'; import { goBack, navigate } from "../../navigation/NavigationService";
import { Palette } from '../../styles'; import { Palette } from "../../styles";
import { FONT_FAMILY } from '../../styles/Fonts'; import { FONT_FAMILY } from "../../styles/Fonts";
import { size } from "../../styles/Style";
import CreateLyricsHeader from "../Writing/components/CreateLyricsHeader";
import { useUserData } from "../../providers/UserDataProvider";
import { size } from '../../styles/Style';
import CreateLyricsHeader from '../Writing/components/CreateLyricsHeader';
const DownloadSongs = ({ route }) => { const DownloadSongs = ({ route }) => {
// const params = useRoute().params; const { currentUID } = useUserData();
const { action, uri, project } = route.params || {}; const { action, uri, project } = route.params || {};
console.log('project id', project?.id); console.log("project id", project?.id);
const { setIsLoading, setTooltip } = useMinuit(); const { setIsLoading, setTooltip } = useMinuit();
const handleDownloadUri = async () => { const handleDownloadUri = async () => {
if (action === 'playback' && project?.id) { if (action === "playback" && project?.id) {
// Publication du playback // Publication du playback
console.log('project id : ', project.id); console.log("project id : ", project.id);
try { try {
setIsLoading(true); setIsLoading(true);
const { resultURI = null } = await uploadFileToFirebase({ const { resultURI = null } = await uploadFileToFirebase({
uri: uri, uri: uri,
path: `musics/${project.id}/source.mp4`, path: `users/${currentUID}/projects/${project.id}/playback.mp4`,
shouldCompress: true, shouldCompress: true,
fileType: 'VIDEO', fileType: "VIDEO",
}); });
if (!resultURI) throw new Error("Téléversement de l'image impossible"); if (!resultURI) throw new Error("Téléversement de l'image impossible");
@@ -49,23 +50,23 @@ const DownloadSongs = ({ route }) => {
playbackUrl: resultURI, playbackUrl: resultURI,
updatedAt: serverTimestamp(), updatedAt: serverTimestamp(),
}, },
{ merge: true } { merge: true },
); );
setTooltip({ setTooltip({
type: 'success', type: "success",
text: 'Vidéo uploadée, conversion HLS en cours…', text: "Vidéo uploadée, conversion HLS en cours…",
}); });
} else { } else {
setTooltip({ setTooltip({
type: 'error', type: "error",
text: 'Erreur lors de la publication du playback', text: "Erreur lors de la publication du playback",
}); });
} }
} catch (error) { } catch (error) {
console.log('error upload playback', error); console.log("error upload playback", error);
setTooltip({ setTooltip({
type: 'error', type: "error",
text: 'Erreur lors de la publication du playback', text: "Erreur lors de la publication du playback",
}); });
} finally { } finally {
setIsLoading(false); setIsLoading(false);
@@ -78,37 +79,39 @@ const DownloadSongs = ({ route }) => {
return ( return (
<Page <Page
backgroundImg={ backgroundImg={
action === 'playback' action === "playback"
? background.playbackBG2 ? background.playbackBG2
: background.productionBG2 : background.productionBG2
} }
headerType='NONE'> headerType="NONE"
>
<MusicLandHeader onPressBack={goBack} progress={50} /> <MusicLandHeader onPressBack={goBack} progress={50} />
<View style={{ flex: 1, paddingTop: responsiveHeight(15) }}> <View style={{ flex: 1, paddingTop: responsiveHeight(15) }}>
<CreateLyricsHeader <CreateLyricsHeader
gradientProps={{ gradientProps={{
start: { x: 0, y: 0 }, start: { x: 0, y: 0 },
end: { x: 0, y: 1 }, end: { x: 0, y: 1 },
}}> }}
>
<Image <Image
source={img.placeholder2} source={img.placeholder2}
style={{ style={{
...size({ size: 158 }), ...size({ size: 158 }),
borderRadius: 13, borderRadius: 13,
alignSelf: 'center', alignSelf: "center",
}} }}
/> />
<View style={{ gap: 12, paddingVertical: 12 }}> <View style={{ gap: 12, paddingVertical: 12 }}>
<Text style={styles.title}> <Text style={styles.title}>
Prêt à télécharger{' '} Prêt à télécharger{" "}
{action === 'playback' ? 'ton Playback' : 'ta chanson'} ? {action === "playback" ? "ton Playback" : "ta chanson"} ?
</Text> </Text>
<View style={{ gap: 10 }}> <View style={{ gap: 10 }}>
<Pressable <Pressable
style={styles.itemContainer} style={styles.itemContainer}
onPress={ onPress={
() => { () => {
console.log('test'); console.log("test");
handleDownloadUri(); handleDownloadUri();
} }
@@ -116,18 +119,19 @@ const DownloadSongs = ({ route }) => {
// action, // action,
// uri, // uri,
// }) // })
}> }
>
<BlurView <BlurView
style={styles.blurView} style={styles.blurView}
intensity={Platform.OS !== 'ios' ? 10 : 40} intensity={Platform.OS !== "ios" ? 10 : 40}
tint='dark' tint="dark"
// experimentalBlurMethod={ // experimentalBlurMethod={
// Platform.OS !== "ios" ? "dimezisBlurView" : "none" // Platform.OS !== "ios" ? "dimezisBlurView" : "none"
// } // }
> >
<Text style={styles.label}> <Text style={styles.label}>
Télécharger{' '} Télécharger{" "}
{action === 'playback' ? 'mon Playback' : 'ma chanson'} {action === "playback" ? "mon Playback" : "ma chanson"}
</Text> </Text>
<Text style={styles.description}>Pour moi uniquement</Text> <Text style={styles.description}>Pour moi uniquement</Text>
</BlurView> </BlurView>
@@ -138,25 +142,26 @@ const DownloadSongs = ({ route }) => {
navigate(Routes.StreamSong, { navigate(Routes.StreamSong, {
action, action,
}) })
}> }
>
<BlurView <BlurView
style={styles.blurView} style={styles.blurView}
intensity={Platform.OS !== 'ios' ? 10 : 40} intensity={Platform.OS !== "ios" ? 10 : 40}
tint='dark' tint="dark"
// experimentalBlurMethod={ // experimentalBlurMethod={
// Platform.OS !== "ios" ? "dimezisBlurView" : "none" // Platform.OS !== "ios" ? "dimezisBlurView" : "none"
// } // }
> >
<Text style={styles.label}> <Text style={styles.label}>
Diffuser{' '} Diffuser{" "}
{action === 'playback' ? 'mon Playback' : 'ma chanson'} sur {action === "playback" ? "mon Playback" : "ma chanson"} sur
la plateforme de MusicLand (+ réseaux sociaux) et participer la plateforme de MusicLand (+ réseaux sociaux) et participer
au concours au concours
</Text> </Text>
<Text style={styles.description}> <Text style={styles.description}>
Top 3: 1er 15% du CA ML - 2ème 10% du CA ML - 3ème 5% du CA Top 3: 1er 15% du CA ML - 2ème 10% du CA ML - 3ème 5% du CA
ML (catégorie{' '} ML (catégorie{" "}
{action === 'playback' ? 'Playback' : 'chanson'}) {action === "playback" ? "Playback" : "chanson"})
</Text> </Text>
</BlurView> </BlurView>
</Pressable> </Pressable>
@@ -193,9 +198,9 @@ const styles = StyleSheet.create({
}, },
itemContainer: { itemContainer: {
borderRadius: 12, borderRadius: 12,
overflow: 'hidden', overflow: "hidden",
backgroundColor: '#FFFFFF03', backgroundColor: "#FFFFFF03",
shadowColor: '#0000001A', shadowColor: "#0000001A",
shadowOffset: { shadowOffset: {
width: 0, width: 0,
height: 2, height: 2,