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