ArchiveAds commited on
Commit
4d80813
·
verified ·
1 Parent(s): 26e8c8e

Update public/index.html

Browse files
Files changed (1) hide show
  1. public/index.html +97 -46
public/index.html CHANGED
@@ -65,9 +65,9 @@
65
  .input-table input { border: none; border-radius: 0; margin: 0; background: transparent; width: 100%; height: 100%; padding: 10px; }
66
  .del-btn { background: white; color: #ef4444; font-weight: 900; padding: 5px 10px; width: auto; margin: 0; font-size: 16px; }
67
 
68
- #crop-interface { display: none; height: 100%; flex-direction: column; }
69
  .crop-container { flex: 1; background: #000; border-radius: 8px; overflow: hidden; position: relative; min-height: 300px; }
70
- #image-to-crop { display: block; max-width: 100%; max-height: 500px; margin: 0 auto; }
71
 
72
  .progress-box { display:none; margin-top:20px; border-top:1px solid #eee; padding-top:20px; text-align: center; }
73
  .dl-link { color: var(--success); text-decoration: none; font-weight: bold; }
@@ -87,33 +87,48 @@
87
  <div class="tab" onclick="switchTab('instagram-dl')">Instagram Download</div>
88
  </div>
89
 
90
- <!-- 1. SINGLE INSTAGRAM TAB -->
91
  <div id="single-insta" class="tab-content active">
92
- <div style="max-width: 600px; margin: 0 auto;">
 
93
  <h3>Single Instagram Post</h3>
94
  <label>Instagram URL</label>
95
  <input type="text" id="si-url" placeholder="https://instagram.com/p/..." />
96
-
97
- <label>Caption</label>
98
- <input type="text" id="si-caption" placeholder="Enter caption..." />
99
-
100
- <label>Brand Name</label>
101
- <input type="text" id="si-brand" placeholder="ArchiveAds" />
102
-
103
- <label>Visual Style</label>
104
- <select id="si-template">
105
- <option value="archive_ads">Archive Ads (Red)</option>
106
- <option value="timeless_gallery">Timeless (Blue)</option>
107
- <option value="timeless_gallery_dark">Timeless Dark (Gold)</option>
108
- </select>
109
 
110
- <button id="btn-si" onclick="processSingleVideo('si')">Generate Video</button>
111
-
112
- <div id="si-progress" class="progress-box">
113
- <div style="font-weight:bold; margin-bottom:10px;" id="si-status-text">Processing...</div>
114
- <div id="si-download-area"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  </div>
116
  </div>
 
 
 
 
 
 
 
117
  </div>
118
 
119
  <!-- 2. BATCH INSTAGRAM TAB -->
@@ -281,39 +296,76 @@
281
  }).catch(() => window.open(url));
282
  }
283
 
284
- // --- SINGLE INSTAGRAM LOGIC ---
285
- async function processSingleVideo(prefix) {
286
- const url = document.getElementById(`${prefix}-url`).value.trim();
287
  if(!url) return alert("Enter URL");
288
 
289
- const caption = document.getElementById(`${prefix}-caption`).value;
290
- const brand = document.getElementById(`${prefix}-brand`).value;
291
- const templateId = document.getElementById(`${prefix}-template`).value;
292
-
293
- const btn = document.getElementById(`btn-${prefix}`);
294
- const prog = document.getElementById(`${prefix}-progress`);
295
- const status = document.getElementById(`${prefix}-status-text`);
296
- const area = document.getElementById(`${prefix}-download-area`);
297
 
298
- btn.disabled = true; btn.textContent = "Processing...";
299
- prog.style.display = 'block';
300
- status.textContent = "Starting Job...";
301
- area.innerHTML = "";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
  try {
304
  const res = await fetch('/start', {
305
  method: 'POST',
306
  headers: { 'Content-Type': 'application/json' },
307
- body: JSON.stringify({ url, caption, brandName: brand, templateId })
308
  });
309
  const data = await res.json();
310
  if (data.success) {
311
- pollSingleJob(data.jobId, prefix);
312
  } else {
313
- status.textContent = "Error: " + data.error;
314
- btn.disabled = false;
315
  }
316
- } catch(e) { status.textContent = "Connection Error"; btn.disabled = false; }
 
 
 
 
 
 
 
 
 
317
  }
318
 
319
  // --- SINGLE YOUTUBE LOGIC (Check Formats First) ---
@@ -374,7 +426,7 @@
374
  function pollSingleJob(jobId, prefix) {
375
  const status = document.getElementById(`${prefix}-status-text`);
376
  const area = document.getElementById(`${prefix}-download-area`);
377
- const btn = document.getElementById(`btn-${prefix}`);
378
 
379
  const poll = setInterval(async () => {
380
  const res = await fetch(`/status/${jobId}`);
@@ -388,13 +440,12 @@
388
  const fname = job.fileUrl.split('/').pop();
389
  area.innerHTML = `<button onclick="forceDownload('${job.fileUrl}', '${fname}')" style="background:none;border:none;color:#10b981;font-weight:bold;cursor:pointer;font-size:16px;">Download Video</button>`;
390
  playDing();
391
- btn.disabled = false;
392
- btn.textContent = prefix === 'sy' ? "Check Formats" : "Generate Video";
393
  }
394
  if (job.status === 'error') {
395
  clearInterval(poll);
396
  status.innerHTML = `<span style="color:red">Error: ${job.error}</span>`;
397
- btn.disabled = false;
398
  }
399
  }
400
  }, 2000);
 
65
  .input-table input { border: none; border-radius: 0; margin: 0; background: transparent; width: 100%; height: 100%; padding: 10px; }
66
  .del-btn { background: white; color: #ef4444; font-weight: 900; padding: 5px 10px; width: auto; margin: 0; font-size: 16px; }
67
 
68
+ #crop-interface, #si-crop-interface { display: none; height: 100%; flex-direction: column; }
69
  .crop-container { flex: 1; background: #000; border-radius: 8px; overflow: hidden; position: relative; min-height: 300px; }
70
+ #image-to-crop, #si-image-to-crop { display: block; max-width: 100%; max-height: 500px; margin: 0 auto; }
71
 
72
  .progress-box { display:none; margin-top:20px; border-top:1px solid #eee; padding-top:20px; text-align: center; }
73
  .dl-link { color: var(--success); text-decoration: none; font-weight: bold; }
 
87
  <div class="tab" onclick="switchTab('instagram-dl')">Instagram Download</div>
88
  </div>
89
 
90
+ <!-- 1. SINGLE INSTAGRAM TAB (Updated with Crop Workflow) -->
91
  <div id="single-insta" class="tab-content active">
92
+ <!-- Step 1: Input -->
93
+ <div id="si-input-view" style="max-width: 600px; margin: 0 auto;">
94
  <h3>Single Instagram Post</h3>
95
  <label>Instagram URL</label>
96
  <input type="text" id="si-url" placeholder="https://instagram.com/p/..." />
97
+ <button id="btn-si-fetch" onclick="fetchSingleInsta()">Fetch Video</button>
98
+ </div>
 
 
 
 
 
 
 
 
 
 
 
99
 
100
+ <!-- Step 2: Crop & Edit -->
101
+ <div id="si-crop-interface" style="display:none;">
102
+ <div class="crop-container">
103
+ <img id="si-image-to-crop" src="" alt="Video Frame">
104
+ </div>
105
+ <div style="margin-top: 20px; border-top: 1px solid #e5e7eb; padding-top: 20px;">
106
+ <label>Visual Style</label>
107
+ <select id="si-template">
108
+ <option value="archive_ads">Archive Ads (Red)</option>
109
+ <option value="timeless_gallery">Timeless (Blue)</option>
110
+ <option value="timeless_gallery_dark">Timeless Dark (Gold)</option>
111
+ </select>
112
+ <div style="display:flex; gap:10px;">
113
+ <div style="flex:2">
114
+ <label>Caption</label>
115
+ <input type="text" id="si-caption" placeholder="Enter caption..." />
116
+ </div>
117
+ <div style="flex:1">
118
+ <label>Brand Name</label>
119
+ <input type="text" id="si-brand" placeholder="ArchiveAds" />
120
+ </div>
121
+ </div>
122
+ <button id="btn-si-render" onclick="renderSingleInsta()">Generate Video</button>
123
  </div>
124
  </div>
125
+
126
+ <!-- Step 3: Progress/Download -->
127
+ <div id="si-progress" class="progress-box">
128
+ <div style="font-weight:bold; margin-bottom:10px;" id="si-status-text">Processing...</div>
129
+ <div id="si-download-area"></div>
130
+ <button id="btn-si-reset" style="margin-top:15px; background:#64748b; display:none;" onclick="resetSingleInsta()">Create Another</button>
131
+ </div>
132
  </div>
133
 
134
  <!-- 2. BATCH INSTAGRAM TAB -->
 
296
  }).catch(() => window.open(url));
297
  }
298
 
299
+ // --- 1. SINGLE INSTAGRAM LOGIC (Fetch -> Crop -> Render) ---
300
+ async function fetchSingleInsta() {
301
+ const url = document.getElementById('si-url').value.trim();
302
  if(!url) return alert("Enter URL");
303
 
304
+ const btn = document.getElementById('btn-si-fetch');
305
+ btn.disabled = true; btn.textContent = "Fetching...";
 
 
 
 
 
 
306
 
307
+ try {
308
+ // Reuse the analyze-clone route which fetches and gets a frame
309
+ const res = await fetch('/analyze-clone', {
310
+ method: 'POST',
311
+ headers: { 'Content-Type': 'application/json' },
312
+ body: JSON.stringify({ url })
313
+ });
314
+ const data = await res.json();
315
+
316
+ if(data.success) {
317
+ document.getElementById('si-input-view').style.display = 'none';
318
+ document.getElementById('si-crop-interface').style.display = 'block';
319
+
320
+ const img = document.getElementById('si-image-to-crop');
321
+ img.src = data.frameUrl;
322
+
323
+ if(cropper) cropper.destroy();
324
+ cropper = new Cropper(img, {
325
+ viewMode: 1, dragMode: 'move', autoCropArea: 0.8, restore: false, guides: true, center: true, highlight: false, cropBoxMovable: true, cropBoxResizable: true, toggleDragModeOnDblclick: false,
326
+ });
327
+ } else {
328
+ alert("Error: " + data.error);
329
+ btn.disabled = false; btn.textContent = "Fetch Video";
330
+ }
331
+ } catch(e) { alert("Error fetching video"); btn.disabled = false; btn.textContent = "Fetch Video"; }
332
+ }
333
 
334
+ async function renderSingleInsta() {
335
+ if(!cropper) return;
336
+ const url = document.getElementById('si-url').value.trim();
337
+ const caption = document.getElementById('si-caption').value;
338
+ const brand = document.getElementById('si-brand').value;
339
+ const templateId = document.getElementById('si-template').value;
340
+
341
+ const cropData = cropper.getData(true);
342
+ const cropString = `${Math.floor(cropData.width)}:${Math.floor(cropData.height)}:${Math.floor(cropData.x)}:${Math.floor(cropData.y)}`;
343
+
344
+ document.getElementById('si-crop-interface').style.display = 'none';
345
+ document.getElementById('si-progress').style.display = 'block';
346
+
347
  try {
348
  const res = await fetch('/start', {
349
  method: 'POST',
350
  headers: { 'Content-Type': 'application/json' },
351
+ body: JSON.stringify({ url, caption, brandName: brand, templateId, cropData: cropString })
352
  });
353
  const data = await res.json();
354
  if (data.success) {
355
+ pollSingleJob(data.jobId, 'si');
356
  } else {
357
+ document.getElementById('si-status-text').textContent = "Error: " + data.error;
 
358
  }
359
+ } catch(e) { document.getElementById('si-status-text').textContent = "Connection Error"; }
360
+ }
361
+
362
+ function resetSingleInsta() {
363
+ document.getElementById('si-progress').style.display = 'none';
364
+ document.getElementById('si-input-view').style.display = 'block';
365
+ document.getElementById('si-url').value = "";
366
+ document.getElementById('btn-si-fetch').disabled = false;
367
+ document.getElementById('btn-si-fetch').textContent = "Fetch Video";
368
+ if(cropper) { cropper.destroy(); cropper = null; }
369
  }
370
 
371
  // --- SINGLE YOUTUBE LOGIC (Check Formats First) ---
 
426
  function pollSingleJob(jobId, prefix) {
427
  const status = document.getElementById(`${prefix}-status-text`);
428
  const area = document.getElementById(`${prefix}-download-area`);
429
+ const btnReset = document.getElementById(`btn-${prefix}-reset`); // Optional reset btn
430
 
431
  const poll = setInterval(async () => {
432
  const res = await fetch(`/status/${jobId}`);
 
440
  const fname = job.fileUrl.split('/').pop();
441
  area.innerHTML = `<button onclick="forceDownload('${job.fileUrl}', '${fname}')" style="background:none;border:none;color:#10b981;font-weight:bold;cursor:pointer;font-size:16px;">Download Video</button>`;
442
  playDing();
443
+ if(btnReset) btnReset.style.display = "inline-block";
 
444
  }
445
  if (job.status === 'error') {
446
  clearInterval(poll);
447
  status.innerHTML = `<span style="color:red">Error: ${job.error}</span>`;
448
+ if(btnReset) btnReset.style.display = "inline-block";
449
  }
450
  }
451
  }, 2000);