clear last tickets
This commit is contained in:
+101
-86
@@ -7,6 +7,7 @@ exports.generatePicturePrompt = (project = {}) => {
|
||||
artistName: artistNameRaw = "",
|
||||
} = project || {};
|
||||
|
||||
// --- 1. Nettoyage et Normalisation ---
|
||||
const sanitizeInline = (value = "") => {
|
||||
if (typeof value !== "string") return "";
|
||||
return value
|
||||
@@ -14,13 +15,6 @@ exports.generatePicturePrompt = (project = {}) => {
|
||||
.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 =
|
||||
@@ -30,93 +24,114 @@ exports.generatePicturePrompt = (project = {}) => {
|
||||
const {
|
||||
genres = [],
|
||||
tempo = "",
|
||||
voice = "",
|
||||
instruments = [],
|
||||
mood = "",
|
||||
instruments = [],
|
||||
} = musicConfig || {};
|
||||
const userStyle = sanitizeInline(coverStyleRaw);
|
||||
|
||||
// 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);
|
||||
// --- 2. Intelligence Visuelle (Mapping) ---
|
||||
|
||||
// É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(" | ");
|
||||
// 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),
|
||||
);
|
||||
|
||||
// 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";
|
||||
// Construction de la Palette & Lumière
|
||||
let visualAtmosphere = "";
|
||||
if (isDark) {
|
||||
visualAtmosphere =
|
||||
"Atmosphere: Cinematic dark lighting, moody shadows, deep contrast. Palette: Midnight blue, obsidian, deep purple, metallic accents.";
|
||||
} else if (isEnergetic) {
|
||||
visualAtmosphere =
|
||||
"Atmosphere: Dynamic lighting, high energy, vibrant saturation. Palette: Neon colors, electric blue, magenta, bright yellow, high contrast.";
|
||||
} else {
|
||||
visualAtmosphere =
|
||||
"Atmosphere: Soft natural lighting, harmonious and ethereal. Palette: Pastel tones, warm gold, soft coral, balanced and elegant colors.";
|
||||
}
|
||||
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>
|
||||
// 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";
|
||||
|
||||
<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>
|
||||
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.";
|
||||
}
|
||||
|
||||
<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();
|
||||
// --- 3. Extraction de l'Inspiration (Lyrics) ---
|
||||
|
||||
return prompt;
|
||||
// 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 artistic 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, artistic 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");
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user