my-generator / app.py
Bogatabus's picture
Update app.py
d512512 verified
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)