Files
2026-02-24 10:54:19 +01:00

310 lines
12 KiB
JavaScript

const STYLE_PRESET_MAP = {
'Réaliste (style cinématographique)': {
style: 'Cinematic realism, film still, anamorphic lens, shallow depth of field',
palettes: [
'cool teal and amber highlights, muted neutrals',
'stormy blues with soft gold accents',
'charcoal, steel blue, and warm tungsten glows',
],
lighting: [
'dramatic key light with soft fill and deep shadows',
'low-key cinematic lighting with subtle haze',
],
textures: ['subtle film grain and crisp detail', 'matte finish with fine grain'],
typography: ['clean cinematic title card, subtle embossing'],
compositions: ['cinematic framing with a strong subject focus'],
},
'Artistique (style peinture moderne)': {
style: 'Modern painting, expressive brushstrokes, gallery-ready composition',
palettes: [
'rich pigments with bold contrast',
'muted pastels with a sharp accent color',
'deep jewel tones and warm highlights',
],
lighting: ['soft gallery lighting with gentle gradients'],
textures: ['visible canvas texture and layered paint', 'acrylic strokes with matte finish'],
typography: ['refined serif lettering integrated into the paint'],
compositions: ['balanced composition with painterly focal point'],
},
'Style Dessins': {
style: 'Hand-drawn illustration, clean linework, ink and colored pencil',
palettes: [
'warm paper tones with soft color fills',
'limited palette with bold accent color',
],
lighting: ['soft diffused light with minimal shadows'],
textures: ['paper texture with visible grain'],
typography: ['hand-lettered title with slight texture'],
compositions: ['centered illustration with clear silhouette'],
},
'Style rétros': {
style: 'Retro poster art, vintage print, halftone patterns, aged paper',
palettes: [
'sun-faded oranges, teal, and cream',
'muted reds, mustard, and dark green',
],
lighting: ['flat poster lighting with strong contrast'],
textures: ['distressed print texture and subtle scratches'],
typography: ['vintage serif lettering with screen print texture'],
compositions: ['poster-style layout with bold hierarchy'],
},
'Style cyberpunk': {
style: 'Cyberpunk aesthetic, futuristic city glow, reflective surfaces',
palettes: [
'neon magenta and electric blue on dark backgrounds',
'cyan, violet, and deep black with sharp highlights',
],
lighting: ['neon rim light and high-contrast highlights'],
textures: ['glossy surfaces with subtle rain speckles'],
typography: ['neon tube lettering embedded in the scene'],
compositions: ['dynamic perspective with strong depth'],
},
'Style Afro': {
style: 'Afrofuturist motifs, bold patterns, textile-inspired geometry',
palettes: [
'warm earth tones with vibrant accents',
'gold, terracotta, deep blue, and emerald',
],
lighting: ['warm directional light with rich contrast'],
textures: ['woven fabric texture and layered patterns'],
typography: ['bold lettering with subtle pattern fills'],
compositions: ['symmetrical layout with iconic central motif'],
},
'Style grunge': {
style: 'Grunge collage, distressed textures, gritty mixed media',
palettes: [
'smoky blacks with muted reds and greys',
'dirty neutrals with high-contrast highlights',
],
lighting: ['moody low-key lighting with harsh shadows'],
textures: ['torn paper, scratches, and distressed overlays'],
typography: ['distressed lettering with rough edges'],
compositions: ['layered collage with imperfect alignment'],
},
'Style pop art': {
style: 'Pop art illustration, bold outlines, Ben-Day dots, screen print',
palettes: [
'bold primary colors with black and white',
'bright yellow, red, and cyan with clean contrast',
],
lighting: ['flat graphic lighting with crisp separation'],
textures: ['screen print texture with halftone dots'],
typography: ['bold outlined letters with halftone texture'],
compositions: ['graphic layout with strong shapes and punchy hierarchy'],
},
}
const DEFAULT_STYLE_VARIANTS = [
'Conceptual album art, abstract surrealism, high fidelity',
'Graphic collage, mixed media, layered textures',
'Minimalist design, bold shapes, strong negative space',
'Dreamlike photography, cinematic framing, subtle grain',
'Geometric abstraction, clean gradients, modern design',
'Stylized illustration, crisp edges, bold composition',
]
const COMPOSITION_VARIANTS = [
'centered focal subject with balanced symmetry',
'asymmetrical layout with dynamic diagonal flow',
'minimal composition with strong negative space',
'collage-like layout with layered elements',
'close-up framing for intimacy and impact',
'wide cinematic framing with a distant focal point',
]
const LIGHTING_VARIANTS = [
'dramatic chiaroscuro with deep shadows',
'soft diffused light with gentle gradients',
'high-contrast lighting with sharp highlights',
'backlit silhouette with glowing edges',
'moody low-key lighting with selective highlights',
]
const PALETTE_VARIANTS = [
'deep navy with warm amber accents',
'muted earth tones with copper highlights',
'cool blues with silver accents',
'bold primary colors with black and white',
'monochrome with a single accent color',
'pastel gradients with a sharp contrasting highlight',
'electric blue and magenta on dark tones',
]
const TEXTURE_VARIANTS = [
'subtle film grain with a matte finish',
'canvas texture with visible brushwork',
'paper cutout layers with rough edges',
'glossy highlights with clean edges',
'distressed print texture with scratches',
]
const TYPOGRAPHY_VARIANTS = [
'embossed metallic lettering integrated into the art',
'minimalist sans-serif with generous tracking',
'hand-drawn lettering with slight texture',
'neon-style lettering embedded in the scene',
'classic serif with elegant spacing',
]
const SUBJECT_VARIANTS = [
'a single strong symbol or scene that represents the song',
'an abstract motif with a clear focal point',
'a cinematic moment captured mid-action',
'a minimalist icon or silhouette with strong presence',
'a surreal object or landscape that feels iconic',
]
const ATMOSPHERE_DARK = [
'cinematic and moody with deep contrast',
'noir ambience with subtle haze and high contrast',
'tense and dramatic with low-key depth',
]
const ATMOSPHERE_ENERGETIC = [
'dynamic and high energy with punchy contrast',
'electric and fast-paced with vivid saturation',
'bold and kinetic with sharp highlights',
]
const ATMOSPHERE_SOFT = [
'calm and spacious with balanced mood',
'introspective and gentle with airy depth',
'dreamlike and serene with soft transitions',
]
const hashSeed = (value) => {
const str = String(value || '')
let hash = 0
for (let i = 0; i < str.length; i += 1) {
hash = (hash * 31 + str.charCodeAt(i)) >>> 0
}
return hash
}
const pickVariant = (list, seed, salt = 0) => {
if (!Array.isArray(list) || list.length === 0) return ''
const idx = (seed + salt) % list.length
return list[idx]
}
exports.generatePicturePrompt = (project = {}) => {
const {
title = '',
lyrics: lyricsRaw,
musicConfig = {},
coverStyle: coverStyleRaw = '',
artistName: artistNameRaw = '',
variantSeed = '',
} = 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 = '' } = musicConfig || {}
const userStyle = sanitizeInline(coverStyleRaw)
const seedInput = [titleForPrompt, artistName, userStyle, String(variantSeed || '')].join('|')
const baseSeed = hashSeed(seedInput)
const seed = baseSeed || Math.floor(Math.random() * 1_000_000_000)
// --- 2. Intelligence Visuelle (Mapping & Variations) ---
const preset = STYLE_PRESET_MAP[userStyle] || null
const styleDescriptor = preset?.style || userStyle || pickVariant(DEFAULT_STYLE_VARIANTS, seed, 7)
const renderingStyle = `Art Style: ${styleDescriptor}.`
const compositionChoice =
preset?.compositions?.length > 0
? pickVariant(preset.compositions, seed, 1)
: pickVariant(COMPOSITION_VARIANTS, seed, 1)
const lightingChoice =
preset?.lighting?.length > 0
? pickVariant(preset.lighting, seed, 2)
: pickVariant(LIGHTING_VARIANTS, seed, 2)
const paletteChoice =
preset?.palettes?.length > 0
? pickVariant(preset.palettes, seed, 3)
: pickVariant(PALETTE_VARIANTS, seed, 3)
const textureChoice =
preset?.textures?.length > 0
? pickVariant(preset.textures, seed, 4)
: pickVariant(TEXTURE_VARIANTS, seed, 4)
const typographyChoice =
preset?.typography?.length > 0
? pickVariant(preset.typography, seed, 5)
: pickVariant(TYPOGRAPHY_VARIANTS, seed, 5)
// 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))
const atmosphereChoice = isDark
? pickVariant(ATMOSPHERE_DARK, seed, 6)
: isEnergetic
? pickVariant(ATMOSPHERE_ENERGETIC, seed, 6)
: pickVariant(ATMOSPHERE_SOFT, seed, 6)
// --- 3. Extraction de l'Inspiration (Lyrics) ---
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')
const visualHook = (chorus?.lyrics || verse?.lyrics || '')
.split('\n')
.map((line) => line.trim())
.filter((line) => line.length > 10)
.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."
const subjectChoice = pickVariant(SUBJECT_VARIANTS, seed, 8)
// --- 4. Construction du Prompt Final (Structure Optimisée Imagen 3) ---
const promptParts = [
'Design a professional, high-quality music album cover.',
'**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.`
: '',
`Typography treatment: ${typographyChoice}.`,
'Ensure perfect spelling. The text should be integrated into the artwork (e.g., embossed texture, neon tube, or bold cut-out), not just pasted on top.',
'**Visual Direction:**',
renderingStyle,
`Composition: ${compositionChoice}.`,
`Surface & Texture: ${textureChoice}.`,
`Subject: ${subjectChoice}.`,
imageryPrompt,
'**Mood & Color:**',
`Atmosphere: ${atmosphereChoice}.`,
`Palette: ${paletteChoice}.`,
`Lighting: ${lightingChoice}.`,
genres.length > 0 ? `Musical Vibe Reference: ${genres.join(', ')}.` : '',
'**Constraints:**',
'Use a square 1:1 aspect ratio.',
'No watermarks or logos.',
'No blurry text or messy borders.',
'Avoid generic stock imagery and clichéd instruments unless essential to the concept.',
]
return promptParts.filter(Boolean).join('\n\n')
}