Opera8 commited on
Commit
c1c9940
·
verified ·
1 Parent(s): d5b01e8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +28 -27
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>زیرنویس‌ساز موبایل</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: 350px; 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,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-box { text-align: center; }
206
- video { width: 100%; border-radius: 12px; background: #000; margin-top: 10px; }
 
 
 
 
 
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">ویدیو را از گالری انتخاب کنید</p>
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. تنظیمات ظاهری</h2>
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
- <div class="card hidden" id="cardResult">
283
- <h2>🎉 ویدیو آماده شد!</h2>
284
- <div class="result-box">
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
- // --- مدیریت حافظه مرورگر (LocalStorage) ---
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('cardResult').classList.add('hidden');
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
- videoEl.src = data.url;
 
 
409
  document.getElementById('downloadLink').href = data.url;
410
 
411
- document.getElementById('cardResult').classList.remove('hidden');
412
- document.getElementById('cardResult').scrollIntoView({behavior: 'smooth'});
413
- document.getElementById('cardUpload').classList.add('hidden');
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);