@stack0/sdk 0.5.16 → 0.5.18

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/README.md CHANGED
@@ -483,14 +483,17 @@ console.log(`Progress: ${status.progress}%`);
483
483
  const urls = await stack0.cdn.getStreamingUrls('asset-id');
484
484
  console.log(urls.hlsUrl);
485
485
 
486
- // Generate thumbnails
487
- const thumb = await stack0.cdn.getThumbnail({
486
+ // Get a thumbnail at a timestamp (generated on miss; waits until ready)
487
+ const thumb = await stack0.cdn.getThumbnailAndWait({
488
488
  assetId: 'video-asset-id',
489
489
  timestamp: 10.5,
490
490
  width: 320,
491
491
  format: 'webp',
492
492
  });
493
493
 
494
+ // Non-blocking variant: returns { status: 'pending', url: null } while generating
495
+ const maybe = await stack0.cdn.getThumbnail({ assetId: 'video-asset-id', timestamp: 0 });
496
+
494
497
  // Extract audio
495
498
  const { jobId } = await stack0.cdn.extractAudio({
496
499
  projectSlug: 'my-project',
@@ -534,6 +537,28 @@ const mergeJob = await stack0.cdn.createMergeJob({
534
537
  audioTrack: { assetId: 'music-id', loop: true, fadeIn: 2, fadeOut: 3 },
535
538
  output: { format: 'mp4', quality: '1080p', filename: 'final.mp4' },
536
539
  });
540
+
541
+ // Composite a screen recording onto a held phone (screen keyed #F500FA):
542
+ // the keyed background sits on top, so fingers occlude the inset recording
543
+ const composited = await stack0.cdn.createMergeJob({
544
+ projectSlug: 'my-project',
545
+ inputs: [
546
+ {
547
+ assetId: 'app-screen-recording',
548
+ composite: {
549
+ backgroundAssetId: 'holding-phone-shot',
550
+ quad: {
551
+ topLeft: { x: 320, y: 480 },
552
+ topRight: { x: 760, y: 500 },
553
+ bottomLeft: { x: 300, y: 1400 },
554
+ bottomRight: { x: 740, y: 1440 },
555
+ },
556
+ fit: 'cover',
557
+ chromaKey: { color: '#F500FA', similarity: 0.34, blend: 0.06 },
558
+ },
559
+ },
560
+ ],
561
+ });
537
562
  ```
538
563
 
539
564
  ### Private Files
@@ -177,6 +177,16 @@ interface VideoVariant {
177
177
  quality: VideoQuality;
178
178
  codec?: VideoCodec;
179
179
  bitrate?: number;
180
+ /**
181
+ * Bytes actually written to storage for this rendition, measured after the job completes.
182
+ * For HLS this covers the variant playlist and its segments; for MP4, the single file.
183
+ *
184
+ * Response-only — ignored if you set it on a transcode request.
185
+ *
186
+ * Undefined means not measured (job still running, job predates measurement, or the
187
+ * measurement failed), never zero. `bitrate * duration` is an estimate; this is not.
188
+ */
189
+ sizeBytes?: number;
180
190
  }
181
191
  /**
182
192
  * Watermark burned into a video during transcode.
@@ -299,6 +309,13 @@ interface TranscodeJob {
299
309
  /** Provider or preprocessing failure details when status is `failed`. */
300
310
  errorMessage: string | null;
301
311
  mediaConvertJobId: string | null;
312
+ /**
313
+ * Every byte this job wrote: renditions, HLS segments, and manifests together, since
314
+ * storage is billed per object rather than for the media alone.
315
+ *
316
+ * Null until the job completes, and null if the measurement failed. Not zero.
317
+ */
318
+ totalOutputBytes: number | null;
302
319
  createdAt: Date;
303
320
  startedAt: Date | null;
304
321
  completedAt: Date | null;
@@ -335,10 +352,16 @@ interface ThumbnailRequest {
335
352
  format?: "jpg" | "png" | "webp";
336
353
  }
337
354
  interface ThumbnailResponse {
338
- url: string;
355
+ id: string | null;
356
+ assetId: string;
339
357
  timestamp: number;
340
- width: number;
341
- height: number;
358
+ /** CDN URL of the frame; null while status is "pending" */
359
+ url: string | null;
360
+ width: number | null;
361
+ height: number | null;
362
+ format: string;
363
+ /** "ready" when the frame exists; "pending" while generation is queued/in flight */
364
+ status: "ready" | "pending";
342
365
  }
343
366
  interface RegenerateThumbnailRequest {
344
367
  assetId: string;
@@ -581,6 +604,42 @@ interface CdnStorageBreakdownResponse {
581
604
  sizeFormatted: string;
582
605
  };
583
606
  }
607
+ interface CdnStorageUsageRequest {
608
+ projectSlug?: string;
609
+ environment?: CdnEnvironment;
610
+ /**
611
+ * Virtual folder path to scope the total to, e.g. "/customers/acme". The folder itself and
612
+ * everything nested under it are counted. Omit to total the whole project or organization.
613
+ */
614
+ folder?: string;
615
+ }
616
+ interface CdnStorageUsageBucket {
617
+ bytes: number;
618
+ bytesFormatted: string;
619
+ objectCount: number;
620
+ }
621
+ interface CdnStorageUsageResponse {
622
+ /** Uploads plus every measured derived object stored under the scope. */
623
+ totalBytes: number;
624
+ totalFormatted: string;
625
+ /** Objects behind that total. Storage is billed per object, so segments and manifests count. */
626
+ objectCount: number;
627
+ /** Split by asset type; a video's renditions count as video. */
628
+ byType: Record<string, CdnStorageUsageBucket>;
629
+ breakdown: {
630
+ /** The uploaded files themselves. */
631
+ originals: CdnStorageUsageBucket;
632
+ /** Renditions, HLS segments and manifests, thumbnails, GIFs. */
633
+ derived: CdnStorageUsageBucket;
634
+ };
635
+ /**
636
+ * Assets known to have derivatives whose bytes have not been measured yet. Their storage is
637
+ * missing from `totalBytes`, so a nonzero count means the total is a floor, not the answer.
638
+ */
639
+ unmeasuredAssets: number;
640
+ /** The folder the total covers, or null when the whole project or org was counted. */
641
+ folder: string | null;
642
+ }
584
643
  interface GetFolderRequest {
585
644
  id: string;
586
645
  }
@@ -693,6 +752,63 @@ interface TextOverlay {
693
752
  /** Stroke/outline configuration for better visibility */
694
753
  stroke?: TextOverlayStroke;
695
754
  }
755
+ /** A point in background pixel coordinates */
756
+ interface CompositePoint {
757
+ x: number;
758
+ y: number;
759
+ }
760
+ /**
761
+ * Perspective destination quad: where the overlay's four corners land on the
762
+ * background, in background pixels. Use for corner-pinning a clip onto an
763
+ * angled surface like a phone screen in a held-phone shot.
764
+ */
765
+ interface CompositeQuad {
766
+ topLeft: CompositePoint;
767
+ topRight: CompositePoint;
768
+ bottomLeft: CompositePoint;
769
+ bottomRight: CompositePoint;
770
+ }
771
+ /** Axis-aligned destination rectangle in background pixels (no perspective) */
772
+ interface CompositeRect {
773
+ x: number;
774
+ y: number;
775
+ width: number;
776
+ height: number;
777
+ }
778
+ /**
779
+ * Chroma key applied to the background: the keyed color becomes transparent so
780
+ * the overlay shows through only there, and real foreground (fingers, hands)
781
+ * occludes the overlay.
782
+ */
783
+ interface CompositeChromaKey {
784
+ /** Key color in hex format (e.g., "#F500FA") */
785
+ color: string;
786
+ /** Color distance tolerance 0.01-1 (default: 0.3) */
787
+ similarity?: number;
788
+ /** Edge blend 0-1 (default: 0.1) */
789
+ blend?: number;
790
+ }
791
+ /**
792
+ * Composite configuration: place this input (the overlay) into a region of a
793
+ * background asset. The background can be an image or video. The keyed or
794
+ * masked background is layered on top of the placed overlay, so foreground in
795
+ * the background shot occludes the inset — ideal for "POV holding my phone"
796
+ * style UGC where an app recording is pinned onto the phone's (keyed) screen.
797
+ */
798
+ interface CompositeConfig {
799
+ /** Asset ID of the background image or video */
800
+ backgroundAssetId: string;
801
+ /** Perspective corner-pin destination (requires chromaKey or maskAssetId) */
802
+ quad?: CompositeQuad;
803
+ /** Axis-aligned destination rectangle (simple PiP when no key/mask) */
804
+ rect?: CompositeRect;
805
+ /** How the overlay fills the destination region's aspect ratio (default: "cover") */
806
+ fit?: "cover" | "contain" | "fill";
807
+ /** Chroma key applied to the background */
808
+ chromaKey?: CompositeChromaKey;
809
+ /** Grayscale mask image asset: white keeps the background, black reveals the overlay */
810
+ maskAssetId?: string;
811
+ }
696
812
  /**
697
813
  * A single input item for the merge operation.
698
814
  * Can be a video, image, or audio file.
@@ -708,6 +824,8 @@ interface MergeInputItem {
708
824
  endTime?: number;
709
825
  /** Text overlay/caption configuration */
710
826
  textOverlay?: TextOverlay;
827
+ /** Composite this input into a region of a background asset */
828
+ composite?: CompositeConfig;
711
829
  }
712
830
  /**
713
831
  * Audio track overlay configuration for merge jobs.
@@ -1309,7 +1427,12 @@ declare class CDN {
1309
1427
  */
1310
1428
  getStreamingUrls(assetId: string): Promise<StreamingUrls>;
1311
1429
  /**
1312
- * Generate a thumbnail from a video at a specific timestamp
1430
+ * Get the thumbnail at a specific timestamp, generating it on miss
1431
+ *
1432
+ * If no thumbnail exists at that timestamp yet, generation is queued
1433
+ * automatically and a response with `status: "pending"` (null url) is
1434
+ * returned. Poll until `status` is "ready", or use
1435
+ * {@link getThumbnailAndWait} to do that for you.
1313
1436
  *
1314
1437
  * @example
1315
1438
  * ```typescript
@@ -1319,24 +1442,48 @@ declare class CDN {
1319
1442
  * width: 320,
1320
1443
  * format: 'webp',
1321
1444
  * });
1322
- * console.log(`Thumbnail URL: ${thumbnail.url}`);
1445
+ * if (thumbnail.status === 'ready') {
1446
+ * console.log(`Thumbnail URL: ${thumbnail.url}`);
1447
+ * }
1323
1448
  * ```
1324
1449
  */
1325
1450
  getThumbnail(request: ThumbnailRequest): Promise<ThumbnailResponse>;
1451
+ /**
1452
+ * Get the thumbnail at a specific timestamp, waiting for generation to complete
1453
+ *
1454
+ * Calls {@link getThumbnail} and polls until the thumbnail is ready.
1455
+ *
1456
+ * @example
1457
+ * ```typescript
1458
+ * const thumbnail = await cdn.getThumbnailAndWait({
1459
+ * assetId: 'video-asset-id',
1460
+ * timestamp: 3.5,
1461
+ * });
1462
+ * console.log(`Thumbnail URL: ${thumbnail.url}`);
1463
+ * ```
1464
+ */
1465
+ getThumbnailAndWait(request: ThumbnailRequest, options?: {
1466
+ pollInterval?: number;
1467
+ timeout?: number;
1468
+ }): Promise<ThumbnailResponse>;
1326
1469
  /**
1327
1470
  * Regenerate a thumbnail for a video (force regeneration even if one exists)
1328
1471
  *
1329
- * Useful for retrying failed thumbnail generation or regenerating with different settings.
1472
+ * Useful for retrying failed thumbnail generation or regenerating with
1473
+ * different settings. Regeneration is async: poll {@link getThumbnail} (or
1474
+ * use {@link getThumbnailAndWait}) at the same timestamp until the new
1475
+ * frame is ready.
1330
1476
  *
1331
1477
  * @example
1332
1478
  * ```typescript
1333
- * const result = await cdn.regenerateThumbnail({
1479
+ * await cdn.regenerateThumbnail({
1334
1480
  * assetId: 'video-asset-id',
1335
1481
  * timestamp: 5, // 5 seconds into the video
1336
1482
  * width: 1280,
1337
1483
  * format: 'jpg',
1338
1484
  * });
1339
- * console.log(`Thumbnail regeneration queued: ${result.status}`);
1485
+ * const thumbnail = await cdn.getThumbnailAndWait({ assetId: 'video-asset-id', timestamp: 5 });
1486
+ * console.log(`Thumbnail URL: ${thumbnail.url}`);
1340
1487
  * ```
1341
1488
  */
1342
1489
  regenerateThumbnail(request: RegenerateThumbnailRequest): Promise<RegenerateThumbnailResponse>;
@@ -1602,6 +1749,29 @@ declare class CDN {
1602
1749
  * ```
1603
1750
  */
1604
1751
  getStorageBreakdown(request?: CdnStorageBreakdownRequest): Promise<CdnStorageBreakdownResponse>;
1752
+ /**
1753
+ * Get total stored bytes for a project or folder, derived assets included.
1754
+ *
1755
+ * Totals uploads plus everything the pipelines derived from them — video renditions, HLS
1756
+ * segments and manifests, thumbnails, GIFs — in one call. Use this to meter a plan limit:
1757
+ * paging `list()` and summing `size` counts uploads only, and an HLS ladder typically runs
1758
+ * well past the size of the source it came from.
1759
+ *
1760
+ * Check `unmeasuredAssets`. A nonzero value means some assets have derivatives whose bytes
1761
+ * have not been measured, so `totalBytes` is a floor rather than the full number.
1762
+ *
1763
+ * @example
1764
+ * ```typescript
1765
+ * const usage = await cdn.getStorageUsage({
1766
+ * projectSlug: 'my-project',
1767
+ * folder: '/customers/acme',
1768
+ * });
1769
+ * console.log(`${usage.totalFormatted} across ${usage.objectCount} objects`);
1770
+ * console.log(` uploads: ${usage.breakdown.originals.bytesFormatted}`);
1771
+ * console.log(` derived: ${usage.breakdown.derived.bytesFormatted}`);
1772
+ * ```
1773
+ */
1774
+ getStorageUsage(request?: CdnStorageUsageRequest): Promise<CdnStorageUsageResponse>;
1605
1775
  private convertUsageDates;
1606
1776
  private convertUsageDataPointDates;
1607
1777
  /**
@@ -1716,6 +1886,33 @@ declare class CDN {
1716
1886
  * });
1717
1887
  * console.log(`Merge job started: ${job.id}`);
1718
1888
  * ```
1889
+ *
1890
+ * @example Composite a screen recording onto a held phone ("POV holding my phone"):
1891
+ * ```typescript
1892
+ * const job = await cdn.createMergeJob({
1893
+ * projectSlug: 'my-project',
1894
+ * inputs: [
1895
+ * { assetId: 'reaction-clip-id' },
1896
+ * {
1897
+ * assetId: 'app-recording-id', // the overlay clip
1898
+ * composite: {
1899
+ * backgroundAssetId: 'holding-phone-shot-id', // image or video
1900
+ * // Corner-pin onto the angled screen (background pixel coords, TL/TR/BL/BR)
1901
+ * quad: {
1902
+ * topLeft: { x: 320, y: 480 },
1903
+ * topRight: { x: 760, y: 500 },
1904
+ * bottomLeft: { x: 300, y: 1400 },
1905
+ * bottomRight: { x: 740, y: 1440 },
1906
+ * },
1907
+ * fit: 'cover',
1908
+ * // Screen area painted #F500FA in the background is keyed out, so the
1909
+ * // recording shows through and fingers over the screen occlude it
1910
+ * chromaKey: { color: '#F500FA', similarity: 0.34, blend: 0.06 },
1911
+ * },
1912
+ * },
1913
+ * ],
1914
+ * });
1915
+ * ```
1719
1916
  */
1720
1917
  createMergeJob(request: CreateMergeJobRequest): Promise<MergeJob>;
1721
1918
  /**
@@ -1874,4 +2071,4 @@ declare class CDN {
1874
2071
  private convertImportFileDates;
1875
2072
  }
1876
2073
 
1877
- export { type Asset, type AssetStatus, type AssetType, type AudioTrackInput, type BundleDownloadUrlRequest, type BundleDownloadUrlResponse, type BundleStatus, CDN, type CancelImportResponse, type CancelMergeJobRequest, type CdnEnvironment, type CdnStorageBreakdownItem, type CdnStorageBreakdownRequest, type CdnStorageBreakdownResponse, type CdnUsageDataPoint, type CdnUsageHistoryRequest, type CdnUsageHistoryResponse, type CdnUsageRequest, type CdnUsageResponse, type ConfirmUploadRequest, type ConfirmUploadResponse, type CreateBundleRequest, type CreateBundleResponse, type CreateFolderRequest, type CreateImportRequest, type CreateImportResponse, type CreateMergeJobRequest, type DeleteAssetRequest, type DeleteAssetsRequest, type DeleteAssetsResponse, type DownloadBundle, type ExtractAudioRequest, type ExtractAudioResponse, type Folder, type FolderListItem, type FolderTreeNode, type GenerateGifRequest, type GetAssetRequest, type GetFolderByPathRequest, type GetFolderRequest, type GetFolderTreeRequest, type GetMergeJobRequest, type GifStatus, type ImageVideoWatermarkOptions, type ImageWatermarkConfig, type ImageWatermarkPosition, type ImageWatermarkSizingMode, type ImportAuthType, type ImportError, type ImportFile, type ImportFileStatus, type ImportJob, type ImportJobStatus, type ImportJobSummary, type ImportPathMode, type ListAssetsRequest, type ListAssetsResponse, type ListBundlesRequest, type ListBundlesResponse, type ListFoldersRequest, type ListFoldersResponse, type ListGifsRequest, type ListImportFilesRequest, type ListImportFilesResponse, type ListImportsRequest, type ListImportsResponse, type ListJobsRequest, type ListJobsResponse, type ListMergeJobsRequest, type ListMergeJobsResponse, type ListPrivateFilesRequest, type ListPrivateFilesResponse, type ListThumbnailsRequest, type ListThumbnailsResponse, type MergeAspectRatio, type MergeInputItem, type MergeJob, type MergeJobWithOutput, type MergeOutputConfig, type MergeOutputFormat, type MergeQuality, type MergeStatus, type MoveAssetsRequest, type MoveAssetsResponse, type MoveFolderRequest, type MoveFolderResponse, type MovePrivateFilesRequest, type MovePrivateFilesResponse, type PrivateDownloadUrlRequest, type PrivateDownloadUrlResponse, type PrivateFile, type PrivateFileStatus, type PrivateUploadUrlRequest, type PrivateUploadUrlResponse, type RegenerateThumbnailRequest, type RegenerateThumbnailResponse, type RetryImportResponse, type StreamingUrls, type TextOverlay, type TextOverlayShadow, type TextOverlayStroke, type TextVideoWatermarkOptions, type ThumbnailRequest, type ThumbnailResponse, type TranscodeJob, type TranscodeVideoRequest, type TranscodingStatus, type TransformOptions, type TrimOptions, type UpdateAssetRequest, type UpdateFolderRequest, type UpdatePrivateFileRequest, type UploadFromUrlRequest, type UploadUrlRequest, type UploadUrlResponse, type VideoCodec, type VideoGif, type VideoOutputFormat, type VideoQuality, type VideoThumbnail, type VideoVariant, type WatermarkOptions };
2074
+ export { type Asset, type AssetStatus, type AssetType, type AudioTrackInput, type BundleDownloadUrlRequest, type BundleDownloadUrlResponse, type BundleStatus, CDN, type CancelImportResponse, type CancelMergeJobRequest, type CdnEnvironment, type CdnStorageBreakdownItem, type CdnStorageBreakdownRequest, type CdnStorageBreakdownResponse, type CdnStorageUsageBucket, type CdnStorageUsageRequest, type CdnStorageUsageResponse, type CdnUsageDataPoint, type CdnUsageHistoryRequest, type CdnUsageHistoryResponse, type CdnUsageRequest, type CdnUsageResponse, type CompositeChromaKey, type CompositeConfig, type CompositePoint, type CompositeQuad, type CompositeRect, type ConfirmUploadRequest, type ConfirmUploadResponse, type CreateBundleRequest, type CreateBundleResponse, type CreateFolderRequest, type CreateImportRequest, type CreateImportResponse, type CreateMergeJobRequest, type DeleteAssetRequest, type DeleteAssetsRequest, type DeleteAssetsResponse, type DownloadBundle, type ExtractAudioRequest, type ExtractAudioResponse, type Folder, type FolderListItem, type FolderTreeNode, type GenerateGifRequest, type GetAssetRequest, type GetFolderByPathRequest, type GetFolderRequest, type GetFolderTreeRequest, type GetMergeJobRequest, type GifStatus, type ImageVideoWatermarkOptions, type ImageWatermarkConfig, type ImageWatermarkPosition, type ImageWatermarkSizingMode, type ImportAuthType, type ImportError, type ImportFile, type ImportFileStatus, type ImportJob, type ImportJobStatus, type ImportJobSummary, type ImportPathMode, type ListAssetsRequest, type ListAssetsResponse, type ListBundlesRequest, type ListBundlesResponse, type ListFoldersRequest, type ListFoldersResponse, type ListGifsRequest, type ListImportFilesRequest, type ListImportFilesResponse, type ListImportsRequest, type ListImportsResponse, type ListJobsRequest, type ListJobsResponse, type ListMergeJobsRequest, type ListMergeJobsResponse, type ListPrivateFilesRequest, type ListPrivateFilesResponse, type ListThumbnailsRequest, type ListThumbnailsResponse, type MergeAspectRatio, type MergeInputItem, type MergeJob, type MergeJobWithOutput, type MergeOutputConfig, type MergeOutputFormat, type MergeQuality, type MergeStatus, type MoveAssetsRequest, type MoveAssetsResponse, type MoveFolderRequest, type MoveFolderResponse, type MovePrivateFilesRequest, type MovePrivateFilesResponse, type PrivateDownloadUrlRequest, type PrivateDownloadUrlResponse, type PrivateFile, type PrivateFileStatus, type PrivateUploadUrlRequest, type PrivateUploadUrlResponse, type RegenerateThumbnailRequest, type RegenerateThumbnailResponse, type RetryImportResponse, type StreamingUrls, type TextOverlay, type TextOverlayShadow, type TextOverlayStroke, type TextVideoWatermarkOptions, type ThumbnailRequest, type ThumbnailResponse, type TranscodeJob, type TranscodeVideoRequest, type TranscodingStatus, type TransformOptions, type TrimOptions, type UpdateAssetRequest, type UpdateFolderRequest, type UpdatePrivateFileRequest, type UploadFromUrlRequest, type UploadUrlRequest, type UploadUrlResponse, type VideoCodec, type VideoGif, type VideoOutputFormat, type VideoQuality, type VideoThumbnail, type VideoVariant, type WatermarkOptions };
@@ -177,6 +177,16 @@ interface VideoVariant {
177
177
  quality: VideoQuality;
178
178
  codec?: VideoCodec;
179
179
  bitrate?: number;
180
+ /**
181
+ * Bytes actually written to storage for this rendition, measured after the job completes.
182
+ * For HLS this covers the variant playlist and its segments; for MP4, the single file.
183
+ *
184
+ * Response-only — ignored if you set it on a transcode request.
185
+ *
186
+ * Undefined means not measured (job still running, job predates measurement, or the
187
+ * measurement failed), never zero. `bitrate * duration` is an estimate; this is not.
188
+ */
189
+ sizeBytes?: number;
180
190
  }
181
191
  /**
182
192
  * Watermark burned into a video during transcode.
@@ -299,6 +309,13 @@ interface TranscodeJob {
299
309
  /** Provider or preprocessing failure details when status is `failed`. */
300
310
  errorMessage: string | null;
301
311
  mediaConvertJobId: string | null;
312
+ /**
313
+ * Every byte this job wrote: renditions, HLS segments, and manifests together, since
314
+ * storage is billed per object rather than for the media alone.
315
+ *
316
+ * Null until the job completes, and null if the measurement failed. Not zero.
317
+ */
318
+ totalOutputBytes: number | null;
302
319
  createdAt: Date;
303
320
  startedAt: Date | null;
304
321
  completedAt: Date | null;
@@ -335,10 +352,16 @@ interface ThumbnailRequest {
335
352
  format?: "jpg" | "png" | "webp";
336
353
  }
337
354
  interface ThumbnailResponse {
338
- url: string;
355
+ id: string | null;
356
+ assetId: string;
339
357
  timestamp: number;
340
- width: number;
341
- height: number;
358
+ /** CDN URL of the frame; null while status is "pending" */
359
+ url: string | null;
360
+ width: number | null;
361
+ height: number | null;
362
+ format: string;
363
+ /** "ready" when the frame exists; "pending" while generation is queued/in flight */
364
+ status: "ready" | "pending";
342
365
  }
343
366
  interface RegenerateThumbnailRequest {
344
367
  assetId: string;
@@ -581,6 +604,42 @@ interface CdnStorageBreakdownResponse {
581
604
  sizeFormatted: string;
582
605
  };
583
606
  }
607
+ interface CdnStorageUsageRequest {
608
+ projectSlug?: string;
609
+ environment?: CdnEnvironment;
610
+ /**
611
+ * Virtual folder path to scope the total to, e.g. "/customers/acme". The folder itself and
612
+ * everything nested under it are counted. Omit to total the whole project or organization.
613
+ */
614
+ folder?: string;
615
+ }
616
+ interface CdnStorageUsageBucket {
617
+ bytes: number;
618
+ bytesFormatted: string;
619
+ objectCount: number;
620
+ }
621
+ interface CdnStorageUsageResponse {
622
+ /** Uploads plus every measured derived object stored under the scope. */
623
+ totalBytes: number;
624
+ totalFormatted: string;
625
+ /** Objects behind that total. Storage is billed per object, so segments and manifests count. */
626
+ objectCount: number;
627
+ /** Split by asset type; a video's renditions count as video. */
628
+ byType: Record<string, CdnStorageUsageBucket>;
629
+ breakdown: {
630
+ /** The uploaded files themselves. */
631
+ originals: CdnStorageUsageBucket;
632
+ /** Renditions, HLS segments and manifests, thumbnails, GIFs. */
633
+ derived: CdnStorageUsageBucket;
634
+ };
635
+ /**
636
+ * Assets known to have derivatives whose bytes have not been measured yet. Their storage is
637
+ * missing from `totalBytes`, so a nonzero count means the total is a floor, not the answer.
638
+ */
639
+ unmeasuredAssets: number;
640
+ /** The folder the total covers, or null when the whole project or org was counted. */
641
+ folder: string | null;
642
+ }
584
643
  interface GetFolderRequest {
585
644
  id: string;
586
645
  }
@@ -693,6 +752,63 @@ interface TextOverlay {
693
752
  /** Stroke/outline configuration for better visibility */
694
753
  stroke?: TextOverlayStroke;
695
754
  }
755
+ /** A point in background pixel coordinates */
756
+ interface CompositePoint {
757
+ x: number;
758
+ y: number;
759
+ }
760
+ /**
761
+ * Perspective destination quad: where the overlay's four corners land on the
762
+ * background, in background pixels. Use for corner-pinning a clip onto an
763
+ * angled surface like a phone screen in a held-phone shot.
764
+ */
765
+ interface CompositeQuad {
766
+ topLeft: CompositePoint;
767
+ topRight: CompositePoint;
768
+ bottomLeft: CompositePoint;
769
+ bottomRight: CompositePoint;
770
+ }
771
+ /** Axis-aligned destination rectangle in background pixels (no perspective) */
772
+ interface CompositeRect {
773
+ x: number;
774
+ y: number;
775
+ width: number;
776
+ height: number;
777
+ }
778
+ /**
779
+ * Chroma key applied to the background: the keyed color becomes transparent so
780
+ * the overlay shows through only there, and real foreground (fingers, hands)
781
+ * occludes the overlay.
782
+ */
783
+ interface CompositeChromaKey {
784
+ /** Key color in hex format (e.g., "#F500FA") */
785
+ color: string;
786
+ /** Color distance tolerance 0.01-1 (default: 0.3) */
787
+ similarity?: number;
788
+ /** Edge blend 0-1 (default: 0.1) */
789
+ blend?: number;
790
+ }
791
+ /**
792
+ * Composite configuration: place this input (the overlay) into a region of a
793
+ * background asset. The background can be an image or video. The keyed or
794
+ * masked background is layered on top of the placed overlay, so foreground in
795
+ * the background shot occludes the inset — ideal for "POV holding my phone"
796
+ * style UGC where an app recording is pinned onto the phone's (keyed) screen.
797
+ */
798
+ interface CompositeConfig {
799
+ /** Asset ID of the background image or video */
800
+ backgroundAssetId: string;
801
+ /** Perspective corner-pin destination (requires chromaKey or maskAssetId) */
802
+ quad?: CompositeQuad;
803
+ /** Axis-aligned destination rectangle (simple PiP when no key/mask) */
804
+ rect?: CompositeRect;
805
+ /** How the overlay fills the destination region's aspect ratio (default: "cover") */
806
+ fit?: "cover" | "contain" | "fill";
807
+ /** Chroma key applied to the background */
808
+ chromaKey?: CompositeChromaKey;
809
+ /** Grayscale mask image asset: white keeps the background, black reveals the overlay */
810
+ maskAssetId?: string;
811
+ }
696
812
  /**
697
813
  * A single input item for the merge operation.
698
814
  * Can be a video, image, or audio file.
@@ -708,6 +824,8 @@ interface MergeInputItem {
708
824
  endTime?: number;
709
825
  /** Text overlay/caption configuration */
710
826
  textOverlay?: TextOverlay;
827
+ /** Composite this input into a region of a background asset */
828
+ composite?: CompositeConfig;
711
829
  }
712
830
  /**
713
831
  * Audio track overlay configuration for merge jobs.
@@ -1309,7 +1427,12 @@ declare class CDN {
1309
1427
  */
1310
1428
  getStreamingUrls(assetId: string): Promise<StreamingUrls>;
1311
1429
  /**
1312
- * Generate a thumbnail from a video at a specific timestamp
1430
+ * Get the thumbnail at a specific timestamp, generating it on miss
1431
+ *
1432
+ * If no thumbnail exists at that timestamp yet, generation is queued
1433
+ * automatically and a response with `status: "pending"` (null url) is
1434
+ * returned. Poll until `status` is "ready", or use
1435
+ * {@link getThumbnailAndWait} to do that for you.
1313
1436
  *
1314
1437
  * @example
1315
1438
  * ```typescript
@@ -1319,24 +1442,48 @@ declare class CDN {
1319
1442
  * width: 320,
1320
1443
  * format: 'webp',
1321
1444
  * });
1322
- * console.log(`Thumbnail URL: ${thumbnail.url}`);
1445
+ * if (thumbnail.status === 'ready') {
1446
+ * console.log(`Thumbnail URL: ${thumbnail.url}`);
1447
+ * }
1323
1448
  * ```
1324
1449
  */
1325
1450
  getThumbnail(request: ThumbnailRequest): Promise<ThumbnailResponse>;
1451
+ /**
1452
+ * Get the thumbnail at a specific timestamp, waiting for generation to complete
1453
+ *
1454
+ * Calls {@link getThumbnail} and polls until the thumbnail is ready.
1455
+ *
1456
+ * @example
1457
+ * ```typescript
1458
+ * const thumbnail = await cdn.getThumbnailAndWait({
1459
+ * assetId: 'video-asset-id',
1460
+ * timestamp: 3.5,
1461
+ * });
1462
+ * console.log(`Thumbnail URL: ${thumbnail.url}`);
1463
+ * ```
1464
+ */
1465
+ getThumbnailAndWait(request: ThumbnailRequest, options?: {
1466
+ pollInterval?: number;
1467
+ timeout?: number;
1468
+ }): Promise<ThumbnailResponse>;
1326
1469
  /**
1327
1470
  * Regenerate a thumbnail for a video (force regeneration even if one exists)
1328
1471
  *
1329
- * Useful for retrying failed thumbnail generation or regenerating with different settings.
1472
+ * Useful for retrying failed thumbnail generation or regenerating with
1473
+ * different settings. Regeneration is async: poll {@link getThumbnail} (or
1474
+ * use {@link getThumbnailAndWait}) at the same timestamp until the new
1475
+ * frame is ready.
1330
1476
  *
1331
1477
  * @example
1332
1478
  * ```typescript
1333
- * const result = await cdn.regenerateThumbnail({
1479
+ * await cdn.regenerateThumbnail({
1334
1480
  * assetId: 'video-asset-id',
1335
1481
  * timestamp: 5, // 5 seconds into the video
1336
1482
  * width: 1280,
1337
1483
  * format: 'jpg',
1338
1484
  * });
1339
- * console.log(`Thumbnail regeneration queued: ${result.status}`);
1485
+ * const thumbnail = await cdn.getThumbnailAndWait({ assetId: 'video-asset-id', timestamp: 5 });
1486
+ * console.log(`Thumbnail URL: ${thumbnail.url}`);
1340
1487
  * ```
1341
1488
  */
1342
1489
  regenerateThumbnail(request: RegenerateThumbnailRequest): Promise<RegenerateThumbnailResponse>;
@@ -1602,6 +1749,29 @@ declare class CDN {
1602
1749
  * ```
1603
1750
  */
1604
1751
  getStorageBreakdown(request?: CdnStorageBreakdownRequest): Promise<CdnStorageBreakdownResponse>;
1752
+ /**
1753
+ * Get total stored bytes for a project or folder, derived assets included.
1754
+ *
1755
+ * Totals uploads plus everything the pipelines derived from them — video renditions, HLS
1756
+ * segments and manifests, thumbnails, GIFs — in one call. Use this to meter a plan limit:
1757
+ * paging `list()` and summing `size` counts uploads only, and an HLS ladder typically runs
1758
+ * well past the size of the source it came from.
1759
+ *
1760
+ * Check `unmeasuredAssets`. A nonzero value means some assets have derivatives whose bytes
1761
+ * have not been measured, so `totalBytes` is a floor rather than the full number.
1762
+ *
1763
+ * @example
1764
+ * ```typescript
1765
+ * const usage = await cdn.getStorageUsage({
1766
+ * projectSlug: 'my-project',
1767
+ * folder: '/customers/acme',
1768
+ * });
1769
+ * console.log(`${usage.totalFormatted} across ${usage.objectCount} objects`);
1770
+ * console.log(` uploads: ${usage.breakdown.originals.bytesFormatted}`);
1771
+ * console.log(` derived: ${usage.breakdown.derived.bytesFormatted}`);
1772
+ * ```
1773
+ */
1774
+ getStorageUsage(request?: CdnStorageUsageRequest): Promise<CdnStorageUsageResponse>;
1605
1775
  private convertUsageDates;
1606
1776
  private convertUsageDataPointDates;
1607
1777
  /**
@@ -1716,6 +1886,33 @@ declare class CDN {
1716
1886
  * });
1717
1887
  * console.log(`Merge job started: ${job.id}`);
1718
1888
  * ```
1889
+ *
1890
+ * @example Composite a screen recording onto a held phone ("POV holding my phone"):
1891
+ * ```typescript
1892
+ * const job = await cdn.createMergeJob({
1893
+ * projectSlug: 'my-project',
1894
+ * inputs: [
1895
+ * { assetId: 'reaction-clip-id' },
1896
+ * {
1897
+ * assetId: 'app-recording-id', // the overlay clip
1898
+ * composite: {
1899
+ * backgroundAssetId: 'holding-phone-shot-id', // image or video
1900
+ * // Corner-pin onto the angled screen (background pixel coords, TL/TR/BL/BR)
1901
+ * quad: {
1902
+ * topLeft: { x: 320, y: 480 },
1903
+ * topRight: { x: 760, y: 500 },
1904
+ * bottomLeft: { x: 300, y: 1400 },
1905
+ * bottomRight: { x: 740, y: 1440 },
1906
+ * },
1907
+ * fit: 'cover',
1908
+ * // Screen area painted #F500FA in the background is keyed out, so the
1909
+ * // recording shows through and fingers over the screen occlude it
1910
+ * chromaKey: { color: '#F500FA', similarity: 0.34, blend: 0.06 },
1911
+ * },
1912
+ * },
1913
+ * ],
1914
+ * });
1915
+ * ```
1719
1916
  */
1720
1917
  createMergeJob(request: CreateMergeJobRequest): Promise<MergeJob>;
1721
1918
  /**
@@ -1874,4 +2071,4 @@ declare class CDN {
1874
2071
  private convertImportFileDates;
1875
2072
  }
1876
2073
 
1877
- export { type Asset, type AssetStatus, type AssetType, type AudioTrackInput, type BundleDownloadUrlRequest, type BundleDownloadUrlResponse, type BundleStatus, CDN, type CancelImportResponse, type CancelMergeJobRequest, type CdnEnvironment, type CdnStorageBreakdownItem, type CdnStorageBreakdownRequest, type CdnStorageBreakdownResponse, type CdnUsageDataPoint, type CdnUsageHistoryRequest, type CdnUsageHistoryResponse, type CdnUsageRequest, type CdnUsageResponse, type ConfirmUploadRequest, type ConfirmUploadResponse, type CreateBundleRequest, type CreateBundleResponse, type CreateFolderRequest, type CreateImportRequest, type CreateImportResponse, type CreateMergeJobRequest, type DeleteAssetRequest, type DeleteAssetsRequest, type DeleteAssetsResponse, type DownloadBundle, type ExtractAudioRequest, type ExtractAudioResponse, type Folder, type FolderListItem, type FolderTreeNode, type GenerateGifRequest, type GetAssetRequest, type GetFolderByPathRequest, type GetFolderRequest, type GetFolderTreeRequest, type GetMergeJobRequest, type GifStatus, type ImageVideoWatermarkOptions, type ImageWatermarkConfig, type ImageWatermarkPosition, type ImageWatermarkSizingMode, type ImportAuthType, type ImportError, type ImportFile, type ImportFileStatus, type ImportJob, type ImportJobStatus, type ImportJobSummary, type ImportPathMode, type ListAssetsRequest, type ListAssetsResponse, type ListBundlesRequest, type ListBundlesResponse, type ListFoldersRequest, type ListFoldersResponse, type ListGifsRequest, type ListImportFilesRequest, type ListImportFilesResponse, type ListImportsRequest, type ListImportsResponse, type ListJobsRequest, type ListJobsResponse, type ListMergeJobsRequest, type ListMergeJobsResponse, type ListPrivateFilesRequest, type ListPrivateFilesResponse, type ListThumbnailsRequest, type ListThumbnailsResponse, type MergeAspectRatio, type MergeInputItem, type MergeJob, type MergeJobWithOutput, type MergeOutputConfig, type MergeOutputFormat, type MergeQuality, type MergeStatus, type MoveAssetsRequest, type MoveAssetsResponse, type MoveFolderRequest, type MoveFolderResponse, type MovePrivateFilesRequest, type MovePrivateFilesResponse, type PrivateDownloadUrlRequest, type PrivateDownloadUrlResponse, type PrivateFile, type PrivateFileStatus, type PrivateUploadUrlRequest, type PrivateUploadUrlResponse, type RegenerateThumbnailRequest, type RegenerateThumbnailResponse, type RetryImportResponse, type StreamingUrls, type TextOverlay, type TextOverlayShadow, type TextOverlayStroke, type TextVideoWatermarkOptions, type ThumbnailRequest, type ThumbnailResponse, type TranscodeJob, type TranscodeVideoRequest, type TranscodingStatus, type TransformOptions, type TrimOptions, type UpdateAssetRequest, type UpdateFolderRequest, type UpdatePrivateFileRequest, type UploadFromUrlRequest, type UploadUrlRequest, type UploadUrlResponse, type VideoCodec, type VideoGif, type VideoOutputFormat, type VideoQuality, type VideoThumbnail, type VideoVariant, type WatermarkOptions };
2074
+ export { type Asset, type AssetStatus, type AssetType, type AudioTrackInput, type BundleDownloadUrlRequest, type BundleDownloadUrlResponse, type BundleStatus, CDN, type CancelImportResponse, type CancelMergeJobRequest, type CdnEnvironment, type CdnStorageBreakdownItem, type CdnStorageBreakdownRequest, type CdnStorageBreakdownResponse, type CdnStorageUsageBucket, type CdnStorageUsageRequest, type CdnStorageUsageResponse, type CdnUsageDataPoint, type CdnUsageHistoryRequest, type CdnUsageHistoryResponse, type CdnUsageRequest, type CdnUsageResponse, type CompositeChromaKey, type CompositeConfig, type CompositePoint, type CompositeQuad, type CompositeRect, type ConfirmUploadRequest, type ConfirmUploadResponse, type CreateBundleRequest, type CreateBundleResponse, type CreateFolderRequest, type CreateImportRequest, type CreateImportResponse, type CreateMergeJobRequest, type DeleteAssetRequest, type DeleteAssetsRequest, type DeleteAssetsResponse, type DownloadBundle, type ExtractAudioRequest, type ExtractAudioResponse, type Folder, type FolderListItem, type FolderTreeNode, type GenerateGifRequest, type GetAssetRequest, type GetFolderByPathRequest, type GetFolderRequest, type GetFolderTreeRequest, type GetMergeJobRequest, type GifStatus, type ImageVideoWatermarkOptions, type ImageWatermarkConfig, type ImageWatermarkPosition, type ImageWatermarkSizingMode, type ImportAuthType, type ImportError, type ImportFile, type ImportFileStatus, type ImportJob, type ImportJobStatus, type ImportJobSummary, type ImportPathMode, type ListAssetsRequest, type ListAssetsResponse, type ListBundlesRequest, type ListBundlesResponse, type ListFoldersRequest, type ListFoldersResponse, type ListGifsRequest, type ListImportFilesRequest, type ListImportFilesResponse, type ListImportsRequest, type ListImportsResponse, type ListJobsRequest, type ListJobsResponse, type ListMergeJobsRequest, type ListMergeJobsResponse, type ListPrivateFilesRequest, type ListPrivateFilesResponse, type ListThumbnailsRequest, type ListThumbnailsResponse, type MergeAspectRatio, type MergeInputItem, type MergeJob, type MergeJobWithOutput, type MergeOutputConfig, type MergeOutputFormat, type MergeQuality, type MergeStatus, type MoveAssetsRequest, type MoveAssetsResponse, type MoveFolderRequest, type MoveFolderResponse, type MovePrivateFilesRequest, type MovePrivateFilesResponse, type PrivateDownloadUrlRequest, type PrivateDownloadUrlResponse, type PrivateFile, type PrivateFileStatus, type PrivateUploadUrlRequest, type PrivateUploadUrlResponse, type RegenerateThumbnailRequest, type RegenerateThumbnailResponse, type RetryImportResponse, type StreamingUrls, type TextOverlay, type TextOverlayShadow, type TextOverlayStroke, type TextVideoWatermarkOptions, type ThumbnailRequest, type ThumbnailResponse, type TranscodeJob, type TranscodeVideoRequest, type TranscodingStatus, type TransformOptions, type TrimOptions, type UpdateAssetRequest, type UpdateFolderRequest, type UpdatePrivateFileRequest, type UploadFromUrlRequest, type UploadUrlRequest, type UploadUrlResponse, type VideoCodec, type VideoGif, type VideoOutputFormat, type VideoQuality, type VideoThumbnail, type VideoVariant, type WatermarkOptions };