@stack0/sdk 0.5.15 → 0.5.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1430,7 +1430,12 @@ var CDN = class {
1430
1430
  return this.http.get(`/cdn/video/${assetId}/stream`);
1431
1431
  }
1432
1432
  /**
1433
- * Generate a thumbnail from a video at a specific timestamp
1433
+ * Get the thumbnail at a specific timestamp, generating it on miss
1434
+ *
1435
+ * If no thumbnail exists at that timestamp yet, generation is queued
1436
+ * automatically and a response with `status: "pending"` (null url) is
1437
+ * returned. Poll until `status` is "ready", or use
1438
+ * {@link getThumbnailAndWait} to do that for you.
1434
1439
  *
1435
1440
  * @example
1436
1441
  * ```typescript
@@ -1440,7 +1445,9 @@ var CDN = class {
1440
1445
  * width: 320,
1441
1446
  * format: 'webp',
1442
1447
  * });
1443
- * console.log(`Thumbnail URL: ${thumbnail.url}`);
1448
+ * if (thumbnail.status === 'ready') {
1449
+ * console.log(`Thumbnail URL: ${thumbnail.url}`);
1450
+ * }
1444
1451
  * ```
1445
1452
  */
1446
1453
  async getThumbnail(request) {
@@ -1450,20 +1457,52 @@ var CDN = class {
1450
1457
  if (request.format) params.set("format", request.format);
1451
1458
  return this.http.get(`/cdn/video/thumbnail/${request.assetId}?${params.toString()}`);
1452
1459
  }
1460
+ /**
1461
+ * Get the thumbnail at a specific timestamp, waiting for generation to complete
1462
+ *
1463
+ * Calls {@link getThumbnail} and polls until the thumbnail is ready.
1464
+ *
1465
+ * @example
1466
+ * ```typescript
1467
+ * const thumbnail = await cdn.getThumbnailAndWait({
1468
+ * assetId: 'video-asset-id',
1469
+ * timestamp: 3.5,
1470
+ * });
1471
+ * console.log(`Thumbnail URL: ${thumbnail.url}`);
1472
+ * ```
1473
+ */
1474
+ async getThumbnailAndWait(request, options = {}) {
1475
+ const { pollInterval = 1e3, timeout = 6e4 } = options;
1476
+ const startTime = Date.now();
1477
+ while (true) {
1478
+ const thumbnail = await this.getThumbnail(request);
1479
+ if (thumbnail.status === "ready") {
1480
+ return thumbnail;
1481
+ }
1482
+ if (Date.now() - startTime >= timeout) {
1483
+ throw new Error("Thumbnail generation timed out");
1484
+ }
1485
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
1486
+ }
1487
+ }
1453
1488
  /**
1454
1489
  * Regenerate a thumbnail for a video (force regeneration even if one exists)
1455
1490
  *
1456
- * Useful for retrying failed thumbnail generation or regenerating with different settings.
1491
+ * Useful for retrying failed thumbnail generation or regenerating with
1492
+ * different settings. Regeneration is async: poll {@link getThumbnail} (or
1493
+ * use {@link getThumbnailAndWait}) at the same timestamp until the new
1494
+ * frame is ready.
1457
1495
  *
1458
1496
  * @example
1459
1497
  * ```typescript
1460
- * const result = await cdn.regenerateThumbnail({
1498
+ * await cdn.regenerateThumbnail({
1461
1499
  * assetId: 'video-asset-id',
1462
1500
  * timestamp: 5, // 5 seconds into the video
1463
1501
  * width: 1280,
1464
1502
  * format: 'jpg',
1465
1503
  * });
1466
- * console.log(`Thumbnail regeneration queued: ${result.status}`);
1504
+ * const thumbnail = await cdn.getThumbnailAndWait({ assetId: 'video-asset-id', timestamp: 5 });
1505
+ * console.log(`Thumbnail URL: ${thumbnail.url}`);
1467
1506
  * ```
1468
1507
  */
1469
1508
  async regenerateThumbnail(request) {
@@ -2088,6 +2127,33 @@ var CDN = class {
2088
2127
  * });
2089
2128
  * console.log(`Merge job started: ${job.id}`);
2090
2129
  * ```
2130
+ *
2131
+ * @example Composite a screen recording onto a held phone ("POV holding my phone"):
2132
+ * ```typescript
2133
+ * const job = await cdn.createMergeJob({
2134
+ * projectSlug: 'my-project',
2135
+ * inputs: [
2136
+ * { assetId: 'reaction-clip-id' },
2137
+ * {
2138
+ * assetId: 'app-recording-id', // the overlay clip
2139
+ * composite: {
2140
+ * backgroundAssetId: 'holding-phone-shot-id', // image or video
2141
+ * // Corner-pin onto the angled screen (background pixel coords, TL/TR/BL/BR)
2142
+ * quad: {
2143
+ * topLeft: { x: 320, y: 480 },
2144
+ * topRight: { x: 760, y: 500 },
2145
+ * bottomLeft: { x: 300, y: 1400 },
2146
+ * bottomRight: { x: 740, y: 1440 },
2147
+ * },
2148
+ * fit: 'cover',
2149
+ * // Screen area painted #F500FA in the background is keyed out, so the
2150
+ * // recording shows through and fingers over the screen occlude it
2151
+ * chromaKey: { color: '#F500FA', similarity: 0.34, blend: 0.06 },
2152
+ * },
2153
+ * },
2154
+ * ],
2155
+ * });
2156
+ * ```
2091
2157
  */
2092
2158
  async createMergeJob(request) {
2093
2159
  const response = await this.http.post("/cdn/video/merge", request);