const { useState, useEffect, useRef, useCallback } = React; const API_BASE = 'https://backend-api-iio48.ondigitalocean.app'; // const API_BASE = 'http://localhost:8002'; // Celebrity-specific templates const CELEB_TEMPLATES = { PUTIN_BEREZKI: [ { id: 'birthday', name: 'День рождения', icon: '🎂', defaultText: 'Добрый день, {name}. С днём рождения. Вы достигли серьёзного возраста — значит, вы на правильном пути. Желаю вам здоровья и благополучия. Пусть ваша жизнь будет стабильна, как курс рубля в хороший день. С днём рождения!' }, { id: 'jubilee', name: 'Юбилей', icon: '🎊', defaultText: 'Добрый день, {name}. С юбилеем вас. Круглая дата — это повод подвести итоги и наметить новые цели. Вы многого добились. Желаю вам крепкого здоровья, мудрости и уверенности в завтрашнем дне. С юбилеем!' }, { id: 'promotion', name: 'Повышение', icon: '🎉', defaultText: 'Добрый день, {name}. Поздравляю с повышением. Это закономерный результат вашей работы. Теперь на вас лежит больше ответственности. Уверен, вы справитесь. Желаю успехов на новом посту. Работайте на благо дела.' }, { id: 'wedding', name: 'Свадьба', icon: '💒', defaultText: 'Добрый день, {name}. Поздравляю со свадьбой. Семья — это основа государства и общества. Берегите друг друга, уважайте, поддерживайте. Желаю вам крепкого союза и семейного благополучия. Совет да любовь!' } ], PUTIN_FISHING: [ { id: 'birthday', name: 'День рождения', icon: '🎂', defaultText: 'Добрый день, {name}. С днём рождения. Вы достигли серьёзного возраста — значит, вы на правильном пути. Желаю вам здоровья и благополучия. Пусть ваша жизнь будет стабильна, как курс рубля в хороший день. С днём рождения!' }, { id: 'jubilee', name: 'Юбилей', icon: '🎊', defaultText: 'Добрый день, {name}. С юбилеем вас. Круглая дата — это повод подвести итоги и наметить новые цели. Вы многого добились. Желаю вам крепкого здоровья, мудрости и уверенности в завтрашнем дне. С юбилеем!' }, { id: 'promotion', name: 'Повышение', icon: '🎉', defaultText: 'Добрый день, {name}. Поздравляю с повышением. Это закономерный результат вашей работы. Теперь на вас лежит больше ответственности. Уверен, вы справитесь. Желаю успехов на новом посту. Работайте на благо дела.' }, { id: 'wedding', name: 'Свадьба', icon: '💒', defaultText: 'Добрый день, {name}. Поздравляю со свадьбой. Семья — это основа государства и общества. Берегите друг друга, уважайте, поддерживайте. Желаю вам крепкого союза и семейного благополучия. Совет да любовь!' } ], EANDREEVA: [ { id: 'birthday', name: 'День рождения', icon: '🎂', defaultText: 'Добрый вечер. В эфире новости. Экстренное сообщение: сегодня {name} отмечает день рождения. По данным аналитиков, ваш личный рейтинг бьёт рекорды. Прогноз на год: стабильный рост доходов и безоблачное счастье. Оставайтесь с нами. С днём рождения!' }, { id: 'jubilee', name: 'Юбилей', icon: '🎊', defaultText: 'Мы прерываем эфир ради важной новости. {name} сегодня отмечает юбилей. Это событие федерального масштаба. Вы выглядите безупречно, как и ваша репутация. Желаем вам только хороших новостей и эфирного времени для радости. С юбилеем!' }, { id: 'promotion', name: 'Повышение', icon: '🎉', defaultText: 'Срочная новость в студии. {name} получает повышение. Наши корреспонденты подтверждают: это заслуженно. Прогноз экспертов: карьерный рост продолжится. Желаем вам новых профессиональных высот. Следите за нашими эфирами!' }, { id: 'wedding', name: 'Свадьба', icon: '💒', defaultText: 'Внимание, главная новость дня. {name} вступает в брак. По информации из достоверных источников, это союз, основанный на любви и взаимном уважении. Прогноз на будущее: долгая и счастливая семейная жизнь. Поздравляем!' } ], URGANT: [ { id: 'birthday', name: 'День рождения', icon: '🎂', defaultText: 'Привет, {name}! С днём рождения! Сегодня тот редкий день, когда ты главнее всех гостей в этой воображаемой студии. Желаю тебе рейтингов выше, чем у нас, и зарплаты, как у тех, кого мы не называем. Улыбайся, камера ведь работает!' }, { id: 'jubilee', name: 'Юбилей', icon: '🎊', defaultText: '{name}, с юбилеем! Это прекрасный возраст: вы уже всё знаете, но ещё всё можете. Желаю вам здоровья, как у космонавтов, и нервов, как у моих сценаристов. Пусть ваша жизнь будет лучшим вечерним шоу без рекламы!' }, { id: 'promotion', name: 'Повышение', icon: '🎉', defaultText: '{name}, с повышением! Теперь ты начальник, а значит, шутить над тобой опасно. Желаю, чтобы кресло было удобным, подчиненные — понятливыми, а ответственность не мешала спать. Держи спинку ровно, мы смотрим!' }, { id: 'wedding', name: 'Свадьба', icon: '💒', defaultText: '{name}, поздравляю со свадьбой! Это как подписать контракт на самое длинное шоу в твоей жизни. Желаю, чтобы рейтинги вашей семьи только росли, а спонсоры счастья никогда не заканчивались. Совет да любовь, как говорится!' } ], HARLAMOV: [ { id: 'birthday', name: 'День рождения', icon: '🎂', defaultText: 'Ооо, {name}! С днём рождения, дорогой! Слушай, ну ты вообще красавчик! Столько лет уже, а всё ещё в строю! Желаю тебе здоровья, денег и чтобы все анекдоты в твоей жизни были смешными! С днюхой!' }, { id: 'jubilee', name: 'Юбилей', icon: '🎊', defaultText: 'Боже мой, какой юбилей у {name}! Ты смотри, столько лет на этой планете, а ты ещё не развалился! Это же подвиг какой-то, да, {name}? Желаю тебе продолжать быть таким же крепким, весёлым и не стареть душой! С юбилеем, чемпион!' }, { id: 'promotion', name: 'Повышение', icon: '🎉', defaultText: '{name}, ну ты даёшь! Повышение получил! Теперь ты большой начальник, да? Желаю тебе, чтобы подчинённые не доставали, зарплата радовала, а кресло было мягким! Давай, руководи там, мы в тебя верим!' }, { id: 'wedding', name: 'Свадьба', icon: '💒', defaultText: '{name}, ну поздравляю со свадьбой! Это ж надо было решиться! Желаю вам терпения друг к другу, любви побольше и чтобы тёща далеко жила! Шучу, шучу! Совет да любовь, ребята!' } ], MALAHOV: [ { id: 'birthday', name: 'День рождения', icon: '🎂', defaultText: 'Добро пожаловать на мою программу поздравлений, {name}! Итак, сегодня мы говорим о вас — о человеке, который прожил столько лет. Желаю вам стабильности, здоровья и чтобы ваша жизнь была интересна, как документальный фильм. Поздравляю!' }, { id: 'jubilee', name: 'Юбилей', icon: '🎊', defaultText: 'Сегодня в нашей студии особенный гость — {name} празднует юбилей! Это потрясающая история успеха. Вы прошли через многое и стали только сильнее. Желаю вам ещё больше ярких страниц в книге вашей жизни. С юбилеем!' }, { id: 'promotion', name: 'Повышение', icon: '🎉', defaultText: 'Внимание, сенсация! {name} получает повышение! Это история, которую нужно рассказать всем. Вы заслужили этот успех своим трудом. Желаю вам покорять новые вершины и всегда быть в центре внимания. Поздравляю!' }, { id: 'wedding', name: 'Свадьба', icon: '💒', defaultText: 'Дорогие друзья, у нас эксклюзив! {name} выходит замуж! Это самая романтичная история этого сезона. Желаю вам любви, которая не знает преград, и счастья на всю жизнь. Оставайтесь с нами, впереди много интересного!' } ], KIRKOROV: [ { id: 'birthday', name: 'День рождения', icon: '🎂', defaultText: '{name}, с днём рождения! Пусть твоя жизнь сверкает ярче, чем мой лучший костюм! Желаю тебе купаться в овациях, тонуть в цветах и всегда быть в тренде. Ты — звезда, и сегодня твоё соло. Сияй, мой друг, сияй!' }, { id: 'jubilee', name: 'Юбилей', icon: '🎊', defaultText: '{name}, браво! Юбилей — это королевский праздник. Вы выглядите потрясающе, время не властно над истинным талантом жить! Желаю вам любви фанатов, золота успехов и вечной молодости. Цвет настроения — праздничный!' }, { id: 'promotion', name: 'Повышение', icon: '🎉', defaultText: '{name}, поздравляю! Повышение — это твой новый хит, который взорвал чарты! Теперь ты на вершине музыкального олимпа офиса. Желаю править мудро, жить роскошно и никогда не оглядываться на массовку. Шоу продолжается!' }, { id: 'wedding', name: 'Свадьба', icon: '💒', defaultText: '{name}, какая свадьба! Это грандиозное событие, достойное лучших площадок мира! Желаю вам любви, от которой захватывает дух, и семейного счастья, как в самых красивых песнях. Пусть ваш дуэт звучит вечно!' } ], DMITRIENKO: [ { id: 'birthday', name: 'День рождения', icon: '🎂', defaultText: 'Йо, {name}! С днём рождения! Желаю, чтобы твой год был на репите только с хорошими моментами. Лови свой вайб, не слушай хейтеров и будь в топе. Ты реально крутой человек, пусть всё сбудется. Обнял!' }, { id: 'jubilee', name: 'Юбилей', icon: '🎊', defaultText: '{name}, с юбилеем! Честно, вы выглядите свежо, как новый релиз. Желаю, чтобы здоровье было на максималках, а энергии хватало на все движи. Оставайтесь таким же современным и позитивным. Респект вам!' }, { id: 'promotion', name: 'Повышение', icon: '🎉', defaultText: '{name}, ну ты выдал! С повышением, это реально мощно. Теперь ты биг босс, но оставайся собой. Желаю, чтобы задачи решались на изи, а кэш только рос. Ты заслужил. Газуй дальше!' }, { id: 'wedding', name: 'Свадьба', icon: '💒', defaultText: '{name}, вау, свадьба! Это ж целый лайфстайл чендж! Желаю вам кайфовать вместе, поддерживать друг друга и строить своё счастье. Любовь — это топ. Будьте в муде, ребята!' } ], TRUMP: [ { id: 'birthday', name: 'День рождения', icon: '🎂', defaultText: '{name}, с днём рождения. Ты отличный парень, просто фантастический. Я знаю людей, ты — один из лучших. Желаю тебе огромного успеха, миллиардов улыбок. Сделай этот год великим снова! Никаких фейковых новостей, только победы.' }, { id: 'jubilee', name: 'Юбилей', icon: '🎊', defaultText: '{name}, с юбилеем. Это огромная цифра, очень серьезная. Вы построили великую жизнь, как башню имени меня — Трампа. Желаю вам здоровья — самого крепкого в мире. Пусть все конкуренты завидуют. Вы победитель, признаю!' }, { id: 'promotion', name: 'Повышение', icon: '🎉', defaultText: '{name}, ты получил повышение. Это было лучшее решение руководства, поверь мне. Ты будешь жестким боссом, но справедливым. Зарабатывай много, управляй сильно. Если кто-то против — уволь их!' }, { id: 'wedding', name: 'Свадьба', icon: '💒', defaultText: '{name}, поздравляю со свадьбой. Это огромная сделка, самая важная в жизни. Я знаю толк в свадьбах, у меня их было несколько. Желаю вам построить великую семью. Любовь — это бизнес, в котором оба должны выигрывать!' } ], ELON: [ { id: 'birthday', name: 'День рождения', icon: '🎂', defaultText: 'Привет, {name}. С днём рождения. Земля сделала ещё один оборот вокруг Солнца вместе с тобой. Желаю, чтобы твой КПД был 100%, а багов в жизни — ноль. Мечтай масштабно, как о колонизации Марса. Будущее за тобой.' }, { id: 'jubilee', name: 'Юбилей', icon: '🎊', defaultText: '{name}, поздравляю с вашим юбилеем — это накопленная база данных опыта. Вы — надежная система. Желаю, чтобы ваш внутренний аккумулятор не садился ещё 100 лет. Инноваций вам, здоровья и чистого неба над головой.' }, { id: 'promotion', name: 'Повышение', icon: '🎉', defaultText: '{name}, поздравляю с апгрейдом статуса. Повышение — это выход на новую орбиту. Теперь у тебя больше гравитации. Желаю управлять процессами эффективно, ускорять прогресс и не бояться перегрузок. Вперед к звёздам!' }, { id: 'wedding', name: 'Свадьба', icon: '💒', defaultText: '{name}, поздравляю со свадьбой. Это как запуск совместной миссии на всю жизнь. Синхронизируйте ваши системы, поддерживайте связь и вместе покоряйте новые горизонты. Пусть ваш союз будет устойчивым, как ракета Falcon.' } ], BURUNOV_FIREPLACE: [ { id: 'birthday', name: 'День рождения', icon: '🎂', defaultText: 'Привет, {name}! С днём рождения! Ты — главный герой собственного фильма! Твой фильм уже столько лет идёт в прокате и всё лучше! Желаю ярких сцен и побед! С днём рождения!' }, { id: 'jubilee', name: 'Юбилей', icon: '🎊', defaultText: '{name}, с юбилеем! Слушай, это же целая эпоха! Как будто сериал, который все смотрят и не могут оторваться. Желаю тебе ещё много сезонов, и чтобы каждый был лучше предыдущего! С юбилеем, дружище!' }, { id: 'promotion', name: 'Повышение', icon: '🎉', defaultText: '{name}, с повышением тебя! Ну ты даёшь, режиссёр своей жизни! Теперь у тебя главная роль в офисе. Желаю, чтобы все сцены были удачными, а гонорар — достойным. Давай, покажи им класс!' }, { id: 'wedding', name: 'Свадьба', icon: '💒', defaultText: 'С праздником, {name}! Я актер, видел много сценариев, но ваш — настоящая история любви! Вы — идеальная пара прям как в кино! Желаю романтики, уважения и счастья всегда! С браком, что называется!' } ], BURUNOV_TOAST: [ { id: 'birthday', name: 'День рождения', icon: '🎂', defaultText: 'Привет, {name}! С днём рождения! Ты — главный герой собственного фильма! Твой фильм уже столько лет идёт в прокате и всё лучше! Желаю ярких сцен и побед! С днём рождения!' }, { id: 'jubilee', name: 'Юбилей', icon: '🎊', defaultText: '{name}, с юбилеем! Слушай, это же целая эпоха! Как будто сериал, который все смотрят и не могут оторваться. Желаю тебе ещё много сезонов, и чтобы каждый был лучше предыдущего! С юбилеем, дружище!' }, { id: 'promotion', name: 'Повышение', icon: '🎉', defaultText: '{name}, с повышением тебя! Ну ты даёшь, режиссёр своей жизни! Теперь у тебя главная роль в офисе. Желаю, чтобы все сцены были удачными, а гонорар — достойным. Давай, покажи им класс!' }, { id: 'wedding', name: 'Свадьба', icon: '💒', defaultText: 'С праздником, {name}! Я актер, видел много сценариев, но ваш — настоящая история любви! Вы — идеальная пара прям как в кино! Желаю романтики, уважения и счастья всегда! С браком, что называется!' } ] }; // Available celebrity personas const PERSONAS = [ { id: 'PUTIN_BEREZKI', name: 'Путин (Берёзки)', image: 'https://black-hole.fra1.cdn.digitaloceanspaces.com/celebs/putin/berezki-input-for-veed.jpeg' }, { id: 'PUTIN_FISHING', name: 'Путин (Рыбалка)', image: 'https://black-hole.fra1.cdn.digitaloceanspaces.com/celebs/putin/input-fish.jpeg' }, { id: 'EANDREEVA', name: 'Екатерина Андреева', image: 'https://black-hole.fra1.cdn.digitaloceanspaces.com/celebs/eandreeva/input-news.jpeg' }, { id: 'URGANT', name: 'Иван Ургант', image: 'https://black-hole.fra1.cdn.digitaloceanspaces.com/celebs/urgant/input-red-jacket.jpeg' }, { id: 'HARLAMOV', name: 'Гарик Харламов', image: 'https://black-hole.fra1.cdn.digitaloceanspaces.com/celebs/harlamov/input-red.jpg' }, { id: 'MALAHOV', name: 'Андрей Малахов', image: 'https://black-hole.fra1.cdn.digitaloceanspaces.com/celebs/malahov/input-green.jpeg' }, { id: 'KIRKOROV', name: 'Филипп Киркоров', image: 'https://black-hole.fra1.cdn.digitaloceanspaces.com/celebs/kirkorov/input-red.jpg' }, { id: 'DMITRIENKO', name: 'Ваня Дмитриенко', image: 'https://black-hole.fra1.cdn.digitaloceanspaces.com/celebs/dmitrienko/input-black.png' }, { id: 'TRUMP', name: 'Дональд Трамп', image: 'https://black-hole.fra1.cdn.digitaloceanspaces.com/celebs/trump/input-green.jpg' }, { id: 'ELON', name: 'Илон Маск', image: 'https://black-hole.fra1.cdn.digitaloceanspaces.com/celebs/elon/input-green.jpg' }, { id: 'BURUNOV_FIREPLACE', name: 'Бурунов (Камин)', image: 'https://black-hole.fra1.cdn.digitaloceanspaces.com/celebs/burunov/input-for-veed.png' }, { id: 'BURUNOV_TOAST', name: 'Бурунов (Тост)', image: 'https://black-hole.fra1.cdn.digitaloceanspaces.com/celebs/burunov/toast-input-for-veed.jpeg' } ]; // Default templates (fallback) const DEFAULT_TEMPLATES = [ { id: 'birthday', name: 'День рождения', icon: '🎂', defaultText: 'С днём рождения, {name}!' }, { id: 'jubilee', name: 'Юбилей', icon: '🎊', defaultText: 'С юбилеем, {name}!' }, { id: 'promotion', name: 'Повышение', icon: '🎉', defaultText: 'С повышением, {name}!' }, { id: 'wedding', name: 'Свадьба', icon: '💒', defaultText: 'Поздравляю со свадьбой, {name}!' } ]; // Persona Card Component function PersonaCard({ persona, isSelected, onSelect }) { return (
onSelect(persona)} >
{persona.name}
{persona.name}
); } // Persona Slider Component function PersonaSlider({ personas, selectedPersona, onSelect }) { const sliderRef = useRef(null); const [showLeftArrow, setShowLeftArrow] = useState(false); const [showRightArrow, setShowRightArrow] = useState(false); const checkScrollButtons = useCallback(() => { if (sliderRef.current) { const { scrollLeft, scrollWidth, clientWidth } = sliderRef.current; setShowLeftArrow(scrollLeft > 5); setShowRightArrow(scrollLeft < scrollWidth - clientWidth - 5); } }, []); useEffect(() => { // Check after a small delay to ensure DOM is ready const timer = setTimeout(checkScrollButtons, 100); const slider = sliderRef.current; if (slider) { slider.addEventListener('scroll', checkScrollButtons); window.addEventListener('resize', checkScrollButtons); } return () => { clearTimeout(timer); if (slider) { slider.removeEventListener('scroll', checkScrollButtons); } window.removeEventListener('resize', checkScrollButtons); }; }, [checkScrollButtons, personas]); const scroll = (direction) => { if (sliderRef.current) { const scrollAmount = 250; const currentScroll = sliderRef.current.scrollLeft; const newScroll = direction === 'left' ? currentScroll - scrollAmount : currentScroll + scrollAmount; sliderRef.current.scrollTo({ left: newScroll, behavior: 'smooth' }); } }; return (
{personas.map(persona => ( ))}
); } // Template Card Component function TemplateCard({ template, isSelected, onSelect }) { return (
onSelect(template)} >
{template.icon}
{template.name}
); } // Video Player Component function VideoPlayer({ videoUrl, onReset }) { return (
Скачать видео
); } // Loading Spinner Component function LoadingSpinner() { return (

Генерируем ваше персональное видео...

); } // History Item Component function HistoryItem({ item, onPlay }) { const persona = PERSONAS.find(p => p.id === item.celeb_id); const statusLabels = { pending: 'В очереди', processing: 'Генерация...', completed: 'Готово', failed: 'Ошибка' }; return (
{persona?.name
{persona?.name || item.celeb_id}
{item.wish_text}
{new Date(item.created_at).toLocaleString('ru-RU')}
{statusLabels[item.status] || item.status}
{item.status === 'completed' && item.video_url && ( <> Скачать )} {(item.status === 'pending' || item.status === 'processing') && (
)}
); } // Main App Component function App() { const [selectedPersona, setSelectedPersona] = useState(null); const [selectedTemplate, setSelectedTemplate] = useState(null); const [recipientName, setRecipientName] = useState(''); const [customText, setCustomText] = useState(''); const [useCustomText, setUseCustomText] = useState(false); const [isGenerating, setIsGenerating] = useState(false); const [generatedVideo, setGeneratedVideo] = useState(null); const [history, setHistory] = useState([]); const [playingVideo, setPlayingVideo] = useState(null); const inputSectionRef = useRef(null); const handleCustomTextClick = () => { setSelectedTemplate({ id: 'custom', name: 'Свой текст', icon: '✏️', defaultText: '' }); setUseCustomText(true); setTimeout(() => { inputSectionRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' }); }, 100); }; // Fetch history on mount and periodically const fetchHistory = async () => { try { const response = await fetch(`${API_BASE}/celebs/generations`); const data = await response.json(); setHistory(data); } catch (error) { console.error('Error fetching history:', error); } }; useEffect(() => { fetchHistory(); // Refresh history every 10 seconds to show processing updates const interval = setInterval(fetchHistory, 10000); return () => clearInterval(interval); }, []); const handleTemplateSelect = (template) => { setSelectedTemplate(template); setUseCustomText(false); setCustomText(''); }; const getDisplayText = () => { if (useCustomText) return customText; if (!selectedTemplate || !selectedTemplate.defaultText) return ''; return selectedTemplate.defaultText.replace(/{name}/g, recipientName || '{name}'); }; const pollJobStatus = async (jobId) => { while (true) { try { const response = await fetch(`${API_BASE}/celebs/generate/${jobId}`); const data = await response.json(); if (data.status === 'completed') { setGeneratedVideo(data.video_url); setIsGenerating(false); fetchHistory(); // Refresh history return; } else if (data.status === 'failed') { alert(`Ошибка: ${data.error || 'Не удалось сгенерировать видео'}`); setIsGenerating(false); fetchHistory(); // Refresh history return; } // Wait 5 seconds before polling again await new Promise(resolve => setTimeout(resolve, 5000)); } catch (error) { console.error('Polling error:', error); alert('Ошибка проверки статуса. Попробуйте ещё раз.'); setIsGenerating(false); return; } } }; const handleGenerate = async () => { if (!selectedPersona || !selectedTemplate || (!recipientName && !useCustomText) || (useCustomText && !customText)) { alert('Пожалуйста, заполните все обязательные поля'); return; } setIsGenerating(true); try { const response = await fetch(`${API_BASE}/celebs/generate`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ celeb_id: selectedPersona.id, wish_text: getDisplayText() }) }); const data = await response.json(); fetchHistory(); // Refresh history to show new pending job // Start polling for job completion pollJobStatus(data.id); } catch (error) { alert('Ошибка создания видео. Попробуйте ещё раз.'); console.error(error); setIsGenerating(false); } }; const handleReset = () => { setSelectedPersona(null); setSelectedTemplate(null); setRecipientName(''); setCustomText(''); setUseCustomText(false); setGeneratedVideo(null); setIsGenerating(false); }; if (generatedVideo) { return (

Ваше видео готово!

); } if (isGenerating) { return (

Создаём ваше поздравление

); } return (

Получи видеопоздравление
от ИИ-звезды

от 490 руб. вместе с озвучкой
от 2 минут для оригинального подарка
{/* Left Section - Selection */}

Выберите звезду

{ setSelectedPersona(p); setSelectedTemplate(null); setUseCustomText(false); }} />

Выберите повод

{(selectedPersona ? CELEB_TEMPLATES[selectedPersona.id] || DEFAULT_TEMPLATES : DEFAULT_TEMPLATES).map(template => ( ))}
✏️
Свой текст
{/* Right Section - Form */}

Персонализируйте поздравление

setRecipientName(e.target.value)} maxLength={50} />