veo3.1-docpro / index.html
Hamed744's picture
Update index.html
00cb59b verified
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Video Studio</title>
<link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@400;500;700;800&display=swap" rel="stylesheet">
<style>
:root {
--app-font: 'Vazirmatn', sans-serif;
--app-bg: #f8f9fc;
--panel-bg: #fff;
--text-primary: #1a202c;
--text-secondary: #626f86;
--accent-primary: #4a6cfa;
--accent-secondary: #0fd4a8;
--accent-premium: #FFC107;
--danger-color: #e53e3e;
--warning-color: #FFA000;
--radius-card: 24px;
--radius-btn: 14px;
}
body { font-family: var(--app-font); background-color: var(--app-bg); color: var(--text-primary); margin: 0; padding: 2.5rem 1rem; display: flex; justify-content: center; align-items: flex-start; min-height: 100vh; }
.container { max-width: 820px; width: 100%; }
header { text-align: center; margin-bottom: 1.5rem; }
h1 { font-size: 2.8rem; font-weight: 800; background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
.subtitle { font-size: 1.1rem; color: var(--text-secondary); margin-top: 0.5rem; }
#subscription-badge { display: none; padding: 6px 16px; border-radius: 20px; font-size: 0.9em; font-weight: 700; margin-top: 1rem; letter-spacing: 0.5px; display: inline-block; }
#subscription-badge.free-badge { background: linear-gradient(45deg, #6c757d, #495057); color: white; }
#subscription-badge.paid-badge { background: linear-gradient(45deg, var(--accent-premium), #ffca2c); color: #333; }
main { padding: 2.5rem; background-color: var(--panel-bg); border-radius: var(--radius-card); box-shadow: 0 20px 25px -5px rgba(26,32,44,.07); }
.hidden { display: none !important; }
input[type="email"] { width: 100%; padding: 1rem; border-radius: 12px; border: 1px solid #e1e7ef; background-color: #f6f8fb; font-size: 1rem; box-sizing: border-box; text-align: left; direction: ltr; }
button { display: flex; align-items: center; justify-content: center; width: 100%; padding: 1rem; font-size: 1.1rem; font-weight: 700; color: #fff; border: none; border-radius: var(--radius-btn); cursor: pointer; margin-top: 1rem; background: linear-gradient(95deg, var(--accent-secondary) 0%, var(--accent-primary) 100%); transition: transform 0.2s; }
button:hover:not(:disabled) { transform: translateY(-2px); }
button:disabled { background: #8a94a6; cursor: not-allowed; }
.user-panel { background: #f6f8fb; padding: 1.5rem; border-radius: var(--radius-btn); text-align: center; margin-bottom: 2rem; border: 1px solid #eaf0ff; }
.user-info-header { display: flex; justify-content: center; align-items: center; gap: 10px; margin-bottom: 0.5rem; font-weight: 500; font-size: 1rem; color: var(--text-secondary); }
#user-email-display { font-weight: 700; font-size: 1.1rem; color: var(--accent-primary); margin-bottom: 1rem; word-break: break-all; }
#edit-email-btn { background: none; border: none; cursor: pointer; padding: 0; margin: 0; width: 24px; height: 24px; color: var(--text-secondary); transition: color 0.2s; }
#edit-email-btn:hover { color: var(--accent-primary); }
.user-panel p { margin: 0.5rem 0; font-weight: 500; font-size: 1.1rem; }
.user-panel .credit-value { font-weight: 700; color: var(--accent-primary); }
.credit-btn-wrapper { text-align: center; margin-top: 1.5rem; }
.credit-btn { display: inline-flex; align-items: center; gap: 8px; background: #eaf0ff; color: var(--accent-primary); width: auto; padding: 0.6rem 1.8rem; font-size: 0.95rem; font-weight: 700; margin: 0; border-radius: var(--radius-btn); box-shadow: 0 2px 5px rgba(74, 108, 250, 0.1); transition: all 0.2s ease-in-out; }
.credit-btn:hover { background-color: var(--accent-primary); color: white; transform: translateY(-2px); box-shadow: 0 4px 10px rgba(74, 108, 250, 0.2); }
.modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.6); backdrop-filter: blur(5px); display: flex; align-items: center; justify-content: center; z-index: 1000; opacity: 0; pointer-events: none; transition: opacity 0.3s ease-in-out; }
.modal-overlay.visible { opacity: 1; pointer-events: auto; }
.modal-content { background: #fff; padding: 2.5rem; border-radius: var(--radius-card); width: 90%; max-width: 420px; transform: scale(0.95); transition: transform 0.3s ease-in-out; text-align: center; box-shadow: 0 10px 30px rgba(0,0,0,0.1); }
.modal-overlay.visible .modal-content { transform: scale(1); }
.modal-icon { width: 50px; height: 50px; margin: 0 auto 1.5rem; background-color: rgba(255, 160, 0, 0.1); color: var(--warning-color); border-radius: 50%; display: flex; align-items: center; justify-content: center; }
.modal-icon svg { width: 28px; height: 28px; }
.modal-title { font-size: 1.5rem; font-weight: 800; color: var(--text-primary); margin-bottom: 0.5rem; }
.modal-subtitle { font-size: 1rem; color: var(--text-secondary); margin-bottom: 1rem; }
.modal-text { font-size: 1rem; font-weight: 500; color: var(--text-secondary); line-height: 1.8; margin: 0 0 2rem 0; }
#modal-premium-info { display: none; font-size: 0.9rem; color: var(--accent-primary); background-color: #eaf0ff; padding: 0.8rem; border-radius: 12px; margin-bottom: 2rem; line-height: 1.7; }
.modal-buttons { display: flex; gap: 1rem; width: 100%; }
.modal-button { flex: 1; padding: 0.8rem; font-family: var(--app-font); font-weight: 600; font-size: 1rem; cursor: pointer; transition: all 0.2s ease; border: none; border-radius: var(--radius-btn); }
.modal-button.primary { background: var(--accent-primary); color: white; }
.modal-button.primary:hover { background: #3553D6; }
.modal-button.secondary { background: #e2e8f0; color: var(--text-secondary); }
.modal-button.secondary:hover { background: #cbd5e0; }
.package { border: 2px solid #e1e7ef; padding: 1rem; border-radius: var(--radius-btn); margin-bottom: 1rem; cursor: pointer; transition: all 0.2s; text-align: center; }
.package:hover { border-color: var(--accent-primary); background: #f6f8fb; }
.package.processing { background-color: #e2e8f0; cursor: not-allowed; pointer-events: none; }
.package h4 { margin: 0 0 0.5rem 0; color: var(--text-primary); font-size: 1.2rem; }
.package .price-container { display: flex; justify-content: center; align-items: baseline; gap: 8px; }
.package .current-price { color: var(--text-primary); font-size: 1rem; font-weight: bold; }
.package .original-price { color: var(--danger-color); font-size: 0.85rem; text-decoration: line-through; }
.form-group { margin-bottom: 2rem; } .form-label { font-weight: 700; font-size: 1rem; margin-bottom: 1rem; display: block; } .tabs { display: flex; border-bottom: 1px solid #EAEFF7; margin-bottom: 2rem; } .tab-button { background: none; border: none; outline: none; cursor: pointer; padding: 1rem 1.5rem; font-size: 1rem; font-weight: 700; color: #626F86; border-bottom: 3px solid transparent; } .tab-button.active { color: var(--text-primary); border-bottom-color: var(--accent-primary); } .tab-content { display: none; } .tab-content.active { display: block; } textarea { width: 100%; padding: 1rem; border-radius: 12px; border: 1px solid #E1E7EF; background-color: #F6f8fb; font-family: var(--app-font); font-size: 1rem; box-sizing: border-box; min-height: 120px; }
#image-drop-zone { border: 2px dashed #E1E7EF; border-radius: 12px; padding: 2rem; text-align: center; cursor: pointer; display: block; width: 100%; box-sizing: border-box; }
#image-preview-container img { max-width: 100%; max-height: 200px; border-radius: 12px; margin-top: 1rem; } #results-container { min-height: 300px; position: relative; padding: 1rem; background-color: #F6F8FB; border-radius: var(--radius-card); display: flex; flex-direction: column; align-items: center; justify-content: center; margin-top: 2.5rem; } #status { display: none; width: 100%; text-align: center; font-weight: 600; padding: 1rem; border-radius: 12px; margin-bottom: 1rem; } .status-error { background-color: rgba(229, 62, 62, 0.1); color: var(--danger-color); }
#video-container { width: 100%; text-align: center; } /* تغییر برای وسط‌چین کردن محتوا */
#video-container video { width: 100%; border-radius: var(--radius-card); display: block; }
#loading-animation { text-align: center; padding: 2rem; width: 100%; box-sizing: border-box;}
#loading-animation p { font-weight: 600; color: var(--text-secondary); margin-bottom: 1.5rem; }
.progress-bar-container { width: 100%; background-color: #e2e8f0; border-radius: 10px; overflow: hidden; }
.progress-bar { height: 12px; width: 0%; background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary)); border-radius: 10px; animation: fillProgress 180s linear forwards; }
@keyframes fillProgress { from { width: 0%; } to { width: 100%; } }
.download-btn {
background: var(--accent-primary);
color: white;
width: auto;
padding: 0.8rem 2rem;
margin-top: 1.5rem;
font-size: 1rem;
display: inline-flex;
gap: 8px;
}
.download-btn:hover { background: #3553D6; }
</style>
</head>
<body>
<div class="container">
<header>
<h1>استودیو ویدیو هوش مصنوعی Veo3.1</h1>
<p class="subtitle">ایده‌های خود را به ویدیوهای خیره‌کننده تبدیل کنید.</p>
<div id="subscription-badge"></div>
</header>
<main>
<div id="login-section">
<p>برای استفاده از سرویس، لطفا ایمیل خود را وارد کنید.</p>
<div class="form-group">
<input type="email" id="email-input" placeholder="[email protected]" required>
</div>
<button id="login-btn">ورود / ثبت‌نام</button>
</div>
<div id="app-section" class="hidden">
<div class="user-panel">
<div class="user-info-header">
<span>کاربر:</span>
<button id="edit-email-btn" title="ویرایش ایمیل">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
</button>
</div>
<div id="user-email-display"></div>
<p>اعتبار شما: <span id="user-credits-display" class="credit-value">0</span></p>
<div class="credit-btn-wrapper">
<button id="add-credit-btn" class="credit-btn">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14"/><path d="M5 12h14"/></svg>
افزایش اعتبار
</button>
</div>
</div>
<div class="tabs">
<button class="tab-button active" onclick="openTab(event, 'text-to-video')">متن به ویدیو</button>
<button class="tab-button" onclick="openTab(event, 'image-to-video')">تصویر به ویدیو</button>
</div>
<div id="text-to-video" class="tab-content active">
<form id="text-form">
<div class="form-group"><label for="text-prompt" class="form-label">دستور ساخت</label><textarea id="text-prompt" name="prompt" rows="5" placeholder="مثال: یک گربه فضانورد که در میان ستارگان شناور است..." required></textarea></div>
<button type="submit"><span>ساخت ویدیو (کسر ۱ اعتبار)</span></button>
</form>
</div>
<div id="image-to-video" class="tab-content">
<form id="image-form">
<div class="form-group">
<label class="form-label">آپلود تصویر</label>
<label id="image-drop-zone" for="imageFileInput"><p>تصویر را اینجا بکشید یا برای انتخاب کلیک کنید</p></label>
<input type="file" id="imageFileInput" name="image" accept="image/*" hidden required>
<div id="image-preview-container"></div>
</div>
<div class="form-group"><label for="image-prompt" class="form-label">دستور حرکت</label><textarea id="image-prompt" name="prompt" rows="4" placeholder="مثال: زوم آهسته به بیرون در حالی که برگ‌ها می‌رقصند..." required></textarea></div>
<button type="submit"><span>متحرک‌سازی (کسر ۱ اعتبار)</span></button>
</form>
</div>
<div id="results-container">
<div id="status"></div><div id="video-container"></div>
</div>
</div>
</main>
</div>
<!-- Credit Purchase Modal -->
<div id="creditModal" class="modal-overlay">
<div class="modal-content">
<h3 id="modal-title">افزایش اعتبار</h3>
<p id="modal-subtitle">هر یک اعتبار = یک ویدیو ۸ ثانیه‌ای</p>
<p id="modal-premium-info">شما کاربر اشتراکی هستید به همین خاطر قیمت‌ها برای شما با تخفیف لحاظ شده است. برای کاربرانی که اشتراک ندارند تخفیف وجود ندارد.</p>
<div class="package" data-package="1-credit"><h4>۱ اعتبار</h4><div class="price-container"><span class="original-price"></span><span class="current-price"></span></div></div>
<div class="package" data-package="5-credits"><h4>۵ اعتبار</h4><div class="price-container"><span class="original-price"></span><span class="current-price"></span></div></div>
<div class="package" data-package="50-credits"><h4>۵۰ اعتبار</h4><div class="price-container"><span class="original-price"></span><span class="current-price"></span></div></div>
<button id="close-modal-btn" style="background: #6c757d; margin-top: 2rem;">انصراف</button>
</div>
</div>
<!-- Payment Confirmation Modal -->
<div id="payment-confirm-modal" class="modal-overlay">
<div class="modal-content">
<div class="modal-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></div>
<h3 class="modal-title" style="font-size: 1.3rem;">نکات مهم قبل از پرداخت</h3>
<p class="modal-text">
قبل از پرداخت‌ فیلترشکن خود را <strong>خاموش کنید</strong>.
<br><br>
بعد از پرداخت موفق، برنامه را یک بار کامل ببندید و دوباره باز کنید تا اعتبار به حساب شما اضافه شود.
<br><br>
اعتبارات خریداری شده هم برای <strong>Veo3.1</strong> قابل استفاده است هم برای <strong>Sora</strong>.
</p>
<div class="modal-buttons">
<button class="modal-button secondary" id="cancel-payment-btn">انصراف</button>
<button class="modal-button primary" id="proceed-payment-btn">تایید و پرداخت</button>
</div>
</div>
</div>
<!-- Edit Email Confirmation Modal -->
<div id="edit-email-confirm-modal" class="modal-overlay">
<div class="modal-content">
<div class="modal-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg></div>
<h3 class="modal-title" style="font-size: 1.3rem;">تغییر ایمیل</h3>
<p class="modal-text">آیا از تغییر ایمیل خود و ورود با حساب کاربری جدید مطمئن هستید؟</p>
<div class="modal-buttons">
<button class="modal-button secondary" id="cancel-edit-email-btn">انصراف</button>
<button class="modal-button primary" id="proceed-edit-email-btn">تایید و ویرایش</button>
</div>
</div>
</div>
<script>
const loginSection = document.getElementById('login-section');
const appSection = document.getElementById('app-section');
const emailInput = document.getElementById('email-input');
const loginBtn = document.getElementById('login-btn');
const userEmailDisplay = document.getElementById('user-email-display');
const userCreditsDisplay = document.getElementById('user-credits-display');
const editEmailBtn = document.getElementById('edit-email-btn');
const statusDiv = document.getElementById('status');
const videoContainer = document.getElementById('video-container');
const creditModal = document.getElementById('creditModal');
const subscriptionBadge = document.getElementById('subscription-badge');
const modalPremiumInfo = document.getElementById('modal-premium-info');
const paymentConfirmModal = document.getElementById('payment-confirm-modal');
const proceedPaymentBtn = document.getElementById('proceed-payment-btn');
const cancelPaymentBtn = document.getElementById('cancel-payment-btn');
const editEmailConfirmModal = document.getElementById('edit-email-confirm-modal');
const proceedEditEmailBtn = document.getElementById('proceed-edit-email-btn');
const cancelEditEmailBtn = document.getElementById('cancel-edit-email-btn');
let currentUserEmail = '';
let userSubscriptionStatus = 'free';
let pendingPaymentUrl = '';
const PREMIUM_PAGE_ID = '1149636';
const API_URL = 'https://www.aisada.ir/hf/api.php';
const prices = {
free: { credit: 25000, pack50: 800000 },
paid: { credit: 20000, pack50: 700000 }
};
function updateUIForSubscription() {
if (userSubscriptionStatus === 'paid') {
subscriptionBadge.textContent = 'کاربر اشتراکی';
subscriptionBadge.className = 'paid-badge';
modalPremiumInfo.style.display = 'block';
} else {
subscriptionBadge.textContent = 'کاربر رایگان';
subscriptionBadge.className = 'free-badge';
modalPremiumInfo.style.display = 'none';
}
updatePricingModal();
}
function updatePricingModal() {
const isPaid = userSubscriptionStatus === 'paid';
const pricePerCredit = isPaid ? prices.paid.credit : prices.free.credit;
const formatPrice = (price) => price.toLocaleString('fa-IR');
const pkg1 = document.querySelector('.package[data-package="1-credit"]');
pkg1.querySelector('.current-price').textContent = `${formatPrice(pricePerCredit)} تومان`;
const pkg1Original = pkg1.querySelector('.original-price');
if (isPaid) { pkg1Original.textContent = `${formatPrice(prices.free.credit)} تومان`; pkg1Original.style.display = 'inline'; } else { pkg1Original.style.display = 'none'; }
const pkg5 = document.querySelector('.package[data-package="5-credits"]');
pkg5.querySelector('.current-price').textContent = `${formatPrice(pricePerCredit * 5)} تومان`;
const pkg5Original = pkg5.querySelector('.original-price');
if (isPaid) { pkg5Original.textContent = `${formatPrice(prices.free.credit * 5)} تومان`; pkg5Original.style.display = 'inline'; } else { pkg5Original.style.display = 'none'; }
const pkg50 = document.querySelector('.package[data-package="50-credits"]');
pkg50.querySelector('.current-price').textContent = `${formatPrice(isPaid ? prices.paid.pack50 : prices.free.pack50)} تومان`;
const pkg50Original = pkg50.querySelector('.original-price');
if (isPaid) { pkg50Original.textContent = `${formatPrice(prices.free.pack50)} تومان`; pkg50Original.style.display = 'inline'; } else { pkg50Original.style.display = 'none'; }
}
window.addEventListener('message', (event) => {
if (event.data?.type === 'USER_DATA_RESPONSE') {
let isPaidUser = false;
if (event.data.payload) {
try {
const userObject = JSON.parse(event.data.payload);
isPaidUser = userObject?.isLogin && userObject.accessible_pages?.some(p => p == PREMIUM_PAGE_ID);
} catch (e) { console.error("Failed to parse user data from parent."); }
}
userSubscriptionStatus = isPaidUser ? 'paid' : 'free';
updateUIForSubscription();
}
});
window.addEventListener('DOMContentLoaded', () => {
parent.postMessage({ type: 'REQUEST_USER_DATA' }, '*');
const savedEmail = localStorage.getItem('loggedInEmail');
if (savedEmail) { emailInput.value = savedEmail; checkUserStatus(savedEmail); }
updateUIForSubscription();
});
document.querySelectorAll('.package').forEach(pkg => {
pkg.addEventListener('click', async (e) => {
const button = e.currentTarget;
if (button.classList.contains('processing')) return;
document.querySelectorAll('.package').forEach(p => p.classList.add('processing'));
const originalHTML = button.innerHTML;
button.innerHTML = '<h4>در حال ایجاد لینک...</h4>';
try {
const response = await fetch(API_URL, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ action: 'request_payment', email: currentUserEmail, package: button.dataset.package, user_status: userSubscriptionStatus })
});
if (!response.ok) throw new Error('Payment request failed.');
const data = await response.json();
if (data.payment_url) {
pendingPaymentUrl = data.payment_url;
creditModal.classList.remove('visible');
paymentConfirmModal.classList.add('visible');
} else { alert('خطا: ' + (data.error || 'نامشخص')); }
} catch (error) {
alert('خطا در ارتباط با سرور پرداخت.');
} finally {
document.querySelectorAll('.package').forEach(p => p.classList.remove('processing'));
button.innerHTML = originalHTML;
}
});
});
proceedPaymentBtn.addEventListener('click', () => {
if (pendingPaymentUrl) {
parent.postMessage({ type: 'NAVIGATE_TO_URL', url: pendingPaymentUrl }, '*');
paymentConfirmModal.classList.remove('visible');
pendingPaymentUrl = '';
}
});
cancelPaymentBtn.addEventListener('click', () => {
paymentConfirmModal.classList.remove('visible');
pendingPaymentUrl = '';
creditModal.classList.add('visible');
});
paymentConfirmModal.addEventListener('click', (e) => { if (e.target === paymentConfirmModal) { paymentConfirmModal.classList.remove('visible'); pendingPaymentUrl = ''; } });
editEmailBtn.addEventListener('click', () => {
editEmailConfirmModal.classList.add('visible');
});
proceedEditEmailBtn.addEventListener('click', () => {
editEmailConfirmModal.classList.remove('visible');
appSection.classList.add('hidden');
loginSection.classList.remove('hidden');
emailInput.value = currentUserEmail;
emailInput.focus();
});
cancelEditEmailBtn.addEventListener('click', () => {
editEmailConfirmModal.classList.remove('visible');
});
editEmailConfirmModal.addEventListener('click', (e) => { if (e.target === editEmailConfirmModal) { editEmailConfirmModal.classList.remove('visible'); } });
async function checkUserStatus(email) { if (!email) return; currentUserEmail = email; localStorage.setItem('loggedInEmail', email); userEmailDisplay.textContent = email; loginSection.classList.add('hidden'); appSection.classList.remove('hidden'); userCreditsDisplay.textContent = '...'; try { const response = await fetch(`${API_URL}?action=check_credits&email=${encodeURIComponent(email)}`); if (!response.ok) throw new Error('Network error'); const data = await response.json(); userCreditsDisplay.textContent = data.credits ?? '0'; } catch (error) { userCreditsDisplay.textContent = 'خطا'; } }
async function handleFormSubmit(event, endpoint) {
event.preventDefault();
const form = event.target;
const button = form.querySelector('button[type="submit"]');
button.disabled = true;
button.querySelector('span').textContent = '۱. دریافت مجوز ساخت...';
statusDiv.style.display = 'none';
videoContainer.innerHTML = '';
let jwt_token = '';
try {
const response = await fetch(API_URL, { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ action: 'request_generation_token', email: currentUserEmail }) });
const tokenData = await response.json();
if (!response.ok || tokenData.status !== 'ok') { throw new Error(tokenData.message || 'اعتبار کافی نیست یا خطای سرور رخ داده است.'); }
jwt_token = tokenData.token;
button.querySelector('span').textContent = '۲. ارسال به سرور ساخت...';
} catch (error) {
showStatus(`❌ ${error.message}`, true);
button.disabled = false;
button.querySelector('span').textContent = form.id === 'text-form' ? 'ساخت ویدیو (کسر ۱ اعتبار)' : 'متحرک‌سازی (کسر ۱ اعتبار)';
checkUserStatus(currentUserEmail);
return;
}
const formData = new FormData(form);
formData.append('token', jwt_token);
try {
videoContainer.innerHTML = `<div id="loading-animation"><p>در حال پردازش هوش مصنوعی... این فرآیند ممکن است تا ۳ دقیقه طول بکشد.</p><div class="progress-bar-container"><div class="progress-bar"></div></div></div>`;
const response = await fetch(endpoint, { method: 'POST', body: formData });
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.detail || `خطای سرور ساخت: ${response.status}`);
}
const deductionToken = response.headers.get('X-Deduction-Token');
const videoBlob = await response.blob();
const videoUrl = URL.createObjectURL(videoBlob);
const videoElement = document.createElement('video');
videoElement.src = videoUrl;
videoElement.controls = true;
videoElement.autoplay = true;
videoElement.loop = true;
const downloadButton = document.createElement('button');
downloadButton.className = 'download-btn';
downloadButton.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg><span>دانلود ویدیو</span>`;
downloadButton.onclick = () => {
parent.postMessage({ type: 'DOWNLOAD_REQUEST', url: videoUrl }, '*');
};
videoElement.addEventListener('canplay', () => {
if (deductionToken && !videoElement.dataset.deducted) {
videoElement.dataset.deducted = 'true';
finalizeDeduction(deductionToken);
}
}, { once: true });
videoContainer.innerHTML = '';
videoContainer.appendChild(videoElement);
videoContainer.appendChild(downloadButton);
showStatus('✅ ویدیو با موفقیت ساخته شد!', false);
} catch (error) {
showStatus(`❌ ${error.message} (اعتباری کسر نشد)`, true);
} finally {
button.disabled = false;
button.querySelector('span').textContent = form.id === 'text-form' ? 'ساخت ویدیو (کسر ۱ اعتبار)' : 'متحرک‌سازی (کسر ۱ اعتبار)';
}
}
async function finalizeDeduction(deductionToken) {
console.log("Finalizing deduction with token:", deductionToken);
try {
const response = await fetch(API_URL, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ action: 'finalize_deduction', deduction_token: deductionToken })
});
const result = await response.json();
if (result.status === 'success') {
showStatus('✅ ویدیو با موفقیت ساخته شد! یک اعتبار شما کسر گردید. ویدیو رو دانلود و ذخیره کنید چون در اینجا باقی نمی ماند.', false);
} else {
showStatus(`✅ ویدیو ساخته شد اما در کسر اعتبار خطا رخ داد: ${result.message}`, true);
}
} catch (error) {
console.error("Error sending deduction request:", error);
} finally {
checkUserStatus(currentUserEmail);
}
}
loginBtn.addEventListener('click', () => { const email = emailInput.value.trim().toLowerCase(); if (email && email.includes('@')) checkUserStatus(email); else alert('لطفا یک ایمیل معتبر وارد کنید.'); });
document.getElementById('add-credit-btn').addEventListener('click', () => creditModal.classList.add('visible'));
document.getElementById('close-modal-btn').addEventListener('click', () => creditModal.classList.remove('visible'));
creditModal.addEventListener('click', (e) => { if(e.target === creditModal) creditModal.classList.remove('visible'); });
document.getElementById('text-form').addEventListener('submit', (e) => handleFormSubmit(e, '/text-to-video/'));
document.getElementById('image-form').addEventListener('submit', (e) => handleFormSubmit(e, '/image-to-video/'));
function showStatus(message, isError = false) { if (isError) { const loadingAnim = document.getElementById('loading-animation'); if (loadingAnim) loadingAnim.remove(); } statusDiv.innerHTML = message; statusDiv.style.display = 'block'; statusDiv.className = isError ? 'status-error' : ''; }
function openTab(evt, tabName) { document.querySelectorAll(".tab-content, .tab-button").forEach(el => el.classList.remove("active")); document.getElementById(tabName).classList.add("active"); evt.currentTarget.classList.add("active"); }
const dropZone = document.getElementById('image-drop-zone'); const fileInput = document.getElementById('imageFileInput'); const previewContainer = document.getElementById('image-preview-container'); ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => dropZone.addEventListener(eventName, e => { e.preventDefault(); e.stopPropagation(); })); dropZone.addEventListener('drop', e => { if (e.dataTransfer.files.length) { fileInput.files = e.dataTransfer.files; handleFiles(fileInput.files); } }); fileInput.addEventListener('change', () => handleFiles(fileInput.files));
function handleFiles(files) { const file = files[0]; if (!file || !file.type.startsWith('image/')) { previewContainer.innerHTML = ''; dropZone.querySelector('p').textContent = "فرمت نامعتبر است. لطفاً تصویر انتخاب کنید."; fileInput.value = ''; return; } const img = document.createElement('img'); img.src = URL.createObjectURL(file); previewContainer.innerHTML = ''; previewContainer.appendChild(img); dropZone.querySelector('p').textContent = `فایل انتخاب شد: ${file.name}`; }
</script>
</body>
</html>