fix: post images diversity — SUBJECT+SETTING+LIGHTING prompt

Та же проблема что и у обложек статей:
  старый промт 'Topic essence: первые 250 символов' был слишком абстрактным
  → модель рисовала свой дефолт (тёмный фон + геометрия)

Новый промт (3 некофликтующих параметра):
  VISUAL CONCEPT: конкретный предмет из getPostVisualConcept()
  SETTING: физический антураж (8 вариантов по seed из текста поста)
  LIGHTING + COLOR TEMPERATURE: конкретный свет

getPostVisualConcept(): 8 тематических категорий × 4-6 концептов
  AI, automation, cybersec, code, marketing, money, education, health
  + 12 универсальных концептов
Seed = hash первых 80 символов поста → детерминировано но уникально
This commit is contained in:
Ник (Claude)
2026-06-14 15:13:20 +03:00
parent c40ef90ad1
commit 31b31b75b8
+145 -7
View File
@@ -75,15 +75,36 @@ async function generatePostImage({ post, channel, style = {} }) {
// Извлекаем суть поста для промпта (первые 250 символов)
const postExcerpt = post.replace(/[#*_`>]/g, '').slice(0, 250);
const prompt = `Editorial illustration for a social media post. Topic essence: "${postExcerpt}"
// Визуальная метафора — конкретный предмет/сцена на основе темы
const visualConcept = getPostVisualConcept(post, channel);
Style: ${imageStyle.prompt}.
${palette ? `Color palette: ${palette}.` : ''}
Channel context: ${channel.niche || channel.name}.
${style.image_prompt_instructions ? `\nChannel visual guidelines: ${style.image_prompt_instructions}` : ''}
// Антураж + свет — меняется по хешу поста (детерминированно, но разнообразно)
let seed = 0;
for (let i = 0; i < post.length && i < 100; i++) seed = (seed * 31 + post.charCodeAt(i)) >>> 0;
Composition: 16:9 wide format, balanced, suitable for social media.
Strictly: no text, no letters, no logos, no faces of real people.`;
const SCENES = [
{ setting: 'warm oak desktop surface, afternoon sunlight from left window', lighting: 'golden hour soft shadows', temp: 'warm amber' },
{ setting: 'white marble surface, clean studio', lighting: 'flat professional studio', temp: 'cool whites' },
{ setting: 'dark slate table, single focused overhead spotlight', lighting: 'dramatic single point', temp: 'high contrast' },
{ setting: 'weathered wooden workbench, overcast daylight', lighting: 'soft even overcast', temp: 'muted naturals' },
{ setting: 'black velvet surface, rim lighting from behind', lighting: 'rim lit glowing edges', temp: 'rich blacks gold' },
{ setting: 'glass surface over city lights at night', lighting: 'city glow from below', temp: 'multicolor bokeh' },
{ setting: 'antique library floor, surrounded by books, candlelight', lighting: 'warm candlelight side', temp: 'amber parchment' },
{ setting: 'frosted glass, winter morning, ice crystals at edges', lighting: 'diffused winter morning', temp: 'icy blues whites' },
];
const scene = SCENES[seed % SCENES.length];
const prompt = `Generate a 16:9 editorial illustration for a social media post.
VISUAL CONCEPT: ${visualConcept}
SETTING: ${scene.setting}
LIGHTING: ${scene.lighting}
COLOR TEMPERATURE: ${scene.temp}
${style.image_custom_colors ? `BRAND PALETTE: ${style.image_custom_colors}` : (palette ? `PALETTE: ${palette}` : '')}
STYLE: ${imageStyle.prompt}
${style.image_prompt_instructions ? `CHANNEL STYLE: ${style.image_prompt_instructions}` : ''}
RULES: no text, no letters, no logos, no real human faces.`;
// Единственный провайдер: routerai /responses + gpt-5-image-mini
// Цена: ~₽2.72/картинка. quality параметр не работает, всегда high.
@@ -142,3 +163,120 @@ Strictly: no text, no letters, no logos, no faces of real people.`;
}
module.exports = { generatePostImage, IMAGE_STYLES, IMAGE_PALETTES };
/**
* Извлекает визуальный концепт из текста поста.
* Конкретные, материальные образы — не абстрактные.
*/
function getPostVisualConcept(post, channel) {
const t = post.toLowerCase();
const niche = (channel?.niche || '').toLowerCase();
const combined = t + ' ' + niche;
// Хеш для выбора внутри категории
let seed = 0;
for (let i = 0; i < post.length && i < 80; i++) seed = (seed * 31 + post.charCodeAt(i)) >>> 0;
function pick(arr) { return arr[seed % arr.length]; }
const patterns = [
{
kw: ['ии', ' ai ', 'нейро', 'llm', 'gpt', 'claude', 'chatgpt', 'искусственн', 'neural'],
concepts: [
'A vintage typewriter with keys pressing by invisible force, paper emerging with glowing text',
'An old brass compass spinning and settling on a new direction, surrounded by scattered maps',
'A seed germinating in dark soil, roots and shoots emerging simultaneously, close-up macro',
'A master key held up to warm light, intricate cuts visible, golden bokeh background',
'A book opening by itself, pages turning rapidly, text rearranging mid-air in warm library',
'An optical prism splitting white light into full spectrum, mounted on dark velvet surface',
],
},
{
kw: ['автомат', 'бот', 'automat', 'workflow', 'n8n', 'zapier', 'make', 'скрипт'],
concepts: [
'Vintage clockwork mechanism — interlocking brass gears in motion, macro close-up, amber light',
'A domino chain in the moment of falling, each piece a different color, motion blur',
'Factory assembly line condensed to a tabletop, small objects moving through stages, long exposure',
'A rube goldberg sequence frozen mid-action, multiple contraptions in motion',
'Time-lapse of a city intersection at night, light trails forming perfect flow patterns',
],
},
{
kw: ['взлом', 'хакер', 'безопасн', 'фишинг', 'вирус', 'cyber', 'hack', 'secur'],
concepts: [
'A vintage combination lock under dramatic side lighting, tumblers visible, dark background',
'A glass door with a hairline crack spreading, red emergency light leaking through fracture',
'An old steel safe door hanging slightly open, papers spilling out, harsh spotlight',
'A chain with one shattered link, chrome and steel, dramatic spotlight on break point',
],
},
{
kw: ['код', 'разработ', 'програм', 'code', 'develop', 'software', 'api', 'github'],
concepts: [
'A craftsman workbench covered in precision tools, each perfectly placed, workshop window light',
'An architect drafting table with blueprints unrolled, compass and ruler in use, desk lamp',
'Knitting needles mid-row on a complex pattern, wool threads crossing precisely, natural light',
'A mason building a wall one brick at a time, each brick different texture, golden hour',
],
},
{
kw: ['маркетинг', 'реклам', 'продвиж', 'seo', 'контент', 'growth', 'аудитор'],
concepts: [
'A megaphone lying on a table, vintage brass, city map spread underneath it',
'Seeds being planted in geometric rows, birds-eye view, garden tools aside, spring light',
'A lighthouse beam sweeping over foggy harbor, ships turning toward the light',
'A vendor market stall being set up attractively, colorful awning, morning light',
],
},
{
kw: ['деньг', 'финанс', 'инвест', 'бизнес', 'прибыл', 'доход', 'money', 'business'],
concepts: [
'A vintage scale perfectly balanced with different objects on each side, warm studio light',
'Stack of different vintage coins photographed from above, macro, warm lighting',
'A piggy bank on a wooden surface with a single coin mid-air above it, soft focus',
'Growing seedlings in small pots arranged by height, morning light through window',
],
},
{
kw: ['обучен', 'курс', 'урок', 'учеб', 'знан', 'навык', 'learn', 'educat'],
concepts: [
'Open textbook with handwritten notes in margins, pencil resting on page, desk lamp',
'Stack of colorful books with a cup of coffee, cozy reading corner, soft morning light',
'A graduation mortarboard on stack of books, warm sunlight from side',
'Hands writing in a notebook, pen visible, blurred background of bookshelf',
],
},
{
kw: ['здоровь', 'спорт', 'фитнес', 'еда', 'питан', 'health', 'fit', 'food'],
concepts: [
'Fresh vegetables arranged artfully on white surface, overhead shot, natural light',
'Running shoes on wooden floor, morning light casting long shadows',
'A glass of water with ice and mint, condensation visible, clean white background',
'Yoga mat rolled out near window with morning light streaming in',
],
},
];
for (const { kw, concepts } of patterns) {
if (kw.some(k => combined.includes(k))) {
return pick(concepts);
}
}
// Универсальные — нейтральные но конкретные
const generic = [
'A single lighthouse on rocky coast at dusk, warm light in tower, dramatic sky',
'An empty stage with single spotlight on plain wooden chair, theatre atmosphere',
'A vintage compass on worn leather journal, mountain wilderness background',
'A door slightly ajar with warm light escaping, curious hallway perspective',
'A single match being struck in complete darkness, dramatic flare close-up',
'A crossroads sign in fog, gravel road, dawn light breaking through',
'A paper boat on still water, single ripple expanding outward, minimalist',
'An old film projector casting beam of light, dust particles visible, cinema',
'A telescope pointed skyward from rooftop, city lights below, stars above',
'A bridge disappearing into morning fog, pedestrian perspective',
'A ceramic coffee cup with steam rising, morning light through window',
'An open notebook with a pen and fern plant, flat lay, natural light',
];
return pick(generic);
}