| <!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]) { |
| |
| 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") { |
| |
| 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 = "تولید با موفقیت به پایان رسید!"; |
| |
| 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> |