138 lines
4.8 KiB
JavaScript
138 lines
4.8 KiB
JavaScript
exports.generatePicturePrompt = (project = {}) => {
|
|
const {
|
|
title = "",
|
|
lyrics: lyricsRaw,
|
|
musicConfig = {},
|
|
coverStyle: coverStyleRaw = "",
|
|
artistName: artistNameRaw = "",
|
|
} = project || {};
|
|
|
|
// --- 1. Nettoyage et Normalisation ---
|
|
const sanitizeInline = (value = "") => {
|
|
if (typeof value !== "string") return "";
|
|
return value
|
|
.replace(/[\r\n]+/g, " ")
|
|
.replace(/[<>]/g, "")
|
|
.trim();
|
|
};
|
|
|
|
const titleForPrompt = sanitizeInline(title) || "Sans titre";
|
|
const artistName =
|
|
sanitizeInline(artistNameRaw) || sanitizeInline(project?.userName || "");
|
|
const hasArtistName = artistName.length > 0;
|
|
|
|
const {
|
|
genres = [],
|
|
tempo = "",
|
|
mood = "",
|
|
instruments = [],
|
|
} = musicConfig || {};
|
|
const userStyle = sanitizeInline(coverStyleRaw);
|
|
|
|
// --- 2. Intelligence Visuelle (Mapping) ---
|
|
|
|
// Détermination de l'énergie visuelle
|
|
const isEnergetic =
|
|
tempo &&
|
|
/\b(rapid|fast|vite|agité|upbeat|energ|dance|rock|metal)\b/i.test(
|
|
String(tempo),
|
|
);
|
|
const isDark =
|
|
mood &&
|
|
/\b(sombre|triste|dark|sad|mélancoli|nuit|night|eerie)\b/i.test(
|
|
String(mood),
|
|
);
|
|
|
|
// Construction de la Palette & Lumière
|
|
let visualAtmosphere = "";
|
|
if (isDark) {
|
|
visualAtmosphere =
|
|
"Atmosphere: Cinematic atmosphere, moody shadows, deep contrast. Palette: Midnight blue, obsidian, deep purple, metallic accents.";
|
|
} else if (isEnergetic) {
|
|
visualAtmosphere =
|
|
"Atmosphere: Dynamic atmosphere, high energy, vibrant saturation. Palette: Neon colors, electric blue, magenta, bright yellow, high contrast.";
|
|
} else {
|
|
visualAtmosphere =
|
|
"Atmosphere: Soft atmosphere, harmonious and ethereal. Palette: Pastel tones, warm gold, soft coral, balanced and elegant colors.";
|
|
}
|
|
|
|
// Définition du Style de Rendu (Si l'utilisateur est vague, on renforce)
|
|
let renderingStyle = userStyle
|
|
? `Art Style: ${userStyle}`
|
|
: "Art Style: Digital Art, Mixed Media";
|
|
|
|
if (
|
|
userStyle.toLowerCase().includes("realist") ||
|
|
userStyle.toLowerCase().includes("photo")
|
|
) {
|
|
renderingStyle +=
|
|
", 8k resolution, highly detailed texture, photorealistic, cinematic depth of field, raytracing.";
|
|
} else if (
|
|
userStyle.toLowerCase().includes("illu") ||
|
|
userStyle.toLowerCase().includes("dessin")
|
|
) {
|
|
renderingStyle +=
|
|
", vector art, clean lines, professional illustration, flat design or detailed painting.";
|
|
} else {
|
|
// Style par défaut "Album Cover" qui marche bien
|
|
renderingStyle +=
|
|
", abstract surrealism, conceptual album art, high fidelity, masterpiece.";
|
|
}
|
|
|
|
// --- 3. Extraction de l'Inspiration (Lyrics) ---
|
|
|
|
// On cherche le REFRAIN en priorité pour l'image, car c'est le cœur visuel
|
|
const sections = Array.isArray(lyricsRaw) ? lyricsRaw : [];
|
|
const chorus = sections.find(
|
|
(s) => s.type === "refrain" || s.type === "chorus",
|
|
);
|
|
const verse = sections.find(
|
|
(s) => s.type === "couplet" || s.type === "verse",
|
|
);
|
|
|
|
// On prend 2 lignes max du refrain, ou du premier couplet
|
|
const visualHook = (chorus?.lyrics || verse?.lyrics || "")
|
|
.split("\n")
|
|
.filter((l) => l.length > 10) // On évite les lignes trop courtes
|
|
.slice(0, 2)
|
|
.join(". ");
|
|
|
|
const imageryPrompt = visualHook
|
|
? `Visual Inspiration: An interpretation of these lyrics: "${visualHook}".`
|
|
: "Visual Inspiration: Abstract visual representation of the song's mood.";
|
|
|
|
// --- 4. Construction du Prompt Final (Structure Optimisée Imagen 3) ---
|
|
|
|
const promptParts = [
|
|
// Rôle
|
|
"Design a professional, high-quality music album cover.",
|
|
|
|
// 1. Le Texte (Crucial pour Imagen 3 - Doit être au début ou très clair)
|
|
`**Typography & Text:**`,
|
|
`The song title "${titleForPrompt}" must be the CENTERPIECE. Write it in a distinct font that matches the mood.`,
|
|
hasArtistName
|
|
? `The artist name "${artistName}" must appear smaller, elegant, and legible near the bottom or top.`
|
|
: "",
|
|
"Ensure perfect spelling. The text should be integrated into the artwork (e.g., metallic texture, neon glow, or bold cut-out), not just pasted on top.",
|
|
|
|
// 2. Le Visuel
|
|
`**Visuals:**`,
|
|
renderingStyle,
|
|
imageryPrompt,
|
|
`Subject: A central visual element that represents the song. ${isEnergetic ? "Dynamic composition." : "Balanced, centered composition."}`,
|
|
|
|
// 3. L'Atmosphère (Context from music config)
|
|
`**Mood & Color:**`,
|
|
visualAtmosphere,
|
|
genres.length > 0 ? `Musical Vibe Reference: ${genres.join(", ")}.` : "",
|
|
|
|
// 4. Contraintes Négatives (Phrasées positivement pour l'IA)
|
|
"**Constraints:**",
|
|
"Use a square 1:1 aspect ratio.",
|
|
"Do NOT depict literal musical instruments (like guitars or microphones) unless they are part of a surreal abstract composition.",
|
|
"No blurry text. No messy borders. No watermarks.",
|
|
];
|
|
|
|
return promptParts.filter(Boolean).join("\n\n");
|
|
};
|