@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.
- package/README.md +233 -10
- package/dist/cdn/index.d.mts +239 -26
- package/dist/cdn/index.d.ts +239 -26
- package/dist/cdn/index.js +219 -20
- package/dist/cdn/index.js.map +1 -1
- package/dist/cdn/index.mjs +219 -20
- package/dist/cdn/index.mjs.map +1 -1
- package/dist/extraction/index.d.mts +1 -1
- package/dist/extraction/index.d.ts +1 -1
- package/dist/extraction/index.js +33 -29
- package/dist/extraction/index.js.map +1 -1
- package/dist/extraction/index.mjs +33 -29
- package/dist/extraction/index.mjs.map +1 -1
- package/dist/http-client-Cgie_Rv6.d.mts +25 -0
- package/dist/http-client-Cgie_Rv6.d.ts +25 -0
- package/dist/index.d.mts +1006 -3
- package/dist/index.d.ts +1006 -3
- package/dist/index.js +958 -69
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +957 -70
- package/dist/index.mjs.map +1 -1
- package/dist/mail/index.d.mts +1 -1
- package/dist/mail/index.d.ts +1 -1
- package/dist/mail/index.js +13 -5
- package/dist/mail/index.js.map +1 -1
- package/dist/mail/index.mjs +13 -5
- package/dist/mail/index.mjs.map +1 -1
- package/dist/screenshots/index.d.mts +1 -1
- package/dist/screenshots/index.d.ts +1 -1
- package/dist/screenshots/index.js +32 -26
- package/dist/screenshots/index.js.map +1 -1
- package/dist/screenshots/index.mjs +32 -26
- package/dist/screenshots/index.mjs.map +1 -1
- package/dist/webdata/index.d.mts +1 -1
- package/dist/webdata/index.d.ts +1 -1
- package/dist/webdata/index.js +37 -8
- package/dist/webdata/index.js.map +1 -1
- package/dist/webdata/index.mjs +37 -8
- package/dist/webdata/index.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/http-client-Wr9lXo9_.d.mts +0 -10
- package/dist/http-client-Wr9lXo9_.d.ts +0 -10
package/dist/cdn/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
101
|
+
/** Output format */
|
|
102
|
+
format?: "webp" | "jpeg" | "png" | "avif" | "auto";
|
|
103
|
+
/** Quality 1-100 */
|
|
99
104
|
quality?: number;
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
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(
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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.
|
|
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
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
*
|
|
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
|
-
|
|
273
|
-
|
|
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.
|
|
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;
|