123 lines
5.1 KiB
JavaScript
123 lines
5.1 KiB
JavaScript
exports.generatePicturePrompt = (project = {}) => {
|
|
const {
|
|
title = "",
|
|
lyrics: lyricsRaw,
|
|
musicConfig = {},
|
|
coverStyle: coverStyleRaw = "",
|
|
artistName: artistNameRaw = "",
|
|
} = project || {};
|
|
|
|
const sanitizeInline = (value = "") => {
|
|
if (typeof value !== "string") return "";
|
|
return value
|
|
.replace(/[\r\n]+/g, " ")
|
|
.replace(/[<>]/g, "")
|
|
.trim();
|
|
};
|
|
const normalizeForMatching = (value = "") => {
|
|
if (typeof value !== "string") return "";
|
|
return value
|
|
.normalize("NFD")
|
|
.replace(/[\u0300-\u036f]/g, "")
|
|
.toLowerCase();
|
|
};
|
|
|
|
const titleForPrompt = sanitizeInline(title) || "Sans titre";
|
|
const artistName =
|
|
sanitizeInline(artistNameRaw) || sanitizeInline(project?.userName || "");
|
|
const hasArtistName = artistName.length > 0;
|
|
|
|
const {
|
|
genres = [],
|
|
tempo = "",
|
|
voice = "",
|
|
instruments = [],
|
|
mood = "",
|
|
} = musicConfig || {};
|
|
|
|
// Normalise les paroles en tableau de sections { type, lyrics }
|
|
const lyricsSections = Array.isArray(lyricsRaw)
|
|
? lyricsRaw
|
|
: [
|
|
lyricsRaw?.couplet
|
|
? { type: "couplet", lyrics: lyricsRaw.couplet }
|
|
: null,
|
|
lyricsRaw?.refrain
|
|
? { type: "refrain", lyrics: lyricsRaw.refrain }
|
|
: null,
|
|
].filter(Boolean);
|
|
|
|
// Échantillon pour l'ambiance (courtes bribes pour inspirer la direction créative)
|
|
const lyricsSample = (lyricsSections || [])
|
|
.map((s) => (s?.lyrics || "").split("\n").slice(0, 2).join(" "))
|
|
.filter(Boolean)
|
|
.slice(0, 6)
|
|
.join(" | ");
|
|
|
|
// Etiquettes d'ambiance
|
|
const tags = [];
|
|
if (Array.isArray(genres) && genres.length)
|
|
tags.push(`Genres: ${genres.join(", ")}`);
|
|
if (tempo) tags.push(`Tempo: ${tempo}`);
|
|
if (Array.isArray(instruments) && instruments.length)
|
|
tags.push(`Instruments: ${instruments.join(", ")}`);
|
|
if (voice) tags.push(`Vocal: ${voice}`);
|
|
if (mood) tags.push(`Mood: ${mood}`);
|
|
|
|
// Guidance palette selon tempo (simple heuristique)
|
|
const paletteHint =
|
|
tempo && /\b(rapid|fast|vite|agité|upbeat|energ)/i.test(String(tempo))
|
|
? "Palette vive et contrastée (magenta, cyan, jaune, bleu électrique)"
|
|
: "Palette harmonieuse et douce (bleu nuit, violet, corail, or pâle)";
|
|
|
|
const coverStyleInput = String(coverStyleRaw || "").trim();
|
|
const coverStyle = sanitizeInline(coverStyleInput);
|
|
const normalizedStyle = normalizeForMatching(coverStyle);
|
|
let coverStyleGuidance = coverStyle;
|
|
if (normalizedStyle.includes("realist")) {
|
|
coverStyleGuidance =
|
|
"Style réaliste photographique hyper détaillé, textures fidèles, lumière naturelle, profondeur de champ crédible, aucun effet cartoon, fantastique ou peint";
|
|
}
|
|
const hasTags = tags.length > 0;
|
|
const tagsLine = hasTags
|
|
? `${tags.join(
|
|
" ; ",
|
|
)}. Utilise ces indications pour guider uniquement la palette, l'énergie et l'émotion, sans représenter littéralement les instruments, objets ou mots cités.`
|
|
: "Ambiance sonore non précisée : crée une atmosphère abstraite sans instrument ni objet musical apparent.";
|
|
const lyricsLine = lyricsSample || "Pas d'extraits fournis";
|
|
const styleLine = coverStyleGuidance
|
|
? `${coverStyleGuidance}. ${paletteHint}`
|
|
: `${paletteHint}. Style libre, artistique et lumineux.`;
|
|
const typographyLine = hasArtistName
|
|
? `Reproduis strictement le titre de la chanson ${titleForPrompt} sans modification, sans traduction et sans ajout ou suppression de caractères. Ajoute également le nom de l'artiste ${artistName} de manière artistique, parfaitement lisible et hiérarchisée, en harmonie avec le style abstrait et lumineux de la pochette.`
|
|
: `Reproduis strictement le titre de la chanson ${titleForPrompt} sans modification, sans traduction et sans ajout ou suppression de caractères. Intègre-le de manière artistique et parfaitement lisible, en harmonie avec le style abstrait et lumineux de la pochette.`;
|
|
const artistLine = hasArtistName
|
|
? ` <ARTISTE>${artistName}</ARTISTE>\n`
|
|
: "";
|
|
|
|
const prompt = `<BRIEF>
|
|
<OBJECTIF>Créer une pochette d'album en adéquation avec les paroles de la musique et qui respecte le style ${coverStyle}</OBJECTIF>
|
|
<TITRE>${titleForPrompt}</TITRE>
|
|
${artistLine} <STYLE_MUSICAL>${tagsLine}</STYLE_MUSICAL>
|
|
<EXTRAITS_PAROLES>${lyricsLine}</EXTRAITS_PAROLES>
|
|
</BRIEF>
|
|
|
|
<CONTEXTES_VISUELS>
|
|
<FORMAT>Image carrée 1024x1024 pixels, résolution haute.</FORMAT>
|
|
<STYLE>${styleLine}</STYLE>
|
|
<COMPOSITION>La composition doit être dynamique et remplir toute la surface, sans laisser de bordures ni de zones vides.</COMPOSITION>
|
|
<TYPOGRAPHIE>${typographyLine}</TYPOGRAPHIE>
|
|
<ORTHOGRAPHE>Aucune faute d'orthographe n'est tolérée. Respecte la casse et l'orthographe exactes du titre fourni.</ORTHOGRAPHE>
|
|
</CONTEXTES_VISUELS>
|
|
|
|
<CONTRAINTES>
|
|
<INTERDIT>Personnes, visages ou silhouettes reconnaissables.</INTERDIT>
|
|
<INTERDIT>Fonds blancs ou bordures délimitant l'image.</INTERDIT>
|
|
<INTERDIT>Texte illisible ou trop petit.</INTERDIT>
|
|
<INTERDIT>Représenter des instruments ou objets évoqués dans STYLE_MUSICAL : ces informations servent uniquement au contexte audio.</INTERDIT>
|
|
</CONTRAINTES>
|
|
`.trim();
|
|
|
|
return prompt;
|
|
};
|