sushilideaclan01 commited on
Commit
f41b748
·
1 Parent(s): 53706f2

added favicon

Browse files
frontend/index.html CHANGED
@@ -3,6 +3,7 @@
3
  <head>
4
  <meta charset="UTF-8" />
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 
6
  <title>Product Showcase Studio</title>
7
  <link rel="preconnect" href="https://fonts.googleapis.com" />
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
 
3
  <head>
4
  <meta charset="UTF-8" />
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
7
  <title>Product Showcase Studio</title>
8
  <link rel="preconnect" href="https://fonts.googleapis.com" />
9
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
frontend/public/favicon.svg ADDED
frontend/src/api.ts CHANGED
@@ -419,32 +419,57 @@ export function waitForSeedanceVideo(taskId: string, timeoutMs = 600000): Promis
419
  return new Promise((resolve, reject) => {
420
  let settled = false;
421
  const es = createSeedanceEventSource(taskId);
422
- const pollEveryMs = 4000;
423
- const poller = window.setInterval(async () => {
 
 
424
  if (settled) return;
425
- try {
426
- const data = await seedanceStatus(taskId);
427
- if (data.state === 'success' && data.url) {
428
- settled = true;
429
- window.clearInterval(poller);
430
- clearTimeout(t);
431
- es.close();
432
- resolve(data.url);
433
- } else if (data.state === 'fail') {
434
- settled = true;
435
- window.clearInterval(poller);
436
- clearTimeout(t);
437
- es.close();
438
- reject(new Error(data.failMsg || 'Seedance generation failed'));
439
- }
440
- } catch {
441
- /* ignore transient poll errors; SSE can still resolve */
 
 
 
 
442
  }
443
- }, pollEveryMs);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
444
  const t = setTimeout(() => {
445
  if (settled) return;
446
  settled = true;
447
- window.clearInterval(poller);
448
  es.close();
449
  reject(new Error('Seedance generation timed out'));
450
  }, timeoutMs);
@@ -456,25 +481,14 @@ export function waitForSeedanceVideo(taskId: string, timeoutMs = 600000): Promis
456
  url?: string | null;
457
  failMsg?: string | null;
458
  };
459
- if (data.state === 'success' && data.url) {
460
- settled = true;
461
- window.clearInterval(poller);
462
- clearTimeout(t);
463
- es.close();
464
- resolve(data.url);
465
- } else if (data.state === 'fail') {
466
- settled = true;
467
- window.clearInterval(poller);
468
- clearTimeout(t);
469
- es.close();
470
- reject(new Error(data.failMsg || 'Seedance generation failed'));
471
- }
472
  } catch {
473
  /* ignore malformed SSE payloads */
474
  }
475
  };
476
  es.onerror = () => {
477
- /* EventSource retries automatically; timeout handles terminal failure */
 
478
  };
479
  });
480
  }
@@ -503,31 +517,92 @@ export function createKlingEventSource(taskId: string): EventSource {
503
  return new EventSource(`${API_BASE}/api/veo/events/${taskId}`);
504
  }
505
 
 
 
 
 
 
 
 
 
 
 
 
506
  export function waitForKlingVideo(taskId: string, timeoutMs = 420000): Promise<string> {
507
  return new Promise((resolve, reject) => {
 
508
  const es = createKlingEventSource(taskId);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
509
  const t = setTimeout(() => {
 
 
 
510
  es.close();
511
  reject(new Error('Video generation timed out'));
512
  }, timeoutMs);
513
  es.onmessage = (ev) => {
 
514
  try {
515
  const data = JSON.parse(ev.data);
516
- if (data.status === 'succeeded' && data.url) {
517
- clearTimeout(t);
518
- es.close();
519
- resolve(data.url as string);
520
- } else if (data.status === 'failed' || data.status === 'cancelled') {
521
- clearTimeout(t);
522
- es.close();
523
- reject(new Error(data.error || data.message || 'Generation failed'));
524
- }
525
  } catch {
526
  /* ignore */
527
  }
528
  };
529
  es.onerror = () => {
530
- /* EventSource retries; rely on timeout */
 
531
  };
532
  });
533
  }
 
419
  return new Promise((resolve, reject) => {
420
  let settled = false;
421
  const es = createSeedanceEventSource(taskId);
422
+ let poller: number | null = null;
423
+ const pollEveryMs = 10000;
424
+
425
+ const settleSuccess = (url: string) => {
426
  if (settled) return;
427
+ settled = true;
428
+ if (poller !== null) window.clearInterval(poller);
429
+ clearTimeout(t);
430
+ es.close();
431
+ resolve(url);
432
+ };
433
+
434
+ const settleFail = (msg?: string | null) => {
435
+ if (settled) return;
436
+ settled = true;
437
+ if (poller !== null) window.clearInterval(poller);
438
+ clearTimeout(t);
439
+ es.close();
440
+ reject(new Error(msg || 'Seedance generation failed'));
441
+ };
442
+
443
+ const inspect = (data: { state?: string; url?: string | null; failMsg?: string | null }) => {
444
+ if (data.state === 'success' && data.url) {
445
+ settleSuccess(data.url);
446
+ } else if (data.state === 'fail') {
447
+ settleFail(data.failMsg);
448
  }
449
+ };
450
+
451
+ const startFallbackPolling = () => {
452
+ if (poller !== null || settled) return;
453
+ poller = window.setInterval(async () => {
454
+ if (settled) return;
455
+ try {
456
+ const data = await seedanceStatus(taskId);
457
+ inspect(data);
458
+ } catch {
459
+ /* ignore transient poll errors; timeout handles terminal failure */
460
+ }
461
+ }, pollEveryMs);
462
+ };
463
+
464
+ // Fast path: handle already-finished tasks without waiting for callback/SSE.
465
+ void seedanceStatus(taskId).then(inspect).catch(() => {
466
+ /* ignore transient status errors; SSE path remains active */
467
+ });
468
+
469
  const t = setTimeout(() => {
470
  if (settled) return;
471
  settled = true;
472
+ if (poller !== null) window.clearInterval(poller);
473
  es.close();
474
  reject(new Error('Seedance generation timed out'));
475
  }, timeoutMs);
 
481
  url?: string | null;
482
  failMsg?: string | null;
483
  };
484
+ inspect(data);
 
 
 
 
 
 
 
 
 
 
 
 
485
  } catch {
486
  /* ignore malformed SSE payloads */
487
  }
488
  };
489
  es.onerror = () => {
490
+ // EventSource retries automatically; poll only as a degraded-path fallback.
491
+ startFallbackPolling();
492
  };
493
  });
494
  }
 
517
  return new EventSource(`${API_BASE}/api/veo/events/${taskId}`);
518
  }
519
 
520
+ export async function klingStatus(taskId: string): Promise<{
521
+ status?: string;
522
+ url?: string | null;
523
+ error?: string | null;
524
+ message?: string | null;
525
+ }> {
526
+ const res = await fetch(`${API_BASE}/api/veo/status/${encodeURIComponent(taskId)}`);
527
+ if (!res.ok) throw new Error(await parseError(res, 'Video status check failed'));
528
+ return res.json();
529
+ }
530
+
531
  export function waitForKlingVideo(taskId: string, timeoutMs = 420000): Promise<string> {
532
  return new Promise((resolve, reject) => {
533
+ let settled = false;
534
  const es = createKlingEventSource(taskId);
535
+ let poller: number | null = null;
536
+ const pollEveryMs = 10000;
537
+
538
+ const settleSuccess = (url: string) => {
539
+ if (settled) return;
540
+ settled = true;
541
+ if (poller !== null) window.clearInterval(poller);
542
+ clearTimeout(t);
543
+ es.close();
544
+ resolve(url);
545
+ };
546
+
547
+ const settleFail = (msg?: string | null) => {
548
+ if (settled) return;
549
+ settled = true;
550
+ if (poller !== null) window.clearInterval(poller);
551
+ clearTimeout(t);
552
+ es.close();
553
+ reject(new Error(msg || 'Generation failed'));
554
+ };
555
+
556
+ const inspect = (data: {
557
+ status?: string;
558
+ url?: string | null;
559
+ error?: string | null;
560
+ message?: string | null;
561
+ }) => {
562
+ if (data.status === 'succeeded' && data.url) {
563
+ settleSuccess(data.url);
564
+ } else if (data.status === 'failed' || data.status === 'cancelled') {
565
+ settleFail(data.error || data.message);
566
+ }
567
+ };
568
+
569
+ const startFallbackPolling = () => {
570
+ if (poller !== null || settled) return;
571
+ poller = window.setInterval(async () => {
572
+ if (settled) return;
573
+ try {
574
+ const data = await klingStatus(taskId);
575
+ inspect(data);
576
+ } catch {
577
+ /* ignore transient poll errors; timeout handles terminal failure */
578
+ }
579
+ }, pollEveryMs);
580
+ };
581
+
582
+ // Fast path for already-finished tasks.
583
+ void klingStatus(taskId).then(inspect).catch(() => {
584
+ /* ignore transient status errors; SSE path remains active */
585
+ });
586
+
587
  const t = setTimeout(() => {
588
+ if (settled) return;
589
+ settled = true;
590
+ if (poller !== null) window.clearInterval(poller);
591
  es.close();
592
  reject(new Error('Video generation timed out'));
593
  }, timeoutMs);
594
  es.onmessage = (ev) => {
595
+ if (settled) return;
596
  try {
597
  const data = JSON.parse(ev.data);
598
+ inspect(data);
 
 
 
 
 
 
 
 
599
  } catch {
600
  /* ignore */
601
  }
602
  };
603
  es.onerror = () => {
604
+ // EventSource retries automatically; poll only as a degraded-path fallback.
605
+ startFallbackPolling();
606
  };
607
  });
608
  }