diff --git a/src/services/covers.js b/src/services/covers.js index 56a4c0b..7e54db3 100644 --- a/src/services/covers.js +++ b/src/services/covers.js @@ -167,7 +167,7 @@ function buildCoverPrompt({ title, tags = [], articleId = 0, channelStyle = null paletteDesc = PALETTES[channelStyle.image_palette] || null; } - const visualMetaphor = getVisualMetaphor(title, tags); + const visualMetaphor = getVisualMetaphor(title, tags, articleId); return `Create a wide 16:9 editorial cover illustration. @@ -183,112 +183,166 @@ ${channelStyle?.image_prompt_instructions ? `\nCHANNEL STYLE: ${channelStyle.ima RULE: absolutely no text, no letters, no words, no logos, no real human faces.`; } -function getVisualMetaphor(title, tags = []) { +/** + * Возвращает уникальную визуальную метафору для обложки. + * articleId гарантирует уникальность даже при одинаковой теме. + * Метафоры — конкретные, материальные, не абстрактные. + */ +function getVisualMetaphor(title, tags = [], articleId = 0) { const t = (title + ' ' + tags.join(' ')).toLowerCase(); + // Чистый index по articleId — каждая новая статья = следующая метафора в цикле + // Гарантирует повторение только через N статей (N = кол-во метафор в категории) + function pick(arr) { return arr[articleId % arr.length]; } + + // Тематические паттерны → конкретные визуальные метафоры const patterns = [ - { kw: ['взлом', 'хакер', 'атак', 'уязвим', 'безопасн', 'hack', 'secur', 'cyber', 'exploit', 'inject', 'фишинг'], + { kw: ['взлом', 'хакер', 'атак', 'уязвим', 'безопасн', 'hack', 'secur', 'cyber', 'exploit', 'inject', 'фишинг', 'ransomware'], metaphors: [ - 'A digital lock being opened by invisible force, streams of binary code dissolving into particles', - 'Abstract shield shattering into geometric fragments, neon cracks spreading through dark digital surface', - 'A labyrinth of glowing circuits with a single thread breaking through a barrier', - 'Mirror reflecting a distorted version of itself, duality and deception made abstract', + 'A vintage combination lock under ultraviolet light, tumblers glowing, one tumbler broken open — dark industrial background', + 'A glass door with a hairline crack spreading across its surface, red light leaking through the fracture', + 'An old steel safe with its door hanging open, interior papers spilling out, emergency light casting harsh shadows', + 'A chain with one shattered link, chrome and steel, dramatic spotlight on the break point', + 'An antique key duplicating itself in a hall of mirrors, each copy slightly distorted', + 'A fingerprint dissolving into scattered pixels under a magnifying glass, neon forensic lab aesthetic', + 'A locked filing cabinet surrounded by floating question marks and warning symbols, cinematic lighting', ] }, - { kw: ['ии', ' ai ', 'нейро', 'llm', 'gpt', 'claude', 'модел', 'neural', 'machine learn', 'deep learn', 'искусственн'], + { kw: ['ии', ' ai ', 'нейро', 'llm', 'gpt', 'claude', 'модел', 'neural', 'machine learn', 'deep learn', 'искусственн', 'chatgpt'], metaphors: [ - 'Abstract neural network nodes pulsing with light, interconnected pathways branching into infinity', - 'A geometric brain lattice dissolving at the edges into streams of luminous particles', - 'Floating data cubes assembling themselves into emergent patterns, representing machine cognition', - 'Concentric rings of light expanding outward from a central pulsing node', + 'A vintage typewriter whose keys are being pressed by invisible force, paper emerging with text forming itself', + 'An hourglass where sand flows upward, each grain transforming into a tiny glowing letter', + 'A book opening by itself in a dark library, pages turning rapidly, text rearranging mid-air', + 'An old brass telescope pointed at a sky full of equations instead of stars', + 'A clay sculpture being shaped by invisible hands, smooth surfaces revealing complex geometry underneath', + 'A single candle flame in darkness, its shadow casting a complex branching pattern on the wall behind', + 'A compass spinning wildly then settling on a new direction, surrounded by scattered maps and charts', + 'A chess queen piece casting a shadow of an entire army, dramatic chiaroscuro lighting', + 'An optical prism splitting white light into spectrum, mounted on dark velvet, macro photography', + 'A moth drawn to a glowing lamp, dozens of moths circling, long exposure photography blur', + 'A seed germinating in time-lapse, roots and shoots emerging simultaneously, dark soil background', + 'A master key on a ring of different keys, held up to light, warm metal tones', + 'A musician tuning an instrument by ear, close-up on hands on strings, warm studio light', + 'A cartographer sketching an unmapped territory, hand drawing a coastline into blank space', ] }, - { kw: ['автомат', 'бот', 'automat', 'workflow', 'pipeline', 'скрипт', 'robot', 'n8n', 'make ', 'zapier'], + { kw: ['автомат', 'бот', 'automat', 'workflow', 'pipeline', 'скрипт', 'robot', 'n8n', 'make ', 'zapier', 'automation'], metaphors: [ - 'Elegant gears of light meshing perfectly, each tooth sparking as they turn in unison', - 'Abstract conveyor belt transforming raw geometric shapes into refined crystalline forms', - 'Overlapping circular flow arrows suggesting perpetual automation, each loop more refined', + 'Vintage clockwork mechanism — interlocking brass gears in motion, warm amber light, macro detail', + 'A factory assembly line condensed to a tabletop, tiny objects moving through stages, long exposure light trails', + 'A domino chain in the moment of falling, each domino a different color, motion blur', + 'A switchboard operator desk with hundreds of cables connecting ports, warm retro office lighting', + 'Pipes of different materials connecting and merging into one clean output pipe, industrial workshop', + 'A rube goldberg sequence frozen in time, multiple contraptions mid-action', + 'Time-lapse of a city intersection at night, light trails forming perfect flow patterns', ] }, - { kw: ['данн', 'аналит', 'data', 'analyt', 'метрик', 'статист', 'chart', 'график'], + { kw: ['данн', 'аналит', 'data', 'analyt', 'метрик', 'статист', 'chart', 'график', 'seo'], metaphors: [ - 'Rivers of light converging from edges to a bright focal point, order emerging from streams', - 'Translucent scatter plot in 3D space, clusters of meaning floating in abstract dimension', - 'Cascading data streams transforming from chaotic noise into clean organized patterns', + 'A vintage stock ticker machine printing endless paper tape, trading floor atmosphere', + 'A weather station with multiple instruments all pointing to extremes, storm approaching', + 'Cartographer desk with overlapping transparent maps, pins and measurements, candlelight', + 'A microscope slide with a complex pattern of cells, lab setting, precise clinical lighting', + 'Library card catalog system with infinite drawers, one drawer pulled open revealing complex index', + 'A piano with sheet music where the notes form bar chart patterns, concert hall lighting', + 'A surveyor theodolite in a field, precision instruments against a dramatic sky', ] }, { kw: ['дипфейк', 'голос', 'deepfake', 'voice', 'fraud', 'fake', 'мошенн'], metaphors: [ - 'A face dissolving at the edges into a mosaic of pixels, the real becoming uncertain', - 'Abstract mask layered over another mask, shadow figures suggesting layered identity', - 'Two mirrors facing each other creating infinite regress, reality and reflection indistinguishable', + 'A wax museum figure melting slightly at the edges, museum spotlights, uncanny valley', + 'Two theatrical masks — comedy and tragedy — but identical, hanging in spotlight', + 'A reflection in water that is slightly different from what stands above it, twilight mood', + 'Vintage photobooth with curtain closed, multiple shadow silhouettes visible through fabric', + 'A hand puppet casting a human-shaped shadow on a wall, single dramatic light source', + 'A mirror cracked down the middle, each half showing a different scene', ] }, { kw: ['код', 'разработ', 'програм', 'code', 'develop', 'software', 'api', 'github', 'cursor', 'copilot'], metaphors: [ - 'Layers of abstract code symbols transforming into crystalline architectural structures', - 'A branching tree of glowing paths, each fork representing choices in logic flow', - 'Floating geometric modules snapping together like elegant puzzle pieces in zero gravity', + 'A craftsman workbench covered in tools, each precisely placed, wood shavings on floor, workshop window light', + 'An architect drafting table with blueprints unrolled, compass and ruler in use, desk lamp', + 'A vintage telephone exchange switchboard, operator perspective, warm bulb lights', + '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 a different texture, golden hour', + 'An open laptop in a dark room, only the screen light illuminating the workspace, code reflection on glasses', ] }, - { kw: ['seo', 'маркетинг', 'контент', 'реклам', 'marketing', 'content', 'growth', 'продвиж'], + { kw: ['маркетинг', 'контент', 'реклам', 'marketing', 'content', 'growth', 'продвиж', 'аудитор'], metaphors: [ - 'Abstract particles rising in an upward spiral, accelerating as they approach the light', - 'Geometric shapes spontaneously organizing from scattered chaos into perfect aligned grid', - 'A single beam cutting through layered fog, illuminating a clear path forward', + 'A megaphone lying on a table, vintage brass, city map spread underneath it', + 'A fishing rod cast into a sea of business cards, one card caught on the hook', + 'A vendor market stall being set up, colorful awning, products arranged attractively, morning light', + 'Seeds being planted in geometric rows, birds-eye view, garden tools aside, spring light', + 'A lighthouse beam sweeping over a foggy harbor, ships turning toward the light', + 'A street performer drawing a large crowd, performer perspective from stage, golden sunset', ] }, { kw: ['email', 'рассылк', 'письм', 'newsletter', 'inbox'], metaphors: [ - 'Paper planes of light streaming outward from a central pulsing source in all directions', - 'Abstract envelopes unfolding into origami shapes that take flight as geometric birds', - 'Digital waves radiating concentrically outward, each ring more refined and intentional', + 'A vintage mailbox overflowing with envelopes, country road, afternoon light', + 'A writing desk with fountain pen mid-sentence on paper, wax seal nearby, window light', + 'Hundreds of paper airplanes suspended in air radiating from a single point, white room', + 'A postal sorting office with letters on conveyor, warm institutional lighting', + 'A message in a bottle washing ashore, ocean at golden hour, single perfect wave', ] }, - { kw: ['vector', 'embed', 'pinecone', 'база знаний', 'rag ', 'retriev', 'weaviate', 'pgvector'], + { kw: ['prompt', 'промпт', 'инжиниринг', 'instruct', 'few-shot', 'chain', 'копирайт'], metaphors: [ - 'Multi-dimensional space: glowing constellation points clustering by invisible forces of meaning', - 'Abstract geometric lattice stretching to infinity, nodes connected by silk-thin lines', - 'A library of light — glowing geometric books organized in vast abstract infinite space', + 'A sculptor chiseling marble, chips flying, emerging form just visible, dramatic studio lighting', + 'A translator desk with multiple open dictionaries and handwritten notes between them', + 'A cook adjusting seasoning over a pot, herbs and spices scattered artfully on counter', + 'A jeweler using loupe to set a tiny gem, precision tools, warm workshop light', + 'A conductor baton frozen mid-gesture, orchestra music sheets visible below', ] }, - { kw: ['prompt', 'промпт', 'инжиниринг', 'instruct', 'few-shot', 'chain'], + { kw: ['vector', 'embed', 'база знаний', 'rag ', 'pinecone', 'weaviate', 'langchain', 'llamaindex'], metaphors: [ - 'Abstract command flowing as light through a series of transforming geometric gates', - 'A sculptor chisel made of light, shaping formless material into precise form', - 'Words becoming shapes becoming outcomes — a visual sequence of transformation', + 'A vast library card catalog extending to infinite depth, one card pulled mid-search', + 'A specimen collection in glass cases, each specimen precisely labeled, natural history museum', + 'A wine cellar with bottles organized in a complex system, sommelier consulting notes', + 'A large cork pinboard covered in photos and strings connecting them, detective war room', + 'A seed bank vault with thousands of labeled drawers, clinical cold light', + ] + }, + { kw: ['продуктив', 'задач', 'workflow', 'менеджмент', 'таск', 'task', 'agile', 'sprint'], + metaphors: [ + 'A physical inbox/outbox tray system stacked high on wooden desk, morning office light', + 'A large whiteboard covered in sticky notes being organized by unseen hands', + 'A craftsman apron with every tool in its pocket, each in perfect order', + 'Calendar pages turning on a desk, one date circled in red, pen nearby', ] }, ]; for (const { kw, metaphors } of patterns) { if (kw.some(k => t.includes(k))) { - // Детерминированный но разнообразный выбор по хешу заголовка - let seed = 0; - for (let i = 0; i < title.length; i++) seed = (seed * 31 + title.charCodeAt(i)) >>> 0; - return metaphors[seed % metaphors.length]; + return pick(metaphors); } } + // Универсальные метафоры — конкретные и разнообразные const generic = [ - 'Abstract architectural forms assembling from scattered fragments into a coherent whole', - 'Geometric light rays converging from multiple directions into a single illuminated focal point', - 'Flowing liquid-like shapes morphing and transforming, suggesting evolution and emergence', - 'A horizon where two contrasting worlds meet — ordered and organic — in perfect visual tension', - 'Cascading layers of translucent geometric planes revealing depth and hidden complexity', - 'Crystalline growth pattern emerging organically from a single seed point into elaborate structure', - 'Diagonal force lines cutting through deep negative space with directional energy and purpose', - 'Nested geometric forms expanding outward from center like a visual echo in still water', - 'Abstract topography of peaks and valleys formed by data, landscape of pure information', - 'A vortex of geometric elements spiraling inward toward a luminous center of clarity', + 'A single lighthouse on a rocky coast at dusk, warm light in the tower, storm approaching from sea', + 'A vintage globe being spun by unseen hands, old map colors, library atmosphere', + 'A hand planting a seed in rich dark soil, morning mist in background, close-up', + 'An empty stage with a single spotlight on a plain wooden chair, theatre atmosphere', + 'A vintage compass on a worn leather journal, mountain wilderness in background', + 'A mason jar filled with fireflies, summer night, porch railing, blurred trees behind', + 'An old film projector casting beam of light, dust particles visible, cinema darkness', + 'A door slightly ajar, warm light escaping, curious perspective from hallway', + 'A ladder leaning against a wall reaching above the clouds, surreal realistic style', + 'A single match being struck in complete darkness, dramatic flare, close-up', + 'A crossroads sign pointing in four different directions, fog, gravel road, dawn', + 'A paper boat on still water, single ripple expanding outward, minimalist', + 'An old radio being tuned, backlit frequency dial, warm evening light', + 'A telescope pointed skyward from a rooftop, city lights below, stars above', + 'A bridge in fog, one end visible, other end disappearing into mist, pedestrian perspective', ]; - let seed = 0; - for (let i = 0; i < title.length; i++) seed = (seed * 31 + title.charCodeAt(i)) >>> 0; - return generic[seed % generic.length]; + return pick(generic); } - async function generateCoverViaRouterAI({ prompt }) { const base = config.ai.routeraiBaseUrl; const key = config.ai.routeraiApiKey;