@stack0/sdk 0.2.9 → 0.3.1

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.
Files changed (42) hide show
  1. package/README.md +233 -10
  2. package/dist/cdn/index.d.mts +239 -26
  3. package/dist/cdn/index.d.ts +239 -26
  4. package/dist/cdn/index.js +219 -20
  5. package/dist/cdn/index.js.map +1 -1
  6. package/dist/cdn/index.mjs +219 -20
  7. package/dist/cdn/index.mjs.map +1 -1
  8. package/dist/extraction/index.d.mts +1 -1
  9. package/dist/extraction/index.d.ts +1 -1
  10. package/dist/extraction/index.js +33 -29
  11. package/dist/extraction/index.js.map +1 -1
  12. package/dist/extraction/index.mjs +33 -29
  13. package/dist/extraction/index.mjs.map +1 -1
  14. package/dist/http-client-Cgie_Rv6.d.mts +25 -0
  15. package/dist/http-client-Cgie_Rv6.d.ts +25 -0
  16. package/dist/index.d.mts +1006 -3
  17. package/dist/index.d.ts +1006 -3
  18. package/dist/index.js +958 -69
  19. package/dist/index.js.map +1 -1
  20. package/dist/index.mjs +957 -70
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/mail/index.d.mts +1 -1
  23. package/dist/mail/index.d.ts +1 -1
  24. package/dist/mail/index.js +13 -5
  25. package/dist/mail/index.js.map +1 -1
  26. package/dist/mail/index.mjs +13 -5
  27. package/dist/mail/index.mjs.map +1 -1
  28. package/dist/screenshots/index.d.mts +1 -1
  29. package/dist/screenshots/index.d.ts +1 -1
  30. package/dist/screenshots/index.js +32 -26
  31. package/dist/screenshots/index.js.map +1 -1
  32. package/dist/screenshots/index.mjs +32 -26
  33. package/dist/screenshots/index.mjs.map +1 -1
  34. package/dist/webdata/index.d.mts +1 -1
  35. package/dist/webdata/index.d.ts +1 -1
  36. package/dist/webdata/index.js +37 -8
  37. package/dist/webdata/index.js.map +1 -1
  38. package/dist/webdata/index.mjs +37 -8
  39. package/dist/webdata/index.mjs.map +1 -1
  40. package/package.json +1 -1
  41. package/dist/http-client-Wr9lXo9_.d.mts +0 -10
  42. package/dist/http-client-Wr9lXo9_.d.ts +0 -10
@@ -1,4 +1,4 @@
1
- import { H as HttpClientConfig } from '../http-client-Wr9lXo9_.js';
1
+ import { a as HttpClientConfig } from '../http-client-Cgie_Rv6.js';
2
2
 
3
3
  /**
4
4
  * Type definitions for Stack0 CDN API
@@ -92,22 +92,42 @@ interface MoveAssetsResponse {
92
92
  movedCount: number;
93
93
  }
94
94
  interface TransformOptions {
95
+ /** Output width (snapped to nearest allowed width for caching) */
95
96
  width?: number;
97
+ /** Output height */
96
98
  height?: number;
99
+ /** Resize fit mode */
97
100
  fit?: "cover" | "contain" | "fill" | "inside" | "outside";
98
- format?: "webp" | "jpeg" | "png" | "avif";
101
+ /** Output format */
102
+ format?: "webp" | "jpeg" | "png" | "avif" | "auto";
103
+ /** Quality 1-100 */
99
104
  quality?: number;
100
- }
101
- interface GetTransformUrlRequest {
102
- assetId: string;
103
- options: TransformOptions;
104
- }
105
- interface GetTransformUrlResponse {
106
- url: string;
107
- originalUrl: string;
108
- width?: number;
109
- height?: number;
110
- format?: string;
105
+ /** Smart crop position */
106
+ crop?: "attention" | "entropy" | "center" | "top" | "bottom" | "left" | "right";
107
+ /** Manual crop X offset */
108
+ cropX?: number;
109
+ /** Manual crop Y offset */
110
+ cropY?: number;
111
+ /** Manual crop width */
112
+ cropWidth?: number;
113
+ /** Manual crop height */
114
+ cropHeight?: number;
115
+ /** Blur sigma (0.3-100) */
116
+ blur?: number;
117
+ /** Sharpen sigma */
118
+ sharpen?: number;
119
+ /** Brightness adjustment (-100 to 100) */
120
+ brightness?: number;
121
+ /** Saturation adjustment (-100 to 100) */
122
+ saturation?: number;
123
+ /** Convert to grayscale */
124
+ grayscale?: boolean;
125
+ /** Rotation angle (0, 90, 180, 270) */
126
+ rotate?: number;
127
+ /** Flip vertically */
128
+ flip?: boolean;
129
+ /** Flop horizontally */
130
+ flop?: boolean;
111
131
  }
112
132
  interface FolderTreeNode {
113
133
  id: string;
@@ -135,6 +155,95 @@ interface Folder {
135
155
  createdAt: Date;
136
156
  updatedAt: Date | null;
137
157
  }
158
+ type VideoQuality = "360p" | "480p" | "720p" | "1080p" | "1440p" | "2160p";
159
+ type VideoCodec = "h264" | "h265";
160
+ type VideoOutputFormat = "hls" | "mp4";
161
+ type TranscodingStatus = "pending" | "queued" | "processing" | "completed" | "failed" | "cancelled";
162
+ interface VideoVariant {
163
+ quality: VideoQuality;
164
+ codec?: VideoCodec;
165
+ bitrate?: number;
166
+ }
167
+ interface WatermarkOptions {
168
+ type: "image" | "text";
169
+ imageAssetId?: string;
170
+ text?: string;
171
+ position: "top-left" | "top-right" | "bottom-left" | "bottom-right" | "center";
172
+ opacity?: number;
173
+ }
174
+ interface TrimOptions {
175
+ start: number;
176
+ end: number;
177
+ }
178
+ interface TranscodeVideoRequest {
179
+ projectSlug: string;
180
+ assetId: string;
181
+ outputFormat: VideoOutputFormat;
182
+ variants: VideoVariant[];
183
+ watermark?: WatermarkOptions;
184
+ trim?: TrimOptions;
185
+ webhookUrl?: string;
186
+ }
187
+ interface TranscodeJob {
188
+ id: string;
189
+ assetId: string;
190
+ status: TranscodingStatus;
191
+ outputFormat: VideoOutputFormat;
192
+ variants: VideoVariant[];
193
+ progress: number | null;
194
+ error: string | null;
195
+ mediaConvertJobId: string | null;
196
+ createdAt: Date;
197
+ startedAt: Date | null;
198
+ completedAt: Date | null;
199
+ }
200
+ interface ListJobsRequest {
201
+ projectSlug: string;
202
+ assetId?: string;
203
+ status?: TranscodingStatus;
204
+ limit?: number;
205
+ offset?: number;
206
+ }
207
+ interface ListJobsResponse {
208
+ jobs: TranscodeJob[];
209
+ total: number;
210
+ hasMore: boolean;
211
+ }
212
+ interface StreamingUrls {
213
+ hlsUrl: string | null;
214
+ mp4Urls: Array<{
215
+ quality: VideoQuality;
216
+ url: string;
217
+ }>;
218
+ thumbnails: Array<{
219
+ url: string;
220
+ timestamp: number;
221
+ width: number;
222
+ height: number;
223
+ }>;
224
+ }
225
+ interface ThumbnailRequest {
226
+ assetId: string;
227
+ timestamp: number;
228
+ width?: number;
229
+ format?: "jpg" | "png" | "webp";
230
+ }
231
+ interface ThumbnailResponse {
232
+ url: string;
233
+ timestamp: number;
234
+ width: number;
235
+ height: number;
236
+ }
237
+ interface ExtractAudioRequest {
238
+ projectSlug: string;
239
+ assetId: string;
240
+ format: "mp3" | "aac" | "wav";
241
+ bitrate?: number;
242
+ }
243
+ interface ExtractAudioResponse {
244
+ jobId: string;
245
+ status: TranscodingStatus;
246
+ }
138
247
 
139
248
  /**
140
249
  * Stack0 CDN Client
@@ -143,7 +252,8 @@ interface Folder {
143
252
 
144
253
  declare class CDN {
145
254
  private http;
146
- constructor(config: HttpClientConfig);
255
+ private cdnUrl?;
256
+ constructor(config: HttpClientConfig, cdnUrl?: string);
147
257
  /**
148
258
  * Generate a presigned URL for uploading a file
149
259
  *
@@ -263,23 +373,32 @@ declare class CDN {
263
373
  */
264
374
  move(request: MoveAssetsRequest): Promise<MoveAssetsResponse>;
265
375
  /**
266
- * Get a transformed image URL
376
+ * Get a transformed image URL (client-side, no API call)
267
377
  *
268
378
  * @example
269
379
  * ```typescript
270
- * const { url } = await cdn.getTransformUrl({
271
- * assetId: 'asset-id',
272
- * options: {
273
- * width: 800,
274
- * height: 600,
275
- * fit: 'cover',
276
- * format: 'webp',
277
- * quality: 80,
278
- * },
380
+ * // Using asset's cdnUrl directly
381
+ * const url = cdn.getTransformUrl(asset.cdnUrl, {
382
+ * width: 800,
383
+ * height: 600,
384
+ * fit: 'cover',
385
+ * format: 'webp',
386
+ * quality: 80,
279
387
  * });
388
+ *
389
+ * // Or using cdnUrl from SDK config + s3Key
390
+ * const url = cdn.getTransformUrl(asset.s3Key, { width: 400 });
280
391
  * ```
281
392
  */
282
- getTransformUrl(request: GetTransformUrlRequest): Promise<GetTransformUrlResponse>;
393
+ getTransformUrl(assetUrlOrS3Key: string, options: TransformOptions): string;
394
+ /**
395
+ * Build transform query parameters
396
+ */
397
+ private buildTransformQuery;
398
+ /**
399
+ * Find the nearest allowed width for optimal caching
400
+ */
401
+ private getNearestWidth;
283
402
  /**
284
403
  * Get folder tree for navigation
285
404
  *
@@ -317,6 +436,100 @@ declare class CDN {
317
436
  }>;
318
437
  private convertAssetDates;
319
438
  private convertFolderDates;
439
+ /**
440
+ * Start a video transcoding job
441
+ *
442
+ * @example
443
+ * ```typescript
444
+ * const job = await cdn.transcode({
445
+ * projectSlug: 'my-project',
446
+ * assetId: 'video-asset-id',
447
+ * outputFormat: 'hls',
448
+ * variants: [
449
+ * { quality: '720p', codec: 'h264' },
450
+ * { quality: '1080p', codec: 'h264' },
451
+ * ],
452
+ * webhookUrl: 'https://your-app.com/webhook',
453
+ * });
454
+ * console.log(`Job started: ${job.id}`);
455
+ * ```
456
+ */
457
+ transcode(request: TranscodeVideoRequest): Promise<TranscodeJob>;
458
+ /**
459
+ * Get a transcoding job by ID
460
+ *
461
+ * @example
462
+ * ```typescript
463
+ * const job = await cdn.getJob('job-id');
464
+ * console.log(`Status: ${job.status}, Progress: ${job.progress}%`);
465
+ * ```
466
+ */
467
+ getJob(jobId: string): Promise<TranscodeJob>;
468
+ /**
469
+ * List transcoding jobs with filters
470
+ *
471
+ * @example
472
+ * ```typescript
473
+ * const { jobs, total } = await cdn.listJobs({
474
+ * projectSlug: 'my-project',
475
+ * status: 'processing',
476
+ * limit: 20,
477
+ * });
478
+ * ```
479
+ */
480
+ listJobs(request: ListJobsRequest): Promise<ListJobsResponse>;
481
+ /**
482
+ * Cancel a pending or processing transcoding job
483
+ *
484
+ * @example
485
+ * ```typescript
486
+ * await cdn.cancelJob('job-id');
487
+ * ```
488
+ */
489
+ cancelJob(jobId: string): Promise<{
490
+ success: boolean;
491
+ }>;
492
+ /**
493
+ * Get streaming URLs for a transcoded video
494
+ *
495
+ * @example
496
+ * ```typescript
497
+ * const urls = await cdn.getStreamingUrls('asset-id');
498
+ * console.log(`HLS URL: ${urls.hlsUrl}`);
499
+ * console.log(`MP4 720p: ${urls.mp4Urls.find(u => u.quality === '720p')?.url}`);
500
+ * ```
501
+ */
502
+ getStreamingUrls(assetId: string): Promise<StreamingUrls>;
503
+ /**
504
+ * Generate a thumbnail from a video at a specific timestamp
505
+ *
506
+ * @example
507
+ * ```typescript
508
+ * const thumbnail = await cdn.getThumbnail({
509
+ * assetId: 'video-asset-id',
510
+ * timestamp: 10.5, // 10.5 seconds into the video
511
+ * width: 320,
512
+ * format: 'webp',
513
+ * });
514
+ * console.log(`Thumbnail URL: ${thumbnail.url}`);
515
+ * ```
516
+ */
517
+ getThumbnail(request: ThumbnailRequest): Promise<ThumbnailResponse>;
518
+ /**
519
+ * Extract audio from a video file
520
+ *
521
+ * @example
522
+ * ```typescript
523
+ * const { jobId } = await cdn.extractAudio({
524
+ * projectSlug: 'my-project',
525
+ * assetId: 'video-asset-id',
526
+ * format: 'mp3',
527
+ * bitrate: 192,
528
+ * });
529
+ * ```
530
+ */
531
+ extractAudio(request: ExtractAudioRequest): Promise<ExtractAudioResponse>;
532
+ private convertJobDates;
320
533
  }
321
534
 
322
- export { type Asset, type AssetStatus, type AssetType, CDN, type ConfirmUploadRequest, type ConfirmUploadResponse, type CreateFolderRequest, type DeleteAssetRequest, type DeleteAssetsRequest, type DeleteAssetsResponse, type Folder, type FolderTreeNode, type GetAssetRequest, type GetFolderTreeRequest, type GetTransformUrlRequest, type GetTransformUrlResponse, type ListAssetsRequest, type ListAssetsResponse, type MoveAssetsRequest, type MoveAssetsResponse, type TransformOptions, type UpdateAssetRequest, type UploadUrlRequest, type UploadUrlResponse };
535
+ export { type Asset, type AssetStatus, type AssetType, CDN, type ConfirmUploadRequest, type ConfirmUploadResponse, type CreateFolderRequest, type DeleteAssetRequest, type DeleteAssetsRequest, type DeleteAssetsResponse, type ExtractAudioRequest, type ExtractAudioResponse, type Folder, type FolderTreeNode, type GetAssetRequest, type GetFolderTreeRequest, type ListAssetsRequest, type ListAssetsResponse, type ListJobsRequest, type ListJobsResponse, type MoveAssetsRequest, type MoveAssetsResponse, type StreamingUrls, type ThumbnailRequest, type ThumbnailResponse, type TranscodeJob, type TranscodeVideoRequest, type TranscodingStatus, type TransformOptions, type TrimOptions, type UpdateAssetRequest, type UploadUrlRequest, type UploadUrlResponse, type VideoCodec, type VideoOutputFormat, type VideoQuality, type VideoVariant, type WatermarkOptions };
package/dist/cdn/index.js CHANGED
@@ -8,19 +8,24 @@ var HttpClient = class {
8
8
  this.apiKey = config.apiKey;
9
9
  this.baseUrl = config.baseUrl || "https://api.stack0.dev/v1";
10
10
  }
11
- getHeaders() {
12
- return {
13
- "Content-Type": "application/json",
11
+ getHeaders(includeContentType = true) {
12
+ const headers = {
14
13
  Authorization: `Bearer ${this.apiKey}`
15
14
  };
15
+ if (includeContentType) {
16
+ headers["Content-Type"] = "application/json";
17
+ }
18
+ return headers;
16
19
  }
17
20
  async request(method, path, body) {
18
21
  const url = `${this.baseUrl}${path}`;
22
+ const bodyString = body === void 0 ? void 0 : JSON.stringify(body);
23
+ const hasBody = bodyString !== void 0;
19
24
  try {
20
25
  const response = await fetch(url, {
21
26
  method,
22
- headers: this.getHeaders(),
23
- body: body ? JSON.stringify(body) : void 0
27
+ headers: this.getHeaders(hasBody),
28
+ body: hasBody ? bodyString : void 0
24
29
  });
25
30
  if (!response.ok) {
26
31
  await this.handleErrorResponse(response);
@@ -64,16 +69,22 @@ var HttpClient = class {
64
69
  async delete(path) {
65
70
  return this.request("DELETE", path);
66
71
  }
72
+ async deleteWithBody(path, body) {
73
+ return this.request("DELETE", path, body);
74
+ }
67
75
  async patch(path, body) {
68
76
  return this.request("PATCH", path, body);
69
77
  }
70
78
  };
71
79
 
72
80
  // src/cdn/client.ts
81
+ var ALLOWED_WIDTHS = [256, 384, 640, 750, 828, 1080, 1200, 1920, 2048, 3840];
73
82
  var CDN = class {
74
83
  http;
75
- constructor(config) {
84
+ cdnUrl;
85
+ constructor(config, cdnUrl) {
76
86
  this.http = new HttpClient(config);
87
+ this.cdnUrl = cdnUrl;
77
88
  }
78
89
  /**
79
90
  * Generate a presigned URL for uploading a file
@@ -194,7 +205,7 @@ var CDN = class {
194
205
  * ```
195
206
  */
196
207
  async delete(id) {
197
- return this.http.delete(`/cdn/assets/${id}`);
208
+ return this.http.deleteWithBody(`/cdn/assets/${id}`, { id });
198
209
  }
199
210
  /**
200
211
  * Delete multiple assets
@@ -253,24 +264,73 @@ var CDN = class {
253
264
  return this.http.post("/cdn/assets/move", request);
254
265
  }
255
266
  /**
256
- * Get a transformed image URL
267
+ * Get a transformed image URL (client-side, no API call)
257
268
  *
258
269
  * @example
259
270
  * ```typescript
260
- * const { url } = await cdn.getTransformUrl({
261
- * assetId: 'asset-id',
262
- * options: {
263
- * width: 800,
264
- * height: 600,
265
- * fit: 'cover',
266
- * format: 'webp',
267
- * quality: 80,
268
- * },
271
+ * // Using asset's cdnUrl directly
272
+ * const url = cdn.getTransformUrl(asset.cdnUrl, {
273
+ * width: 800,
274
+ * height: 600,
275
+ * fit: 'cover',
276
+ * format: 'webp',
277
+ * quality: 80,
269
278
  * });
279
+ *
280
+ * // Or using cdnUrl from SDK config + s3Key
281
+ * const url = cdn.getTransformUrl(asset.s3Key, { width: 400 });
270
282
  * ```
271
283
  */
272
- async getTransformUrl(request) {
273
- return this.http.post("/cdn/transform", request);
284
+ getTransformUrl(assetUrlOrS3Key, options) {
285
+ let baseUrl;
286
+ if (assetUrlOrS3Key.startsWith("http://") || assetUrlOrS3Key.startsWith("https://")) {
287
+ const url = new URL(assetUrlOrS3Key);
288
+ baseUrl = `${url.protocol}//${url.host}${url.pathname}`;
289
+ } else if (this.cdnUrl) {
290
+ const cdnBase = this.cdnUrl.endsWith("/") ? this.cdnUrl.slice(0, -1) : this.cdnUrl;
291
+ baseUrl = `${cdnBase}/${assetUrlOrS3Key}`;
292
+ } else {
293
+ throw new Error("getTransformUrl requires either a full URL or cdnUrl to be configured in Stack0 options");
294
+ }
295
+ const params = this.buildTransformQuery(options);
296
+ if (!params) {
297
+ return baseUrl;
298
+ }
299
+ return `${baseUrl}?${params}`;
300
+ }
301
+ /**
302
+ * Build transform query parameters
303
+ */
304
+ buildTransformQuery(options) {
305
+ const params = new URLSearchParams();
306
+ if (options.format) params.set("f", options.format);
307
+ if (options.quality !== void 0) params.set("q", options.quality.toString());
308
+ if (options.width !== void 0) {
309
+ const width = this.getNearestWidth(options.width);
310
+ params.set("w", width.toString());
311
+ }
312
+ if (options.height !== void 0) params.set("h", options.height.toString());
313
+ if (options.fit) params.set("fit", options.fit);
314
+ if (options.crop) params.set("crop", options.crop);
315
+ if (options.cropX !== void 0) params.set("crop-x", options.cropX.toString());
316
+ if (options.cropY !== void 0) params.set("crop-y", options.cropY.toString());
317
+ if (options.cropWidth !== void 0) params.set("crop-w", options.cropWidth.toString());
318
+ if (options.cropHeight !== void 0) params.set("crop-h", options.cropHeight.toString());
319
+ if (options.blur !== void 0) params.set("blur", options.blur.toString());
320
+ if (options.sharpen !== void 0) params.set("sharpen", options.sharpen.toString());
321
+ if (options.brightness !== void 0) params.set("brightness", options.brightness.toString());
322
+ if (options.saturation !== void 0) params.set("saturation", options.saturation.toString());
323
+ if (options.grayscale) params.set("grayscale", "true");
324
+ if (options.rotate !== void 0) params.set("rotate", options.rotate.toString());
325
+ if (options.flip) params.set("flip", "y");
326
+ if (options.flop) params.set("flop", "x");
327
+ return params.toString();
328
+ }
329
+ /**
330
+ * Find the nearest allowed width for optimal caching
331
+ */
332
+ getNearestWidth(width) {
333
+ return ALLOWED_WIDTHS.reduce((prev, curr) => Math.abs(curr - width) < Math.abs(prev - width) ? curr : prev);
274
334
  }
275
335
  /**
276
336
  * Get folder tree for navigation
@@ -316,7 +376,10 @@ var CDN = class {
316
376
  async deleteFolder(id, deleteContents = false) {
317
377
  const params = new URLSearchParams();
318
378
  if (deleteContents) params.set("deleteContents", "true");
319
- return this.http.delete(`/cdn/folders/${id}?${params.toString()}`);
379
+ return this.http.deleteWithBody(`/cdn/folders/${id}?${params.toString()}`, {
380
+ id,
381
+ deleteContents
382
+ });
320
383
  }
321
384
  convertAssetDates(asset) {
322
385
  if (typeof asset.createdAt === "string") {
@@ -336,6 +399,142 @@ var CDN = class {
336
399
  }
337
400
  return folder;
338
401
  }
402
+ // ============================================================================
403
+ // Video Transcoding Methods
404
+ // ============================================================================
405
+ /**
406
+ * Start a video transcoding job
407
+ *
408
+ * @example
409
+ * ```typescript
410
+ * const job = await cdn.transcode({
411
+ * projectSlug: 'my-project',
412
+ * assetId: 'video-asset-id',
413
+ * outputFormat: 'hls',
414
+ * variants: [
415
+ * { quality: '720p', codec: 'h264' },
416
+ * { quality: '1080p', codec: 'h264' },
417
+ * ],
418
+ * webhookUrl: 'https://your-app.com/webhook',
419
+ * });
420
+ * console.log(`Job started: ${job.id}`);
421
+ * ```
422
+ */
423
+ async transcode(request) {
424
+ const response = await this.http.post("/cdn/video/transcode", request);
425
+ return this.convertJobDates(response);
426
+ }
427
+ /**
428
+ * Get a transcoding job by ID
429
+ *
430
+ * @example
431
+ * ```typescript
432
+ * const job = await cdn.getJob('job-id');
433
+ * console.log(`Status: ${job.status}, Progress: ${job.progress}%`);
434
+ * ```
435
+ */
436
+ async getJob(jobId) {
437
+ const response = await this.http.get(`/cdn/video/jobs/${jobId}`);
438
+ return this.convertJobDates(response);
439
+ }
440
+ /**
441
+ * List transcoding jobs with filters
442
+ *
443
+ * @example
444
+ * ```typescript
445
+ * const { jobs, total } = await cdn.listJobs({
446
+ * projectSlug: 'my-project',
447
+ * status: 'processing',
448
+ * limit: 20,
449
+ * });
450
+ * ```
451
+ */
452
+ async listJobs(request) {
453
+ const params = new URLSearchParams();
454
+ params.set("projectSlug", request.projectSlug);
455
+ if (request.assetId) params.set("assetId", request.assetId);
456
+ if (request.status) params.set("status", request.status);
457
+ if (request.limit) params.set("limit", request.limit.toString());
458
+ if (request.offset) params.set("offset", request.offset.toString());
459
+ const response = await this.http.get(`/cdn/video/jobs?${params.toString()}`);
460
+ return {
461
+ ...response,
462
+ jobs: response.jobs.map((job) => this.convertJobDates(job))
463
+ };
464
+ }
465
+ /**
466
+ * Cancel a pending or processing transcoding job
467
+ *
468
+ * @example
469
+ * ```typescript
470
+ * await cdn.cancelJob('job-id');
471
+ * ```
472
+ */
473
+ async cancelJob(jobId) {
474
+ return this.http.post(`/cdn/video/jobs/${jobId}/cancel`, {});
475
+ }
476
+ /**
477
+ * Get streaming URLs for a transcoded video
478
+ *
479
+ * @example
480
+ * ```typescript
481
+ * const urls = await cdn.getStreamingUrls('asset-id');
482
+ * console.log(`HLS URL: ${urls.hlsUrl}`);
483
+ * console.log(`MP4 720p: ${urls.mp4Urls.find(u => u.quality === '720p')?.url}`);
484
+ * ```
485
+ */
486
+ async getStreamingUrls(assetId) {
487
+ return this.http.get(`/cdn/video/stream/${assetId}`);
488
+ }
489
+ /**
490
+ * Generate a thumbnail from a video at a specific timestamp
491
+ *
492
+ * @example
493
+ * ```typescript
494
+ * const thumbnail = await cdn.getThumbnail({
495
+ * assetId: 'video-asset-id',
496
+ * timestamp: 10.5, // 10.5 seconds into the video
497
+ * width: 320,
498
+ * format: 'webp',
499
+ * });
500
+ * console.log(`Thumbnail URL: ${thumbnail.url}`);
501
+ * ```
502
+ */
503
+ async getThumbnail(request) {
504
+ const params = new URLSearchParams();
505
+ params.set("timestamp", request.timestamp.toString());
506
+ if (request.width) params.set("width", request.width.toString());
507
+ if (request.format) params.set("format", request.format);
508
+ return this.http.get(`/cdn/video/thumbnail/${request.assetId}?${params.toString()}`);
509
+ }
510
+ /**
511
+ * Extract audio from a video file
512
+ *
513
+ * @example
514
+ * ```typescript
515
+ * const { jobId } = await cdn.extractAudio({
516
+ * projectSlug: 'my-project',
517
+ * assetId: 'video-asset-id',
518
+ * format: 'mp3',
519
+ * bitrate: 192,
520
+ * });
521
+ * ```
522
+ */
523
+ async extractAudio(request) {
524
+ return this.http.post("/cdn/video/extract-audio", request);
525
+ }
526
+ convertJobDates(job) {
527
+ if (typeof job.createdAt === "string") {
528
+ job.createdAt = new Date(job.createdAt);
529
+ }
530
+ if (job.startedAt && typeof job.startedAt === "string") {
531
+ job.startedAt = new Date(job.startedAt);
532
+ }
533
+ if (job.completedAt && typeof job.completedAt === "string") {
534
+ job.completedAt = new Date(job.completedAt);
535
+ }
536
+ return job;
537
+ }
339
538
  };
340
539
 
341
540
  exports.CDN = CDN;