Update app.py
Browse files
app.py
CHANGED
|
@@ -150,7 +150,7 @@ async def ui():
|
|
| 150 |
<head>
|
| 151 |
<meta charset="UTF-8">
|
| 152 |
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
| 153 |
-
<title>زیرنویسساز
|
| 154 |
<link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@400;600;800&display=swap" rel="stylesheet">
|
| 155 |
<style>
|
| 156 |
:root {
|
|
@@ -179,7 +179,7 @@ async def ui():
|
|
| 179 |
.upload-icon { font-size: 2.5rem; margin-bottom: 10px; display: block; }
|
| 180 |
|
| 181 |
/* Editor */
|
| 182 |
-
.segment-list { max-height:
|
| 183 |
.segment-item { background: #fff; border: 1px solid var(--border); border-radius: 10px; padding: 12px; margin-bottom: 10px; }
|
| 184 |
.time-tag { font-size: 0.7rem; background: #e5e7eb; color: #374151; padding: 2px 6px; border-radius: 4px; display: inline-block; margin-bottom: 5px; }
|
| 185 |
.text-edit { width: 100%; border: none; background: transparent; font-family: inherit; font-size: 1rem; padding: 0; resize: none; border-bottom: 1px dashed #ccc; }
|
|
@@ -193,7 +193,7 @@ async def ui():
|
|
| 193 |
input[type="range"] { width: 100%; accent-color: var(--primary); }
|
| 194 |
select { width: 100%; padding: 12px; border-radius: 10px; border: 1px solid var(--border); font-family: inherit; background: #fff; font-size: 0.9rem; }
|
| 195 |
|
| 196 |
-
.action-btn { width: 100%; padding: 16px; background: linear-gradient(135deg, #4f46e5, #4338ca); color: white; border: none; border-radius: 14px; font-weight: 800; font-size: 1.1rem; cursor: pointer; box-shadow: 0 4px 15px rgba(79, 70, 229, 0.3); }
|
| 197 |
.action-btn:active { transform: scale(0.98); }
|
| 198 |
|
| 199 |
/* Loader */
|
|
@@ -201,9 +201,14 @@ async def ui():
|
|
| 201 |
.spinner { width: 50px; height: 50px; border: 5px solid #e5e7eb; border-top: 5px solid var(--primary); border-radius: 50%; animation: spin 0.8s linear infinite; margin-bottom: 20px; }
|
| 202 |
@keyframes spin { 100% { transform: rotate(360deg); } }
|
| 203 |
|
| 204 |
-
/* Result */
|
| 205 |
-
.result-
|
| 206 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 207 |
.dl-link { display: block; margin-top: 15px; padding: 15px; background: #10b981; color: white; text-decoration: none; border-radius: 12px; font-weight: bold; font-size: 1.1rem; }
|
| 208 |
|
| 209 |
.hidden { display: none !important; }
|
|
@@ -224,7 +229,7 @@ async def ui():
|
|
| 224 |
<h2>1. انتخاب ویدیو</h2>
|
| 225 |
<div class="upload-box" onclick="document.getElementById('videoInput').click()">
|
| 226 |
<span class="upload-icon">📹</span>
|
| 227 |
-
<p id="uploadMsg">ویدیو را
|
| 228 |
<input type="file" id="videoInput" hidden accept="video/*" onchange="uploadVideo()">
|
| 229 |
</div>
|
| 230 |
</div>
|
|
@@ -235,7 +240,7 @@ async def ui():
|
|
| 235 |
</div>
|
| 236 |
|
| 237 |
<div class="card disabled-panel" id="cardSettings">
|
| 238 |
-
<h2>3. تنظیمات
|
| 239 |
|
| 240 |
<div class="settings-grid">
|
| 241 |
<div class="control-group">
|
|
@@ -251,7 +256,7 @@ async def ui():
|
|
| 251 |
<div class="control-group">
|
| 252 |
<label>استایل کادر</label>
|
| 253 |
<select id="backType">
|
| 254 |
-
<option value="box_solid">کادر پررنگ (هرمزی)</option>
|
| 255 |
<option value="box_transparent">کادر شفاف (سینمایی)</option>
|
| 256 |
<option value="outline">فقط حاشیه (Outline)</option>
|
| 257 |
</select>
|
|
@@ -277,14 +282,12 @@ async def ui():
|
|
| 277 |
</div>
|
| 278 |
|
| 279 |
<button id="burnBtn" class="action-btn" onclick="burnProcess()">ساخت ویدیو نهایی ✨</button>
|
| 280 |
-
</div>
|
| 281 |
|
| 282 |
-
|
| 283 |
-
<
|
| 284 |
-
|
| 285 |
<video id="previewVideo" controls playsinline></video>
|
| 286 |
<a id="downloadLink" class="dl-link" href="#" download>دانلود ویدیو</a>
|
| 287 |
-
<button onclick="location.reload()" style="margin-top:20px; background:none; border:none; color:#6b7280; padding:10px;">شروع دوباره</button>
|
| 288 |
</div>
|
| 289 |
</div>
|
| 290 |
|
|
@@ -294,24 +297,21 @@ async def ui():
|
|
| 294 |
let globalFileId = null;
|
| 295 |
let globalSegments = [];
|
| 296 |
|
| 297 |
-
// --- مدیریت حافظه مرورگر
|
| 298 |
const settingIds = ['primaryColor', 'outlineColor', 'backType', 'fontSelect', 'fontSize', 'marginV'];
|
| 299 |
-
|
| 300 |
-
// لود کردن تنظیمات هنگام باز شدن صفحه
|
| 301 |
document.addEventListener('DOMContentLoaded', () => {
|
| 302 |
settingIds.forEach(id => {
|
| 303 |
const savedVal = localStorage.getItem(id);
|
| 304 |
if (savedVal) {
|
| 305 |
const el = document.getElementById(id);
|
| 306 |
el.value = savedVal;
|
| 307 |
-
// بروزرسانی عدد اسلایدرها
|
| 308 |
if(id === 'fontSize') updateVal('fontSizeVal', savedVal);
|
| 309 |
if(id === 'marginV') updateVal('marginVal', savedVal);
|
| 310 |
}
|
| 311 |
});
|
| 312 |
});
|
| 313 |
|
| 314 |
-
// ذخیره تنظیمات هنگام تغییر
|
| 315 |
settingIds.forEach(id => {
|
| 316 |
document.getElementById(id).addEventListener('input', (e) => {
|
| 317 |
localStorage.setItem(id, e.target.value);
|
|
@@ -332,7 +332,7 @@ async def ui():
|
|
| 332 |
const fileInput = document.getElementById('videoInput');
|
| 333 |
if (!fileInput.files[0]) return;
|
| 334 |
|
| 335 |
-
document.getElementById('
|
| 336 |
document.getElementById('uploadMsg').innerText = fileInput.files[0].name;
|
| 337 |
|
| 338 |
showLoader("در حال آپلود و آنالیز هوشمند...");
|
|
@@ -378,7 +378,7 @@ async def ui():
|
|
| 378 |
}
|
| 379 |
|
| 380 |
async function burnProcess() {
|
| 381 |
-
showLoader("در حال ساخت
|
| 382 |
|
| 383 |
const payload = {
|
| 384 |
file_id: globalFileId,
|
|
@@ -404,15 +404,16 @@ async def ui():
|
|
| 404 |
|
| 405 |
if (data.error) throw new Error(data.error);
|
| 406 |
|
|
|
|
| 407 |
const videoEl = document.getElementById('previewVideo');
|
| 408 |
-
|
|
|
|
|
|
|
| 409 |
document.getElementById('downloadLink').href = data.url;
|
| 410 |
|
| 411 |
-
|
| 412 |
-
document.getElementById('
|
| 413 |
-
document.getElementById('
|
| 414 |
-
document.getElementById('cardEditor').classList.add('hidden');
|
| 415 |
-
document.getElementById('cardSettings').classList.add('hidden');
|
| 416 |
|
| 417 |
} catch (e) {
|
| 418 |
alert("خطا در ساخت ویدیو: " + e.message);
|
|
|
|
| 150 |
<head>
|
| 151 |
<meta charset="UTF-8">
|
| 152 |
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
| 153 |
+
<title>زیرنویسساز حرفهای</title>
|
| 154 |
<link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@400;600;800&display=swap" rel="stylesheet">
|
| 155 |
<style>
|
| 156 |
:root {
|
|
|
|
| 179 |
.upload-icon { font-size: 2.5rem; margin-bottom: 10px; display: block; }
|
| 180 |
|
| 181 |
/* Editor */
|
| 182 |
+
.segment-list { max-height: 300px; overflow-y: auto; padding: 5px; background: #f9fafb; border-radius: 8px; border: 1px solid var(--border); }
|
| 183 |
.segment-item { background: #fff; border: 1px solid var(--border); border-radius: 10px; padding: 12px; margin-bottom: 10px; }
|
| 184 |
.time-tag { font-size: 0.7rem; background: #e5e7eb; color: #374151; padding: 2px 6px; border-radius: 4px; display: inline-block; margin-bottom: 5px; }
|
| 185 |
.text-edit { width: 100%; border: none; background: transparent; font-family: inherit; font-size: 1rem; padding: 0; resize: none; border-bottom: 1px dashed #ccc; }
|
|
|
|
| 193 |
input[type="range"] { width: 100%; accent-color: var(--primary); }
|
| 194 |
select { width: 100%; padding: 12px; border-radius: 10px; border: 1px solid var(--border); font-family: inherit; background: #fff; font-size: 0.9rem; }
|
| 195 |
|
| 196 |
+
.action-btn { width: 100%; padding: 16px; background: linear-gradient(135deg, #4f46e5, #4338ca); color: white; border: none; border-radius: 14px; font-weight: 800; font-size: 1.1rem; cursor: pointer; box-shadow: 0 4px 15px rgba(79, 70, 229, 0.3); margin-bottom: 15px; }
|
| 197 |
.action-btn:active { transform: scale(0.98); }
|
| 198 |
|
| 199 |
/* Loader */
|
|
|
|
| 201 |
.spinner { width: 50px; height: 50px; border: 5px solid #e5e7eb; border-top: 5px solid var(--primary); border-radius: 50%; animation: spin 0.8s linear infinite; margin-bottom: 20px; }
|
| 202 |
@keyframes spin { 100% { transform: rotate(360deg); } }
|
| 203 |
|
| 204 |
+
/* Result Box */
|
| 205 |
+
.result-container {
|
| 206 |
+
margin-top: 20px;
|
| 207 |
+
padding-top: 20px;
|
| 208 |
+
border-top: 2px dashed #e5e7eb;
|
| 209 |
+
text-align: center;
|
| 210 |
+
}
|
| 211 |
+
video { width: 100%; border-radius: 12px; background: #000; margin-top: 10px; box-shadow: 0 10px 30px -10px rgba(0,0,0,0.2); }
|
| 212 |
.dl-link { display: block; margin-top: 15px; padding: 15px; background: #10b981; color: white; text-decoration: none; border-radius: 12px; font-weight: bold; font-size: 1.1rem; }
|
| 213 |
|
| 214 |
.hidden { display: none !important; }
|
|
|
|
| 229 |
<h2>1. انتخاب ویدیو</h2>
|
| 230 |
<div class="upload-box" onclick="document.getElementById('videoInput').click()">
|
| 231 |
<span class="upload-icon">📹</span>
|
| 232 |
+
<p id="uploadMsg">ویدیو را انتخاب کنید</p>
|
| 233 |
<input type="file" id="videoInput" hidden accept="video/*" onchange="uploadVideo()">
|
| 234 |
</div>
|
| 235 |
</div>
|
|
|
|
| 240 |
</div>
|
| 241 |
|
| 242 |
<div class="card disabled-panel" id="cardSettings">
|
| 243 |
+
<h2>3. تنظیمات و خروجی</h2>
|
| 244 |
|
| 245 |
<div class="settings-grid">
|
| 246 |
<div class="control-group">
|
|
|
|
| 256 |
<div class="control-group">
|
| 257 |
<label>استایل کادر</label>
|
| 258 |
<select id="backType">
|
| 259 |
+
<option value="box_solid" selected>کادر پررنگ (هرمزی)</option>
|
| 260 |
<option value="box_transparent">کادر شفاف (سینمایی)</option>
|
| 261 |
<option value="outline">فقط حاشیه (Outline)</option>
|
| 262 |
</select>
|
|
|
|
| 282 |
</div>
|
| 283 |
|
| 284 |
<button id="burnBtn" class="action-btn" onclick="burnProcess()">ساخت ویدیو نهایی ✨</button>
|
|
|
|
| 285 |
|
| 286 |
+
<!-- محل نمایش ویدیو خروجی (زیر دکمه) -->
|
| 287 |
+
<div id="resultSection" class="result-container hidden">
|
| 288 |
+
<h3 style="color:#10b981; margin:0 0 10px 0;">✅ ویدیو آماده شد!</h3>
|
| 289 |
<video id="previewVideo" controls playsinline></video>
|
| 290 |
<a id="downloadLink" class="dl-link" href="#" download>دانلود ویدیو</a>
|
|
|
|
| 291 |
</div>
|
| 292 |
</div>
|
| 293 |
|
|
|
|
| 297 |
let globalFileId = null;
|
| 298 |
let globalSegments = [];
|
| 299 |
|
| 300 |
+
// --- مدیریت حافظه مرورگر ---
|
| 301 |
const settingIds = ['primaryColor', 'outlineColor', 'backType', 'fontSelect', 'fontSize', 'marginV'];
|
| 302 |
+
|
|
|
|
| 303 |
document.addEventListener('DOMContentLoaded', () => {
|
| 304 |
settingIds.forEach(id => {
|
| 305 |
const savedVal = localStorage.getItem(id);
|
| 306 |
if (savedVal) {
|
| 307 |
const el = document.getElementById(id);
|
| 308 |
el.value = savedVal;
|
|
|
|
| 309 |
if(id === 'fontSize') updateVal('fontSizeVal', savedVal);
|
| 310 |
if(id === 'marginV') updateVal('marginVal', savedVal);
|
| 311 |
}
|
| 312 |
});
|
| 313 |
});
|
| 314 |
|
|
|
|
| 315 |
settingIds.forEach(id => {
|
| 316 |
document.getElementById(id).addEventListener('input', (e) => {
|
| 317 |
localStorage.setItem(id, e.target.value);
|
|
|
|
| 332 |
const fileInput = document.getElementById('videoInput');
|
| 333 |
if (!fileInput.files[0]) return;
|
| 334 |
|
| 335 |
+
document.getElementById('resultSection').classList.add('hidden');
|
| 336 |
document.getElementById('uploadMsg').innerText = fileInput.files[0].name;
|
| 337 |
|
| 338 |
showLoader("در حال آپلود و آنالیز هوشمند...");
|
|
|
|
| 378 |
}
|
| 379 |
|
| 380 |
async function burnProcess() {
|
| 381 |
+
showLoader("در حال ساخت ویدیو...");
|
| 382 |
|
| 383 |
const payload = {
|
| 384 |
file_id: globalFileId,
|
|
|
|
| 404 |
|
| 405 |
if (data.error) throw new Error(data.error);
|
| 406 |
|
| 407 |
+
// نمایش ویدیو
|
| 408 |
const videoEl = document.getElementById('previewVideo');
|
| 409 |
+
// اضافه کردن تایماستمپ برای جلوگیری از کش شدن ویدیوی قبلی
|
| 410 |
+
videoEl.src = data.url + "?t=" + new Date().getTime();
|
| 411 |
+
|
| 412 |
document.getElementById('downloadLink').href = data.url;
|
| 413 |
|
| 414 |
+
// باز کردن بخش نتیجه (بدون بستن ادیتور)
|
| 415 |
+
document.getElementById('resultSection').classList.remove('hidden');
|
| 416 |
+
document.getElementById('resultSection').scrollIntoView({behavior: 'smooth'});
|
|
|
|
|
|
|
| 417 |
|
| 418 |
} catch (e) {
|
| 419 |
alert("خطا در ساخت ویدیو: " + e.message);
|