vidltx / index.html
Opera10's picture
Update index.html
78a7eba verified
Raw
History Blame Contribute Delete
25.2 kB
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LTX 2.3 10Eros - Image to Video</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.rtl.min.css">
<style>
body { font-family: Tahoma, sans-serif; background-color: #f8f9fa; padding: 20px; }
.card { margin-bottom: 20px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }
.loader { border: 4px solid #f3f3f3; border-top: 4px solid #0d6efd; border-radius: 50%; width: 30px; height: 30px; animation: spin 1s linear infinite; display: none; margin-bottom: 10px; }
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
#videoResult { width: 100%; max-width: 100%; border-radius: 8px; margin-top: 15px; display: none; }
.form-label { font-weight: bold; font-size: 0.9em; margin-top: 10px; }
#errorBox { display: none; text-align: left; direction: ltr; font-family: monospace; font-size: 0.85em; max-height: 300px; overflow-y: auto; background: #fff3f3; border: 1px solid #f5c2c2; color: #a94442; }
</style>
</head>
<body>
<div class="container-fluid">
<h2 class="text-center mb-4 text-primary">LTX 2.3 10Eros - تبدیل تصویر به ویدیو</h2>
<p class="text-center text-muted">اتصال مستقیم و پایدار (نسخه نهایی با پشتیبانی از ساختار ویدیوی Gradio)</p>
<div class="row">
<!-- ستون تنظیمات -->
<div class="col-lg-7">
<div class="card p-4">
<h5 class="mb-3">تنظیمات اصلی و API</h5>
<label class="form-label">Hugging Face Token (الزامی برای جلوگیری از خطا)</label>
<input type="text" id="hf_token" class="form-control" placeholder="hf_...">
<label class="form-label mt-3">تصویر اصلی (الزامی) [image_path]</label>
<input type="file" id="image_path" class="form-control" accept="image/*">
<label class="form-label mt-3">پرامپت اصلی (الزامی) [prompt]</label>
<textarea id="prompt" class="form-control" rows="3" placeholder="مفهوم و توضیح ویدیو..."></textarea>
<button id="enhanceBtn" class="btn btn-outline-primary btn-sm mt-2">✨ بهینه‌سازی خودکار پرامپت (Enhance)</button>
<label class="form-label mt-3">پرامپت منفی [negative_prompt]</label>
<textarea id="negative_prompt" class="form-control" rows="2">captions, music, transition, VR, bad quality, subtitles, text, watermark, overlay effects, cartoon, childish, ugly, text, blur, logo, static, low quality, noise, mutant, horror, film grain</textarea>
<div class="row mt-3">
<div class="col-md-6">
<label class="form-label">مدت زمان (ثانیه) [seconds]</label>
<input type="number" id="seconds" class="form-control" value="4" step="0.5">
</div>
<div class="col-md-6">
<label class="form-label">پریست (الگوی پیش‌فرض) [preset]</label>
<select id="preset" class="form-select">
<option value="tuned" selected>Tuned</option>
<option value="original">Original</option>
<option value="tuned #2">Tuned #2</option>
<option value="experimental #1">Experimental #1</option>
</select>
</div>
</div>
<!-- تولید فرم به صورت پویا برای سایر تنظیمات -->
<div class="accordion mt-4" id="settingsAccordion"></div>
</div>
</div>
<!-- ستون خروجی -->
<div class="col-lg-5">
<div class="card p-4 text-center sticky-top" style="top: 20px;">
<h5 class="mb-3">تولید ویدیو</h5>
<button id="generateBtn" class="btn btn-success w-100 py-3 mb-3 text-white fw-bold">شروع تولید (Generate)</button>
<div class="d-flex justify-content-center"><div id="loader" class="loader"></div></div>
<div id="statusText" class="text-secondary small fw-bold mb-2">آماده دریافت فرمان...</div>
<!-- باکس نمایش خطای واقعی سرور -->
<div id="errorBox" class="alert p-3"></div>
<!-- ویدیوی خروجی -->
<video id="videoResult" controls></video>
<div id="seedResult" class="mt-3 text-info small"></div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script type="module">
import { Client } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js";
// فرمت‌بندی تمامی پارامترها
const formSchema = [
{ group: "رزولوشن و ابعاد", id: "max_width", type: "number", val: 1120 },
{ group: "رزولوشن و ابعاد", id: "max_height", type: "number", val: 1344 },
{ group: "رزولوشن و ابعاد", id: "target_mp", type: "number", val: 1.15, step: 0.05 },
{ group: "رزولوشن و ابعاد", id: "snap_multiple", type: "select", options: ["32", "64"], val: "64", castTo: "number" },
{ group: "رزولوشن و ابعاد", id: "custom_res_enabled", type: "checkbox", val: false },
{ group: "تنظیمات چهره و استایل (Targeting)", id: "mode", type: "select", options: ["anchor only", "auto face", "manual bbox"], val: "anchor only" },
{ group: "تنظیمات چهره و استایل (Targeting)", id: "face_bbox", type: "text", val: "" },
{ group: "تنظیمات چهره و استایل (Targeting)", id: "likeness_strength", type: "number", val: 0.9, step: 0.05 },
{ group: "تنظیمات چهره و استایل (Targeting)", id: "likeness_anchor_strength", type: "number", val: 0.15, step: 0.05 },
{ group: "تنظیمات چهره و استایل (Targeting)", id: "latent_anchor_strength", type: "number", val: 0.08, step: 0.01 },
{ group: "تنظیمات چهره و استایل (Targeting)", id: "first_frame_strength", type: "number", val: 0.82, step: 0.01 },
{ group: "وزن مدل‌ها (LoRA - ویدیو)", id: "sulphur_lora_strength", type: "number", val: 0.15, step: 0.05 },
{ group: "وزن مدل‌ها (LoRA - ویدیو)", id: "sulphur_v1_lora_strength", type: "number", val: 0.15, step: 0.05 },
{ group: "وزن مدل‌ها (LoRA - ویدیو)", id: "vbvr_lora_strength", type: "number", val: 0.5, step: 0.05 },
{ group: "وزن مدل‌ها (LoRA - ویدیو)", id: "dreamly_lora_strength", type: "number", val: 0.6, step: 0.05 },
{ group: "وزن مدل‌ها (LoRA - ویدیو)", id: "synth_lora_strength", type: "number", val: 0.0, step: 0.05 },
{ group: "وزن مدل‌ها (LoRA - ویدیو)", id: "plora_lora_strength", type: "number", val: 0.0, step: 0.05 },
{ group: "وزن مدل‌ها (LoRA - ویدیو)", id: "singularity_lora_strength", type: "number", val: 0.3, step: 0.05 },
{ group: "وزن مدل‌ها (LoRA - ویدیو)", id: "omninft_lora_strength", type: "number", val: 0.8, step: 0.05 },
{ group: "وزن مدل‌ها (LoRA - ویدیو)", id: "omninft_bf16_lora_strength", type: "number", val: 0.0, step: 0.05 },
{ group: "وزن مدل‌ها (LoRA - ویدیو)", id: "better_motion_lora_strength", type: "number", val: 0.0, step: 0.05 },
{ group: "وزن مدل‌ها (LoRA - ویدیو)", id: "physics_v2_lora_strength", type: "number", val: 0.0, step: 0.05 },
{ group: "وزن مدل‌ها (LoRA - ویدیو)", id: "hardcut_lora_strength", type: "number", val: 0.0, step: 0.05 },
{ group: "وزن مدل‌ها (LoRA - ویدیو)", id: "transition_lora_strength", type: "number", val: 0.15, step: 0.05 },
{ group: "وزن مدل‌ها (LoRA - صدا)", id: "sulphur_audio_strength", type: "number", val: 0.15, step: 0.05 },
{ group: "وزن مدل‌ها (LoRA - صدا)", id: "sulphur_v1_audio_strength", type: "number", val: 0.15, step: 0.05 },
{ group: "وزن مدل‌ها (LoRA - صدا)", id: "vbvr_audio_strength", type: "number", val: 0.5, step: 0.05 },
{ group: "وزن مدل‌ها (LoRA - صدا)", id: "dreamly_audio_strength", type: "number", val: 0.6, step: 0.05 },
{ group: "وزن مدل‌ها (LoRA - صدا)", id: "synth_audio_strength", type: "number", val: 0.0, step: 0.05 },
{ group: "وزن مدل‌ها (LoRA - صدا)", id: "plora_audio_strength", type: "number", val: 0.0, step: 0.05 },
{ group: "وزن مدل‌ها (LoRA - صدا)", id: "singularity_audio_strength", type: "number", val: 0.3, step: 0.05 },
{ group: "وزن مدل‌ها (LoRA - صدا)", id: "omninft_audio_strength", type: "number", val: 0.8, step: 0.05 },
{ group: "وزن مدل‌ها (LoRA - صدا)", id: "omninft_bf16_audio_strength", type: "number", val: 0.0, step: 0.05 },
{ group: "وزن مدل‌ها (LoRA - صدا)", id: "better_motion_audio_strength", type: "number", val: 0.0, step: 0.05 },
{ group: "وزن مدل‌ها (LoRA - صدا)", id: "physics_v2_audio_strength", type: "number", val: 0.0, step: 0.05 },
{ group: "وزن مدل‌ها (LoRA - صدا)", id: "hardcut_audio_strength", type: "number", val: 0.0, step: 0.05 },
{ group: "وزن مدل‌ها (LoRA - صدا)", id: "transition_audio_strength", type: "number", val: 0.0, step: 0.05 },
{ group: "تنظیمات پیشرفته هویت (Identity)", id: "cache_at_step", type: "number", val: 0 },
{ group: "تنظیمات پیشرفته هویت (Identity)", id: "cache_warmup", type: "number", val: 400 },
{ group: "تنظیمات پیشرفته هویت (Identity)", id: "energy_threshold", type: "number", val: 0.3, step: 0.05 },
{ group: "تنظیمات پیشرفته هویت (Identity)", id: "anchor_similarity_threshold", type: "number", val: 0.3, step: 0.05 },
{ group: "تنظیمات پیشرفته هویت (Identity)", id: "sigma_string", type: "text", val: "0.4824, 0.2412, 0.0" },
{ group: "تنظیمات پیشرفته هویت (Identity)", id: "skip_refine", type: "checkbox", val: false },
{ group: "مصرف منابع و Seed", id: "seed", type: "number", val: -1 },
{ group: "مصرف منابع و Seed", id: "randomize_seed", type: "checkbox", val: true },
{ group: "مصرف منابع و Seed", id: "gen_budget", type: "number", val: 0 },
{ group: "مصرف منابع و Seed", id: "enhance_budget", type: "number", val: 80 },
{ group: "مصرف منابع و Seed", id: "profile_name", type: "text", val: "" },
{ group: "حالت چند مرجعی (MSR)", id: "input_mode", type: "select", options: ["single image (i2v)", "multi-reference (MSR)", "multi-reference (original)"], val: "single image (i2v)" },
{ group: "حالت چند مرجعی (MSR)", id: "msr_ref2", type: "file", val: null },
{ group: "حالت چند مرجعی (MSR)", id: "msr_ref3", type: "file", val: null },
{ group: "حالت چند مرجعی (MSR)", id: "msr_ref4", type: "file", val: null },
{ group: "حالت چند مرجعی (MSR)", id: "msr_background", type: "file", val: null },
{ group: "حالت چند مرجعی (MSR)", id: "msr_frame_count", type: "select", options: ["17", "25", "33", "41"], val: "41", castTo: "number" },
{ group: "حالت چند مرجعی (MSR)", id: "msr_guide_strength", type: "number", val: 1.0, step: 0.05 },
{ group: "حالت چند مرجعی (MSR)", id: "msr_lora_strength", type: "number", val: 0.7, step: 0.05 },
{ group: "زنجیره صحنه و پرامپت زمانی", id: "prompt_relay_enabled", type: "checkbox", val: false },
{ group: "زنجیره صحنه و پرامپت زمانی", id: "prompt_segments", type: "textarea", val: "" },
{ group: "زنجیره صحنه و پرامپت زمانی", id: "scene_chain_enabled", type: "checkbox", val: false },
{ group: "زنجیره صحنه و پرامپت زمانی", id: "scene_chain_prompt", type: "textarea", val: "" },
{ group: "زنجیره صحنه و پرامپت زمانی", id: "scene_chain_max_scenes", type: "number", val: 2 },
{ group: "زنجیره صحنه و پرامپت زمانی", id: "scene_chain_frame_overlap", type: "number", val: 8 },
{ group: "زنجیره صحنه و پرامپت زمانی", id: "scene_chain_mid_guide", type: "checkbox", val: true },
{ group: "زنجیره صحنه و پرامپت زمانی", id: "scene_chain_mid_guide_strength", type: "number", val: 0.25, step: 0.05 },
{ group: "صدای مرجع و تنظیمات KV", id: "kv_enabled", type: "checkbox", val: false },
{ group: "صدای مرجع و تنظیمات KV", id: "kv_strength", type: "number", val: 1.0, step: 0.05 },
{ group: "صدای مرجع و تنظیمات KV", id: "audio_ref_enabled", type: "checkbox", val: false },
{ group: "صدای مرجع و تنظیمات KV", id: "audio_ref_file", type: "file", val: null },
{ group: "صدای مرجع و تنظیمات KV", id: "audio_ref_guidance_scale", type: "number", val: 3.0, step: 0.5 },
{ group: "صدای مرجع و تنظیمات KV", id: "audio_ref_stem_sep", type: "checkbox", val: false },
{ group: "صدای مرجع و تنظیمات KV", id: "audio_ref_normalize", type: "checkbox", val: true },
{ group: "کی‌فریم‌ها (Keyframes)", id: "kf_strength", type: "number", val: 0.82, step: 0.01 },
{ group: "کی‌فریم‌ها (Keyframes)", id: "kf_last_image", type: "file", val: null },
{ group: "کی‌فریم‌ها (Keyframes)", id: "kf_mid_enabled", type: "checkbox", val: false },
{ group: "کی‌فریم‌ها (Keyframes)", id: "kf_mid_1_image", type: "file", val: null },
{ group: "کی‌فریم‌ها (Keyframes)", id: "kf_mid_1_pos", type: "number", val: 50 },
{ group: "کی‌فریم‌ها (Keyframes)", id: "kf_mid_2_image", type: "file", val: null },
{ group: "کی‌فریم‌ها (Keyframes)", id: "kf_mid_2_pos", type: "number", val: 50 },
{ group: "کی‌فریم‌ها (Keyframes)", id: "kf_mid_3_image", type: "file", val: null },
{ group: "کی‌فریم‌ها (Keyframes)", id: "kf_mid_3_pos", type: "number", val: 50 },
{ group: "کی‌فریم‌ها (Keyframes)", id: "kf_mid_4_image", type: "file", val: null },
{ group: "کی‌فریم‌ها (Keyframes)", id: "kf_mid_4_pos", type: "number", val: 50 },
{ group: "کی‌فریم‌ها (Keyframes)", id: "kf_mid_5_image", type: "file", val: null },
{ group: "کی‌فریم‌ها (Keyframes)", id: "kf_mid_5_pos", type: "number", val: 50 }
];
const accordion = document.getElementById("settingsAccordion");
const groups = {};
formSchema.forEach(item => {
if (!groups[item.group]) groups[item.group] = [];
groups[item.group].push(item);
});
let index = 0;
for (const [groupName, items] of Object.entries(groups)) {
let html = `<div class="accordion-item">
<h2 class="accordion-header" id="heading${index}">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse${index}">
${groupName}
</button>
</h2>
<div id="collapse${index}" class="accordion-collapse collapse" data-bs-parent="#settingsAccordion">
<div class="accordion-body">
<div class="row">`;
items.forEach(field => {
html += `<div class="${field.type === 'textarea' || field.type === 'file' ? 'col-12' : 'col-md-6'} mb-2">`;
if (field.type === "checkbox") {
html += `
<div class="form-check mt-3">
<input class="form-check-input" type="checkbox" id="${field.id}" ${field.val ? "checked" : ""}>
<label class="form-check-label" for="${field.id}">${field.id}</label>
</div>`;
} else if (field.type === "select") {
html += `<label class="form-label">${field.id}</label>
<select id="${field.id}" class="form-select">`;
field.options.forEach(opt => {
html += `<option value="${opt}" ${opt === field.val ? "selected" : ""}>${opt}</option>`;
});
html += `</select>`;
} else if (field.type === "textarea") {
html += `<label class="form-label">${field.id}</label>
<textarea id="${field.id}" class="form-control" rows="2">${field.val}</textarea>`;
} else {
html += `<label class="form-label">${field.id}</label>
<input type="${field.type}" id="${field.id}" class="form-control" value="${field.val !== null ? field.val : ''}" ${field.step ? `step="${field.step}"` : ''}>`;
}
html += `</div>`;
});
html += `</div></div></div></div>`;
accordion.innerHTML += html;
index++;
}
document.getElementById("enhanceBtn").addEventListener("click", async () => {
const hfToken = document.getElementById("hf_token").value.trim();
const imgInput = document.getElementById("image_path");
const promptInput = document.getElementById("prompt");
const enhanceBtn = document.getElementById("enhanceBtn");
if (!imgInput.files.length) return alert("ابتدا باید یک تصویر اصلی انتخاب کنید!");
if (!promptInput.value.trim()) return alert("ابتدا یک پرامپت کوتاه برای ایده اولیه بنویسید!");
enhanceBtn.innerText = "⏳ در حال بهینه‌سازی...";
enhanceBtn.disabled = true;
try {
const client = await Client.connect("Fighterdan/LTX-2.3-10Eros_I2V", { hf_token: hfToken });
const getFile = (id) => document.getElementById(id) && document.getElementById(id).files.length ? document.getElementById(id).files[0] : null;
const enhanceParams = {
image_path: imgInput.files[0],
prompt: promptInput.value,
enhance_budget: Number(document.getElementById("enhance_budget")?.value || 80),
msr_ref2_path: getFile("msr_ref2"),
msr_ref3_path: getFile("msr_ref3"),
msr_ref4_path: getFile("msr_ref4"),
msr_bg_path: getFile("msr_background")
};
const result = await client.predict("/enhance_prompt", enhanceParams);
if (result.data && result.data[0]) {
promptInput.value = result.data[0];
alert("پرامپت با موفقیت بهینه‌سازی شد!");
}
} catch (error) {
console.error(error);
alert("خطا در بهینه‌سازی پرامپت: " + error.message);
} finally {
enhanceBtn.innerText = "✨ بهینه‌سازی خودکار پرامپت (Enhance)";
enhanceBtn.disabled = false;
}
});
document.getElementById("generateBtn").addEventListener("click", async () => {
const hfToken = document.getElementById("hf_token").value.trim();
const btn = document.getElementById("generateBtn");
const loader = document.getElementById("loader");
const statusText = document.getElementById("statusText");
const videoRes = document.getElementById("videoResult");
const seedRes = document.getElementById("seedResult");
const errorBox = document.getElementById("errorBox");
const imgInput = document.getElementById("image_path");
const promptInput = document.getElementById("prompt").value;
if (!imgInput.files.length) return alert("لطفاً تصویر اصلی را آپلود کنید.");
if (!promptInput) return alert("لطفاً پرامپت را وارد کنید.");
btn.disabled = true;
loader.style.display = "block";
videoRes.style.display = "none";
errorBox.style.display = "none";
statusText.style.display = "block";
statusText.innerText = "در حال تولید ویدیو (بسته به شلوغی سرور ممکن است چند دقیقه طول بکشد)...";
try {
const params = {
image_path: imgInput.files[0],
prompt: promptInput,
negative_prompt: document.getElementById("negative_prompt").value,
preset: document.getElementById("preset").value,
seconds: parseFloat(document.getElementById("seconds").value),
};
formSchema.forEach(field => {
const el = document.getElementById(field.id);
if (field.type === "checkbox") {
params[field.id] = el.checked;
} else if (field.type === "number") {
params[field.id] = parseFloat(el.value);
} else if (field.type === "file") {
if (el.files.length > 0) {
params[field.id] = el.files[0];
}
} else if (field.type === "select") {
params[field.id] = field.castTo === "number" ? Number(el.value) : el.value;
} else {
params[field.id] = el.value;
}
});
delete params.enhance_budget;
const client = await Client.connect("Fighterdan/LTX-2.3-10Eros_I2V", { hf_token: hfToken });
const result = await client.predict("/generate", params);
const data = result.data;
// ========================================================
// بخش هوشمند استخراج آدرس ویدیو بر اساس ساختار تو در تو
// ========================================================
let videoUrl = "";
if (data && data[0]) {
// در نسخه‌های جدید Gradio داده ویدیو در key به نام "video" قرار دارد
let fileData = data[0].video ? data[0].video : data[0];
if (fileData instanceof Blob) {
videoUrl = URL.createObjectURL(fileData);
} else if (typeof fileData === "string") {
videoUrl = fileData;
} else if (typeof fileData === "object") {
// استخراج URL یا ساخته شدن مسیر پایه
videoUrl = fileData.url || (fileData.path ? `https://fighterdan-ltx-2-3-10eros-i2v.hf.space/file=${fileData.path}` : "");
}
}
if (videoUrl) {
// موفقیت کامل
videoRes.src = videoUrl;
videoRes.style.display = "block";
statusText.innerText = "تولید با موفقیت به پایان رسید!";
// data[1] همان گزارش موفقیت (مثل 1024x1152, 25 frames) است
seedRes.innerText = "گزارش خروجی: " + (data[1] || "") + " | Seed: " + (data[2] || "");
} else {
// اگر ویدیویی یافت نشد، یعنی واقعا در پایتون اکسپشن رخ داده
statusText.style.display = "none";
errorBox.style.display = "block";
if (data && data[1]) {
errorBox.innerHTML = `<strong>خطای سرور:</strong><br><br>${data[1].replace(/\n/g, '<br>')}`;
} else {
errorBox.innerText = "خطا: ارتباط موفق بود اما ویدیویی برگشت داده نشد.";
}
}
} catch (error) {
console.error(error);
statusText.style.display = "none";
errorBox.style.display = "block";
errorBox.innerText = "خطای کلاینت/شبکه: " + error.message;
} finally {
btn.disabled = false;
loader.style.display = "none";
}
});
</script>
</body>
</html>