From 31b31b75b89afd1eec1eb1d4bfa28dcf4ca87f5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9D=D0=B8=D0=BA=20=28Claude=29?= Date: Sun, 14 Jun 2026 15:13:20 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20post=20images=20diversity=20=E2=80=94=20?= =?UTF-8?q?SUBJECT+SETTING+LIGHTING=20prompt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Та же проблема что и у обложек статей: старый промт '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 символов поста → детерминировано но уникально --- src/services/postImages.js | 152 +++++++++++++++++++++++++++++++++++-- 1 file changed, 145 insertions(+), 7 deletions(-) diff --git a/src/services/postImages.js b/src/services/postImages.js index 65d1320..2084df0 100644 --- a/src/services/postImages.js +++ b/src/services/postImages.js @@ -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); +}