blitzreels 0.1.7 → 0.1.9

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 (92) hide show
  1. package/LICENSE +25 -0
  2. package/README.md +73 -5
  3. package/dist/cli-errors.d.ts.map +1 -1
  4. package/dist/cli-runtime.d.ts +1 -1
  5. package/dist/cli-runtime.d.ts.map +1 -1
  6. package/dist/cli-runtime.js +1 -1
  7. package/dist/cli-runtime.js.map +1 -1
  8. package/dist/command-metadata.d.ts.map +1 -1
  9. package/dist/command-metadata.js +2 -0
  10. package/dist/command-metadata.js.map +1 -1
  11. package/dist/command-registry.d.ts.map +1 -1
  12. package/dist/command-registry.js +2 -0
  13. package/dist/command-registry.js.map +1 -1
  14. package/dist/commands/auth-runtime.js +1 -1
  15. package/dist/commands/auth-runtime.js.map +1 -1
  16. package/dist/commands/bulk-delete-runtime.d.ts +12 -0
  17. package/dist/commands/bulk-delete-runtime.d.ts.map +1 -0
  18. package/dist/commands/bulk-delete-runtime.js +21 -0
  19. package/dist/commands/bulk-delete-runtime.js.map +1 -0
  20. package/dist/commands/caption-dry-run.d.ts +1 -0
  21. package/dist/commands/caption-dry-run.d.ts.map +1 -1
  22. package/dist/commands/caption-dry-run.js +32 -0
  23. package/dist/commands/caption-dry-run.js.map +1 -1
  24. package/dist/commands/caption-overrides.d.ts +13 -1
  25. package/dist/commands/caption-overrides.d.ts.map +1 -1
  26. package/dist/commands/caption-overrides.js +35 -2
  27. package/dist/commands/caption-overrides.js.map +1 -1
  28. package/dist/commands/caption-style-commands.d.ts.map +1 -1
  29. package/dist/commands/caption-style-commands.js +13 -3
  30. package/dist/commands/caption-style-commands.js.map +1 -1
  31. package/dist/commands/captions-specs.d.ts.map +1 -1
  32. package/dist/commands/captions-specs.js +18 -2
  33. package/dist/commands/captions-specs.js.map +1 -1
  34. package/dist/commands/captions.js +32 -0
  35. package/dist/commands/captions.js.map +1 -1
  36. package/dist/commands/clips.d.ts +4 -0
  37. package/dist/commands/clips.d.ts.map +1 -1
  38. package/dist/commands/clips.js +775 -7
  39. package/dist/commands/clips.js.map +1 -1
  40. package/dist/commands/command-operation.d.ts +2 -2
  41. package/dist/commands/command-operation.d.ts.map +1 -1
  42. package/dist/commands/command-operation.js +1 -1
  43. package/dist/commands/command-operation.js.map +1 -1
  44. package/dist/commands/editing-workflows.d.ts.map +1 -1
  45. package/dist/commands/editing-workflows.js +24 -6
  46. package/dist/commands/editing-workflows.js.map +1 -1
  47. package/dist/commands/faceless.d.ts.map +1 -1
  48. package/dist/commands/faceless.js +1 -0
  49. package/dist/commands/faceless.js.map +1 -1
  50. package/dist/commands/jobs.d.ts.map +1 -1
  51. package/dist/commands/jobs.js +2 -0
  52. package/dist/commands/jobs.js.map +1 -1
  53. package/dist/commands/media-assets.d.ts +5 -7
  54. package/dist/commands/media-assets.d.ts.map +1 -1
  55. package/dist/commands/media-assets.js +481 -21
  56. package/dist/commands/media-assets.js.map +1 -1
  57. package/dist/commands/media-overlays.d.ts.map +1 -1
  58. package/dist/commands/media-overlays.js +20 -2
  59. package/dist/commands/media-overlays.js.map +1 -1
  60. package/dist/commands/media-runtime.d.ts +4 -7
  61. package/dist/commands/media-runtime.d.ts.map +1 -1
  62. package/dist/commands/media-runtime.js +39 -7
  63. package/dist/commands/media-runtime.js.map +1 -1
  64. package/dist/commands/project-read.d.ts +30 -0
  65. package/dist/commands/project-read.d.ts.map +1 -1
  66. package/dist/commands/project-read.js +183 -6
  67. package/dist/commands/project-read.js.map +1 -1
  68. package/dist/commands/projects.d.ts.map +1 -1
  69. package/dist/commands/projects.js +125 -2
  70. package/dist/commands/projects.js.map +1 -1
  71. package/dist/commands/recording-sets.d.ts +44 -0
  72. package/dist/commands/recording-sets.d.ts.map +1 -0
  73. package/dist/commands/recording-sets.js +385 -0
  74. package/dist/commands/recording-sets.js.map +1 -0
  75. package/dist/commands/system.js +1 -1
  76. package/dist/commands/system.js.map +1 -1
  77. package/dist/commands/text-overlay-spec.js +62 -2
  78. package/dist/commands/text-overlay-spec.js.map +1 -1
  79. package/dist/commands/timeline-runtime.d.ts +30 -0
  80. package/dist/commands/timeline-runtime.d.ts.map +1 -1
  81. package/dist/commands/timeline-runtime.js +181 -42
  82. package/dist/commands/timeline-runtime.js.map +1 -1
  83. package/dist/commands/timeline.d.ts.map +1 -1
  84. package/dist/commands/timeline.js +158 -3
  85. package/dist/commands/timeline.js.map +1 -1
  86. package/dist/index.js +17 -3
  87. package/dist/index.js.map +1 -1
  88. package/dist/preview-first-edit.d.ts.map +1 -1
  89. package/dist/update-notifier.d.ts.map +1 -1
  90. package/dist/update-notifier.js +7 -3
  91. package/dist/update-notifier.js.map +1 -1
  92. package/package.json +6 -3
@@ -1,10 +1,20 @@
1
+ import { CliUsageError } from "../cli-errors.js";
1
2
  import { commandSpec } from "../command-metadata.js";
2
3
  import { requireDeleteConfirmation, } from "../preview-first-edit.js";
4
+ import { assertBulkDeleteSucceeded, requireExactlyOneDeleteSelector, } from "./bulk-delete-runtime.js";
3
5
  export const MEDIA_ASSET_COMMAND_SPECS = [
4
6
  commandSpec({
5
7
  command: ["media", "list"],
6
- usage: "blitzreels media list [--folder-id ID] [--asset-type video|audio|image|document] [--search QUERY] [--limit 100] [--offset 0] [--json]",
7
- flags: ["folder-id", "asset-type", "search", "limit", "offset"],
8
+ usage: "blitzreels media list [--folder-id ID] [--asset-type video|audio|image|document] [--search QUERY] [--min-duration 1] [--orientation landscape|portrait|square] [--limit 100] [--offset 0] [--json]",
9
+ flags: [
10
+ "folder-id",
11
+ "asset-type",
12
+ "search",
13
+ "min-duration",
14
+ "orientation",
15
+ "limit",
16
+ "offset",
17
+ ],
8
18
  }),
9
19
  commandSpec({
10
20
  command: ["media", "get"],
@@ -13,8 +23,18 @@ export const MEDIA_ASSET_COMMAND_SPECS = [
13
23
  }),
14
24
  commandSpec({
15
25
  command: ["media", "delete"],
16
- usage: "blitzreels media delete --asset-id ID --confirm-delete",
17
- flags: ["asset-id", "confirm-delete"],
26
+ usage: "blitzreels media delete (--asset-id ID|--asset-ids ID1,ID2|--search QUERY|--name-prefix PREFIX) --confirm-delete --reason TEXT --idempotency-key KEY [--delete-generated-clips true] [--delete-linked-projects true]",
27
+ flags: [
28
+ "asset-id",
29
+ "asset-ids",
30
+ "search",
31
+ "name-prefix",
32
+ "confirm-delete",
33
+ "reason",
34
+ "idempotency-key",
35
+ "delete-generated-clips",
36
+ "delete-linked-projects",
37
+ ],
18
38
  }),
19
39
  commandSpec({
20
40
  command: ["media", "upload"],
@@ -83,11 +103,24 @@ export const MEDIA_ASSET_COMMAND_SPECS = [
83
103
  }),
84
104
  commandSpec({
85
105
  command: ["media", "shorts", "generate"],
86
- usage: "blitzreels media shorts generate --asset-id ID [--content-profile auto|podcast|tutorial|generic] [--auto-trim-silence true]",
87
- flags: ["asset-id", "content-profile", "auto-trim-silence"],
106
+ usage: "blitzreels media shorts generate --asset-id ID --idempotency-key KEY [--content-profile auto|podcast|tutorial|generic] [--auto-trim-silence true]",
107
+ flags: [
108
+ "asset-id",
109
+ "idempotency-key",
110
+ "content-profile",
111
+ "auto-trim-silence",
112
+ ],
88
113
  description: "Start short suggestion generation for a media asset.",
89
114
  dryRunSupported: false,
90
115
  }),
116
+ commandSpec({
117
+ command: ["media", "shorts", "refresh-thumbnail"],
118
+ usage: "blitzreels media shorts refresh-thumbnail --asset-id ID --suggestion-id ID --idempotency-key KEY [--dry-run] [--json]",
119
+ flags: ["asset-id", "suggestion-id", "idempotency-key"],
120
+ description: "Queue thumbnail regeneration for one short suggestion.",
121
+ mutates: true,
122
+ dryRunSupported: true,
123
+ }),
91
124
  commandSpec({
92
125
  command: ["media", "shorts", "apply"],
93
126
  usage: "blitzreels media shorts apply --asset-id ID --suggestion-id ID --project-id ID [--caption-style-id ID|null] [--mark-saved true] [--automatic-layout-json JSON]",
@@ -102,6 +135,59 @@ export const MEDIA_ASSET_COMMAND_SPECS = [
102
135
  description: "Apply a short suggestion to a project timeline.",
103
136
  dryRunSupported: false,
104
137
  }),
138
+ commandSpec({
139
+ command: ["media", "reframe", "status"],
140
+ usage: "blitzreels media reframe status --asset-id ID [--json]",
141
+ flags: ["asset-id"],
142
+ description: "Inspect automatic layout analysis for a media asset.",
143
+ }),
144
+ commandSpec({
145
+ command: ["media", "reframe", "analyze"],
146
+ usage: "blitzreels media reframe analyze --asset-id ID --idempotency-key KEY [--json]",
147
+ flags: ["asset-id", "idempotency-key"],
148
+ description: "Queue or reuse automatic layout analysis for a media asset.",
149
+ mutates: true,
150
+ dryRunSupported: false,
151
+ }),
152
+ commandSpec({
153
+ command: ["media", "reframe", "apply"],
154
+ usage: "blitzreels media reframe apply --asset-id ID --project-id ID --idempotency-key KEY [--replace-timeline-item-id ID|null] [--trim-start 0 --trim-end 60] [--aspect-ratio 9:16|null] [--content-type-hint auto|podcast|tutorial|generic] [--layout-strategy auto|demo|letterbox|split|focus|speaker_grid|webinar|webinar_stack|gameplay] [--dry-run] [--json]",
155
+ flags: [
156
+ "asset-id",
157
+ "project-id",
158
+ { name: "trim-start", type: "number" },
159
+ { name: "trim-end", type: "number" },
160
+ "idempotency-key",
161
+ "replace-timeline-item-id",
162
+ {
163
+ name: "aspect-ratio",
164
+ enum: ["9:16", "16:9", "1:1", "4:5", "null"],
165
+ },
166
+ {
167
+ name: "content-type-hint",
168
+ enum: ["auto", "podcast", "tutorial", "generic"],
169
+ default: "auto",
170
+ },
171
+ {
172
+ name: "layout-strategy",
173
+ enum: [
174
+ "auto",
175
+ "demo",
176
+ "letterbox",
177
+ "split",
178
+ "focus",
179
+ "speaker_grid",
180
+ "webinar",
181
+ "webinar_stack",
182
+ "gameplay",
183
+ ],
184
+ default: "auto",
185
+ },
186
+ ],
187
+ description: "Apply the managed automatic layout plan to a project.",
188
+ mutates: true,
189
+ dryRunSupported: true,
190
+ }),
105
191
  commandSpec({
106
192
  command: ["media", "folders", "list"],
107
193
  usage: "blitzreels media folders list [--parent-folder-id ID] [--json]",
@@ -133,6 +219,14 @@ export const MEDIA_ASSET_COMMAND_SPECS = [
133
219
  usage: "blitzreels media folders delete --folder-id ID --confirm-delete [--cascade true]",
134
220
  flags: ["folder-id", "confirm-delete", "cascade"],
135
221
  }),
222
+ commandSpec({
223
+ command: ["media", "folders", "empty"],
224
+ usage: "blitzreels media folders empty --folder-id ID --confirm-delete --reason TEXT --idempotency-key KEY [--dry-run] [--json]",
225
+ flags: ["folder-id", "confirm-delete", "reason", "idempotency-key"],
226
+ description: "Delete media inside a folder while retaining the folder.",
227
+ mutates: true,
228
+ dryRunSupported: true,
229
+ }),
136
230
  ];
137
231
  export function createMediaAssetCommandOperation({ helpers, }) {
138
232
  return {
@@ -155,6 +249,13 @@ async function handleMediaAssetCommand({ context, helpers, }) {
155
249
  value: helpers.getStringFlag({ flags, name: "asset-type" }),
156
250
  }),
157
251
  search: helpers.getStringFlag({ flags, name: "search" }),
252
+ minDurationSeconds: helpers.getNumberFlag({
253
+ flags,
254
+ name: "min-duration",
255
+ }),
256
+ orientation: parseMediaOrientation({
257
+ value: helpers.getStringFlag({ flags, name: "orientation" }),
258
+ }),
158
259
  limit: helpers.getNumberFlag({ flags, name: "limit" }),
159
260
  offset: helpers.getNumberFlag({ flags, name: "offset" }),
160
261
  });
@@ -201,15 +302,61 @@ async function handleMediaAssetCommand({ context, helpers, }) {
201
302
  requireDeleteConfirmation({
202
303
  confirmed: helpers.getBooleanFlag({ flags, name: "confirm-delete" }),
203
304
  });
204
- const result = await context.client.media.delete({
205
- assetId: helpers.requireStringFlag({ flags, name: "asset-id" }),
206
- });
207
- helpers.printResult({
208
- json: context.json,
209
- value: result,
210
- human: ["Media asset deleted."],
305
+ const assetIdsFlag = helpers.getStringFlag({ flags, name: "asset-ids" });
306
+ const search = helpers.getStringFlag({ flags, name: "search" });
307
+ const namePrefix = helpers.getStringFlag({ flags, name: "name-prefix" });
308
+ requireExactlyOneDeleteSelector({
309
+ selectors: [
310
+ helpers.getStringFlag({ flags, name: "asset-id" }),
311
+ assetIdsFlag,
312
+ search,
313
+ namePrefix,
314
+ ],
211
315
  });
212
- return true;
316
+ {
317
+ const singleAssetId = helpers.getStringFlag({
318
+ flags,
319
+ name: "asset-id",
320
+ });
321
+ const result = await context.client.media.deleteMany({
322
+ ids: singleAssetId === undefined
323
+ ? parseCommaSeparatedAssetIds({ value: assetIdsFlag })
324
+ : [singleAssetId],
325
+ search: search ?? null,
326
+ namePrefix: namePrefix ?? null,
327
+ confirmDelete: true,
328
+ dryRun: false,
329
+ reason: helpers.requireStringFlag({ flags, name: "reason" }),
330
+ idempotencyKey: helpers.requireStringFlag({
331
+ flags,
332
+ name: "idempotency-key",
333
+ }),
334
+ deleteGeneratedClips: helpers.getBooleanFlag({
335
+ flags,
336
+ name: "delete-generated-clips",
337
+ }),
338
+ deleteLinkedProjects: helpers.getBooleanFlag({
339
+ flags,
340
+ name: "delete-linked-projects",
341
+ }),
342
+ });
343
+ helpers.printResult({
344
+ json: context.json,
345
+ value: result,
346
+ human: [
347
+ `Deleted ${result.deleted_count}/${result.selected_count} media assets.`,
348
+ ...(result.failed_count > 0
349
+ ? [`Failed: ${result.failed_count}.`]
350
+ : []),
351
+ ],
352
+ });
353
+ assertBulkDeleteSucceeded({
354
+ failedCount: result.failed_count,
355
+ failedItems: result.failed_items,
356
+ resourceLabel: "media assets",
357
+ });
358
+ return true;
359
+ }
213
360
  }
214
361
  if (action === "upload") {
215
362
  const result = await helpers.uploadMediaFile({ context });
@@ -218,6 +365,8 @@ async function handleMediaAssetCommand({ context, helpers, }) {
218
365
  value: result,
219
366
  human: [
220
367
  `Media uploaded: ${result.id} ${result.name} (${result.asset_type}, ${result.processing_status})`,
368
+ `Processing run: ${result.processing_run_id ?? "none"}`,
369
+ `Follow-ups: transcription=${result.follow_up.transcription_requested ? "yes" : "no"}, description=${result.follow_up.description_requested ? "yes" : "no"}, short suggestions=${result.follow_up.short_suggestions_requested ? "yes" : "no"}`,
221
370
  ],
222
371
  });
223
372
  return true;
@@ -316,6 +465,11 @@ async function handleMediaAssetCommand({ context, helpers, }) {
316
465
  ...(result.asset.processingError
317
466
  ? [`Processing error: ${result.asset.processingError}`]
318
467
  : []),
468
+ ...(result.short_suggestions_outcome?.no_clip_reason
469
+ ? [
470
+ `Short suggestions: ${String(result.short_suggestions_outcome.no_clip_reason)}`,
471
+ ]
472
+ : []),
319
473
  ],
320
474
  });
321
475
  return true;
@@ -323,11 +477,149 @@ async function handleMediaAssetCommand({ context, helpers, }) {
323
477
  if (action === "shorts") {
324
478
  return handleMediaShortsCommand({ context, helpers, subaction });
325
479
  }
480
+ if (action === "reframe" && subaction === "status") {
481
+ const result = await context.client.media.reframe.status({
482
+ assetId: helpers.requireStringFlag({ flags, name: "asset-id" }),
483
+ });
484
+ helpers.printResult({
485
+ json: context.json,
486
+ value: result,
487
+ human: [
488
+ `Reframe analysis: ${result.status}${result.analysis_version === null ? "" : ` v${result.analysis_version}`}.`,
489
+ ],
490
+ });
491
+ return true;
492
+ }
493
+ if (action === "reframe" && subaction === "analyze") {
494
+ const result = await context.client.media.reframe.analyze({
495
+ assetId: helpers.requireStringFlag({ flags, name: "asset-id" }),
496
+ idempotencyKey: helpers.requireStringFlag({
497
+ flags,
498
+ name: "idempotency-key",
499
+ }),
500
+ });
501
+ helpers.printResult({
502
+ json: context.json,
503
+ value: result,
504
+ human: [
505
+ `Reframe analysis: ${result.status}${result.triggered ? " queued" : " reused"}.`,
506
+ ],
507
+ });
508
+ return true;
509
+ }
510
+ if (action === "reframe" && subaction === "apply") {
511
+ const result = await context.client.media.reframe.apply({
512
+ assetId: helpers.requireStringFlag({ flags, name: "asset-id" }),
513
+ projectId: helpers.requireStringFlag({ flags, name: "project-id" }),
514
+ replaceTimelineItemId: helpers.parseNullableStringFlag({
515
+ flags,
516
+ name: "replace-timeline-item-id",
517
+ }) ?? null,
518
+ aspectRatio: parseReframeAspectRatio({
519
+ value: helpers.parseNullableStringFlag({
520
+ flags,
521
+ name: "aspect-ratio",
522
+ }),
523
+ }),
524
+ trimStartSeconds: helpers.getNumberFlag({ flags, name: "trim-start" }) ?? null,
525
+ trimEndSeconds: helpers.getNumberFlag({ flags, name: "trim-end" }) ?? null,
526
+ contentTypeHint: parseReframeContentTypeHint({
527
+ value: helpers.getStringFlag({ flags, name: "content-type-hint" }),
528
+ }),
529
+ layoutStrategy: parseReframeLayoutStrategy({
530
+ value: helpers.getStringFlag({ flags, name: "layout-strategy" }),
531
+ }),
532
+ idempotencyKey: helpers.requireStringFlag({
533
+ flags,
534
+ name: "idempotency-key",
535
+ }),
536
+ });
537
+ helpers.printResult({
538
+ json: context.json,
539
+ value: result,
540
+ human: [
541
+ `Reframe applied: ${result.timeline_item_ids.length} timeline item(s) in ${result.project_id}.`,
542
+ ],
543
+ });
544
+ return true;
545
+ }
326
546
  if (action === "folders") {
327
547
  return handleMediaFoldersCommand({ context, helpers, subaction });
328
548
  }
329
549
  return false;
330
550
  }
551
+ function parseMediaOrientation({ value, }) {
552
+ if (value === undefined)
553
+ return undefined;
554
+ if (value === "landscape" || value === "portrait" || value === "square") {
555
+ return value;
556
+ }
557
+ throw new CliUsageError({
558
+ code: "invalid_arguments",
559
+ message: "--orientation must be landscape, portrait, or square.",
560
+ });
561
+ }
562
+ function parseReframeContentTypeHint({ value, }) {
563
+ if (value === undefined ||
564
+ value === "auto" ||
565
+ value === "podcast" ||
566
+ value === "tutorial" ||
567
+ value === "generic") {
568
+ return value ?? "auto";
569
+ }
570
+ throw new CliUsageError({
571
+ code: "invalid_arguments",
572
+ message: "--content-type-hint must be auto, podcast, tutorial, or generic.",
573
+ });
574
+ }
575
+ function parseReframeLayoutStrategy({ value, }) {
576
+ const resolved = value ?? "auto";
577
+ if (resolved === "auto" ||
578
+ resolved === "demo" ||
579
+ resolved === "letterbox" ||
580
+ resolved === "split" ||
581
+ resolved === "focus" ||
582
+ resolved === "speaker_grid" ||
583
+ resolved === "webinar" ||
584
+ resolved === "webinar_stack" ||
585
+ resolved === "gameplay") {
586
+ return resolved;
587
+ }
588
+ throw new CliUsageError({
589
+ code: "invalid_arguments",
590
+ message: "Unsupported --layout-strategy value.",
591
+ });
592
+ }
593
+ function parseReframeAspectRatio({ value, }) {
594
+ if (value === null || value === undefined) {
595
+ return null;
596
+ }
597
+ if (value === "9:16" ||
598
+ value === "16:9" ||
599
+ value === "1:1" ||
600
+ value === "4:5") {
601
+ return value;
602
+ }
603
+ throw new CliUsageError({
604
+ code: "invalid_arguments",
605
+ message: "--aspect-ratio must be 9:16, 16:9, 1:1, 4:5, or null.",
606
+ });
607
+ }
608
+ function parseCommaSeparatedAssetIds({ value, }) {
609
+ if (value === undefined)
610
+ return [];
611
+ const ids = value
612
+ .split(",")
613
+ .map((id) => id.trim())
614
+ .filter(Boolean);
615
+ if (ids.length === 0) {
616
+ throw new CliUsageError({
617
+ code: "invalid_arguments",
618
+ message: "--asset-ids must contain at least one ID.",
619
+ });
620
+ }
621
+ return Array.from(new Set(ids));
622
+ }
331
623
  async function handleMediaShortsCommand({ context, helpers, subaction, }) {
332
624
  const flags = context.args.flags;
333
625
  if (subaction === "list") {
@@ -362,6 +654,10 @@ async function handleMediaShortsCommand({ context, helpers, subaction, }) {
362
654
  flags,
363
655
  name: "auto-trim-silence",
364
656
  }),
657
+ idempotencyKey: helpers.requireStringFlag({
658
+ flags,
659
+ name: "idempotency-key",
660
+ }),
365
661
  });
366
662
  helpers.printResult({
367
663
  json: context.json,
@@ -370,6 +666,25 @@ async function handleMediaShortsCommand({ context, helpers, subaction, }) {
370
666
  });
371
667
  return true;
372
668
  }
669
+ if (subaction === "refresh-thumbnail") {
670
+ const result = await context.client.media.shorts.refreshThumbnail({
671
+ assetId: helpers.requireStringFlag({ flags, name: "asset-id" }),
672
+ suggestionId: helpers.requireStringFlag({
673
+ flags,
674
+ name: "suggestion-id",
675
+ }),
676
+ idempotencyKey: helpers.requireStringFlag({
677
+ flags,
678
+ name: "idempotency-key",
679
+ }),
680
+ });
681
+ helpers.printResult({
682
+ json: context.json,
683
+ value: result,
684
+ human: [`Thumbnail regeneration queued: ${result.run_id}`],
685
+ });
686
+ return true;
687
+ }
373
688
  if (subaction === "apply") {
374
689
  const result = await context.client.media.shorts.apply({
375
690
  assetId: helpers.requireStringFlag({ flags, name: "asset-id" }),
@@ -497,6 +812,34 @@ async function handleMediaFoldersCommand({ context, helpers, subaction, }) {
497
812
  });
498
813
  return true;
499
814
  }
815
+ if (subaction === "empty") {
816
+ requireDeleteConfirmation({
817
+ confirmed: helpers.getBooleanFlag({ flags, name: "confirm-delete" }),
818
+ });
819
+ const result = await context.client.media.folders.empty({
820
+ folderId: helpers.requireStringFlag({ flags, name: "folder-id" }),
821
+ confirmDelete: true,
822
+ dryRun: false,
823
+ reason: helpers.requireStringFlag({ flags, name: "reason" }),
824
+ idempotencyKey: helpers.requireStringFlag({
825
+ flags,
826
+ name: "idempotency-key",
827
+ }),
828
+ });
829
+ helpers.printResult({
830
+ json: context.json,
831
+ value: result,
832
+ human: [
833
+ `Deleted ${result.deleted_count}/${result.selected_count} media assets; folder retained.`,
834
+ ],
835
+ });
836
+ assertBulkDeleteSucceeded({
837
+ failedCount: result.failed_count,
838
+ failedItems: result.failed_items,
839
+ resourceLabel: "media assets",
840
+ });
841
+ return true;
842
+ }
500
843
  return false;
501
844
  }
502
845
  function buildMediaAssetDryRun({ context, spec, helpers, }) {
@@ -509,12 +852,46 @@ function buildMediaAssetDryRun({ context, spec, helpers, }) {
509
852
  requireDeleteConfirmation({
510
853
  confirmed: helpers.getBooleanFlag({ flags, name: "confirm-delete" }),
511
854
  });
512
- return helpers.dryRunRequest({
513
- spec,
514
- method: "POST",
515
- path: `/workspace/media/assets/${encodeURIComponent(helpers.requireStringFlag({ flags, name: "asset-id" }))}/delete`,
516
- body: {},
855
+ const assetIdsFlag = helpers.getStringFlag({ flags, name: "asset-ids" });
856
+ const search = helpers.getStringFlag({ flags, name: "search" });
857
+ const namePrefix = helpers.getStringFlag({ flags, name: "name-prefix" });
858
+ requireExactlyOneDeleteSelector({
859
+ selectors: [
860
+ helpers.getStringFlag({ flags, name: "asset-id" }),
861
+ assetIdsFlag,
862
+ search,
863
+ namePrefix,
864
+ ],
517
865
  });
866
+ {
867
+ const singleAssetId = helpers.getStringFlag({
868
+ flags,
869
+ name: "asset-id",
870
+ });
871
+ return helpers.dryRunRequest({
872
+ spec,
873
+ method: "POST",
874
+ path: "/workspace/media/assets/batch-delete",
875
+ body: {
876
+ ids: singleAssetId === undefined
877
+ ? parseCommaSeparatedAssetIds({ value: assetIdsFlag })
878
+ : [singleAssetId],
879
+ search: search ?? null,
880
+ name_prefix: namePrefix ?? null,
881
+ confirm_delete: false,
882
+ dry_run: true,
883
+ reason: helpers.requireStringFlag({ flags, name: "reason" }),
884
+ delete_generated_clips: helpers.getBooleanFlag({
885
+ flags,
886
+ name: "delete-generated-clips",
887
+ }),
888
+ delete_linked_projects: helpers.getBooleanFlag({
889
+ flags,
890
+ name: "delete-linked-projects",
891
+ }),
892
+ },
893
+ });
894
+ }
518
895
  }
519
896
  if (action === "update" || action === "rename") {
520
897
  return helpers.dryRunRequest({
@@ -570,6 +947,60 @@ function buildMediaAssetDryRun({ context, spec, helpers, }) {
570
947
  },
571
948
  });
572
949
  }
950
+ if (action === "shorts" && subaction === "refresh-thumbnail") {
951
+ const assetId = helpers.requireStringFlag({ flags, name: "asset-id" });
952
+ const suggestionId = helpers.requireStringFlag({
953
+ flags,
954
+ name: "suggestion-id",
955
+ });
956
+ return helpers.dryRunRequest({
957
+ spec,
958
+ method: "POST",
959
+ path: `/workspace/media/assets/${encodeURIComponent(assetId)}/short-suggestions/${encodeURIComponent(suggestionId)}/thumbnail`,
960
+ headers: {
961
+ "Idempotency-Key": helpers.requireStringFlag({
962
+ flags,
963
+ name: "idempotency-key",
964
+ }),
965
+ },
966
+ body: {},
967
+ });
968
+ }
969
+ if (action === "reframe" && subaction === "apply") {
970
+ const assetId = helpers.requireStringFlag({ flags, name: "asset-id" });
971
+ return helpers.dryRunRequest({
972
+ spec,
973
+ method: "POST",
974
+ path: `/workspace/media/assets/${encodeURIComponent(assetId)}/reframe-plan/apply`,
975
+ headers: {
976
+ "Idempotency-Key": helpers.requireStringFlag({
977
+ flags,
978
+ name: "idempotency-key",
979
+ }),
980
+ },
981
+ body: {
982
+ project_id: helpers.requireStringFlag({ flags, name: "project-id" }),
983
+ replace_timeline_item_id: helpers.parseNullableStringFlag({
984
+ flags,
985
+ name: "replace-timeline-item-id",
986
+ }) ?? null,
987
+ aspect_ratio: parseReframeAspectRatio({
988
+ value: helpers.parseNullableStringFlag({
989
+ flags,
990
+ name: "aspect-ratio",
991
+ }),
992
+ }),
993
+ trim_start_seconds: helpers.getNumberFlag({ flags, name: "trim-start" }) ?? null,
994
+ trim_end_seconds: helpers.getNumberFlag({ flags, name: "trim-end" }) ?? null,
995
+ content_type_hint: parseReframeContentTypeHint({
996
+ value: helpers.getStringFlag({ flags, name: "content-type-hint" }),
997
+ }),
998
+ layout_strategy: parseReframeLayoutStrategy({
999
+ value: helpers.getStringFlag({ flags, name: "layout-strategy" }),
1000
+ }),
1001
+ },
1002
+ });
1003
+ }
573
1004
  if (action === "folders" &&
574
1005
  (subaction === "rename" || subaction === "update")) {
575
1006
  const folderId = helpers.requireStringFlag({ flags, name: "folder-id" });
@@ -608,6 +1039,28 @@ function buildMediaAssetDryRun({ context, spec, helpers, }) {
608
1039
  body: undefined,
609
1040
  });
610
1041
  }
1042
+ if (action === "folders" && subaction === "empty") {
1043
+ requireDeleteConfirmation({
1044
+ confirmed: helpers.getBooleanFlag({ flags, name: "confirm-delete" }),
1045
+ });
1046
+ const folderId = helpers.requireStringFlag({ flags, name: "folder-id" });
1047
+ return helpers.dryRunRequest({
1048
+ spec,
1049
+ method: "POST",
1050
+ path: `/workspace/media/folders/${encodeURIComponent(folderId)}/empty`,
1051
+ headers: {
1052
+ "Idempotency-Key": helpers.requireStringFlag({
1053
+ flags,
1054
+ name: "idempotency-key",
1055
+ }),
1056
+ },
1057
+ body: {
1058
+ confirm_delete: false,
1059
+ dry_run: true,
1060
+ reason: helpers.requireStringFlag({ flags, name: "reason" }),
1061
+ },
1062
+ });
1063
+ }
611
1064
  return undefined;
612
1065
  }
613
1066
  function matchesMediaAssetCommand({ command }) {
@@ -619,14 +1072,21 @@ function matchesMediaAssetCommand({ command }) {
619
1072
  return false;
620
1073
  }
621
1074
  if (action === "shorts") {
622
- return (subaction === "list" || subaction === "generate" || subaction === "apply");
1075
+ return (subaction === "list" ||
1076
+ subaction === "generate" ||
1077
+ subaction === "apply" ||
1078
+ subaction === "refresh-thumbnail");
1079
+ }
1080
+ if (action === "reframe") {
1081
+ return (subaction === "status" || subaction === "analyze" || subaction === "apply");
623
1082
  }
624
1083
  if (action === "folders") {
625
1084
  return (subaction === "list" ||
626
1085
  subaction === "create" ||
627
1086
  subaction === "rename" ||
628
1087
  subaction === "update" ||
629
- subaction === "delete");
1088
+ subaction === "delete" ||
1089
+ subaction === "empty");
630
1090
  }
631
1091
  return [
632
1092
  "list",