Spaces:
Sleeping
Sleeping
| import os | |
| import gradio as gr | |
| # Объявляем упущенную переменную лимита кадров | |
| MAX_FRAMES = 100 | |
| MODELS = { | |
| "⚡ FLUX.1 Schnell (Максимальное качество)": "flux", | |
| "📸 FLUX Realism-Фото (Кинематографичные детали)": "flux-realism", | |
| "🇯🇵 FLUX Anime HD (Идеальное аниме)": "flux-anime" | |
| } | |
| STYLES = { | |
| "Без стиля (Чистый промпт)": "", | |
| "Реализм / Фото": ", cinematic photography, highly detailed, 8k resolution, realistic lighting, dramatic atmosphere, ultra-sharp focus", | |
| "Аниме / Манга": ", anime style, studio ghibli aesthetic, vibrant colors, detailed line art, masterpiece", | |
| "Киберпанк": ", cyberpunk style, neon lights, futuristic city, dark synthwave aesthetic, 8k", | |
| "Фэнтези / Иллюстрация": ", fantasy digital painting, mythical environment, concept art, soft lighting", | |
| "3D Рендер / Pixar": ", 3d render, pixar style, cute character design, claymation feel, vibrant" | |
| } | |
| ASPECT_RATIOS = { | |
| "16:9 (Видеоролики)": "width=1024&height=576", | |
| "9:16 (Shorts/Reels)": "width=576&height=1024", | |
| "1:1 (Квадрат)": "width=768&height=768" | |
| } | |
| # JS-скрипт для автономной работы внутри вашего смартфона через ваш VPN | |
| head_js = """ | |
| <script src="https://cloudflare.com"></script> | |
| <script> | |
| window.js_pipeline = async function(prompts_text, max_frames, model_key, style_key, aspect_key) { | |
| if (!prompts_text.trim()) return ["", "Ошибка: Поле пустое!", ""]; | |
| let lines = prompts_text.split('\\n').map(l => l.trim()).filter(l => l.length > 0); | |
| if (lines.length === 0) return ["", "Ошибка: Нет промптов!", ""]; | |
| let count = Math.min(lines.length, parseInt(max_frames)); | |
| let prompts = lines.slice(0, count); | |
| let target_div = document.getElementById('storyboard_container'); | |
| target_div.innerHTML = "<div style='display:flex; flex-direction:column; gap:40px; align-items:center; width:100%;'></div>"; | |
| let column = target_div.firstChild; | |
| let zip = new JSZip(); | |
| let success_count = 0; | |
| let status_box = document.querySelector('#status_box textarea'); | |
| if(status_box) status_box.value = "🚀 Запуск генерации напрямую через ваш VPN..."; | |
| for (let i = 0; i < prompts.length; i++) { | |
| let idx = i + 1; | |
| let pad_idx = String(idx).padStart(3, '0'); | |
| let full_prompt = prompts[i] + style_key; | |
| let seed = Math.floor(Math.random() * 1000000); | |
| let url = `https://pollinations.ai{encodeURIComponent(full_prompt)}?${aspect_key}&model=${model_key}&seed=${seed}&nologo=true`; | |
| if(status_box) status_box.value = `⏳ Прорисовываем Кадр ${pad_idx} из ${prompts.length}...`; | |
| let card = document.createElement('div'); | |
| card.style = "width:100%; max-width:850px; border:2px solid #eef0f2; padding:20px; border-radius:12px; background:#ffffff; text-align:center; box-shadow:0 4px 12px rgba(0,0,0,0.05);"; | |
| card.innerHTML = ` | |
| <h3 style="margin:0 0 12px 0; color:#222; font-family:sans-serif; font-size:18px;">🖼️ КАДР ${pad_idx}</h3> | |
| <div id="spinner_${idx}" style="width:100%; height:250px; background:#f1f5f9; display:flex; align-items:center; justify-content:center; border-radius:8px; color:#64748b; font-weight:bold;">🎬 Нейросеть рисует кадр...</div> | |
| <p style="font-size:14px; color:#444; margin:15px 0; font-family:sans-serif; background:#f5f7f9; padding:12px; border-radius:6px; text-align:left;">${prompts[i]}</p> | |
| <button id="btn_${idx}" style="background:#3b82f6; border:none; padding:10px 20px; border-radius:6px; cursor:pointer; font-weight:bold; color:#ffffff; font-family:sans-serif;">🔄 Перегенерировать этот Кадр</button> | |
| `; | |
| column.appendChild(card); | |
| try { | |
| let response = await fetch(url); | |
| if (response.ok) { | |
| let blob = await response.blob(); | |
| zip.file(`${pad_idx}.png`, blob); | |
| success_count++; | |
| let blobUrl = URL.createObjectURL(blob); | |
| let spinner = document.getElementById(`spinner_${idx}`); | |
| if(spinner) { | |
| spinner.outerHTML = `<img src="${blobUrl}" style="width:100%; height:auto; border-radius:8px; box-shadow:0 6px 16px rgba(0,0,0,0.1);" />`; | |
| } | |
| document.getElementById(`btn_${idx}`).onclick = async function() { | |
| this.innerHTML = "⏳ Перерисовываю..."; | |
| this.style.background = "#cbd5e1"; | |
| let new_seed = Math.floor(Math.random() * 1000000); | |
| let new_url = `https://pollinations.ai{encodeURIComponent(full_prompt)}?${aspect_key}&model=${model_key}&seed=${new_seed}&nologo=true`; | |
| try { | |
| let res = await fetch(new_url); | |
| if(res.ok) { | |
| let new_blob = await res.blob(); | |
| zip.file(`${pad_idx}.png`, new_blob); | |
| let new_blobUrl = URL.createObjectURL(new_blob); | |
| card.querySelector('img').src = new_blobUrl; | |
| } | |
| } catch(e){} | |
| this.innerHTML = "🔄 Перегенерировать этот Кадр"; | |
| this.style.background = "#3b82f6"; | |
| }; | |
| } | |
| } catch (e) { | |
| let spinner = document.getElementById(`spinner_${idx}`); | |
| if(spinner) spinner.innerText = "⚠️ Ошибка сети. Проверьте VPN."; | |
| } | |
| } | |
| if (success_count > 0) { | |
| if(status_box) status_box.value = `🔥 Готово! Успешно создано кадров: ${success_count} из ${prompts.length}.`; | |
| let zipBlob = await zip.generateAsync({type: "blob"}); | |
| let zipUrl = URL.createObjectURL(zipBlob); | |
| let dl_box = document.getElementById('local_download_box'); | |
| if(dl_box) { | |
| dl_box.innerHTML = `<a href="${zipUrl}" download="capcut_batch.zip" style="display:block; text-align:center; background:#10b981; color:white; padding:15px; border-radius:8px; font-weight:bold; text-decoration:none; font-size:18px; box-shadow:0 4px 12px rgba(16,185,129,0.2);">📦 СКАЧАТЬ ГОТОВЫЙ ZIP ДЛЯ CAPCUT (${(zipBlob.size/1024/1024).toFixed(2)} MB)</a>`; | |
| } | |
| } else { | |
| if(status_box) status_box.value = "Ошибка: Не удалось получить картинки. Смените страну в вашем VPN."; | |
| } | |
| return []; | |
| } | |
| </script> | |
| """ | |
| # В Gradio 6 параметры темы и скриптов передаются в launch() метод | |
| with gr.Blocks() as demo: | |
| gr.Markdown("# 🎬 Безлимитный Автономный Конвейер на Настоящем FLUX.1 (Мобильная Версия 5.1)") | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| prompts_input = gr.TextArea( | |
| label="Сюжетные промпты (Поле очищено. Вставьте каждый кадр строго с новой строки)", | |
| value="", | |
| placeholder="Вставьте сюда ваш готовый текст рассказа по строкам...", | |
| lines=10 | |
| ) | |
| max_frames_slider = gr.Slider(minimum=1, maximum=MAX_FRAMES, value=10, step=1, label="Лимит кадров за один запуск") | |
| with gr.Row(): | |
| model_dropdown = gr.Dropdown(choices=list(MODELS.keys()), value="⚡ FLUX.1 Schnell (Максимальное качество)", label="Движок Нейросети") | |
| style_dropdown = gr.Dropdown(choices=list(STYLES.keys()), value="Без стиля (Чистый промпт)", label="Стиль") | |
| aspect_dropdown = gr.Dropdown(choices=list(ASPECT_RATIOS.keys()), value="16:9 (Видеоролики)", label="Формат") | |
| generate_btn = gr.Button("🚀 Запустить генерацию партии кадров", variant="primary") | |
| with gr.Column(scale=3): | |
| status_output = gr.Textbox(label="Статус выполнения конвейера", elem_id="status_box", interactive=False) | |
| download_html = gr.HTML(value="<div id='local_download_box' style='color:#64748b; text-align:center; padding:20px; border:2px dashed #cbd5e1; border-radius:8px;'>Архив подготовится автоматически после генерации...</div>") | |
| gr.Markdown("### 📺 Крупный просмотр кадров оригинального качества (Столбец)") | |
| gallery_html = gr.HTML(value="<div id='storyboard_container'><p style='color:#888; text-align:center;'>Здесь появится вертикальный список ваших кадров после нажатия кнопки...</p></div>") | |
| generate_btn.click( | |
| fn=None, | |
| inputs=[prompts_input, max_frames_slider, model_dropdown, style_dropdown, aspect_dropdown], | |
| outputs=[], | |
| js="window.js_pipeline" | |
| ) | |
| # По новым правилам Gradio 6 подключаем тему и JavaScript-заголовок здесь | |
| if __name__ == "__main__": | |
| demo.launch(theme=gr.themes.Soft(), head=head_js) | |