@clipwright/core 0.35.0 → 0.37.0
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 +3 -2
- package/dist/account-actors.d.ts +5 -6
- package/dist/account-actors.js +5 -6
- package/dist/account-actors.js.map +1 -1
- package/dist/actors.d.ts +8 -0
- package/dist/actors.js +2 -0
- package/dist/actors.js.map +1 -1
- package/dist/api-failure.d.ts +1 -1
- package/dist/api-failure.js +2 -2
- package/dist/api-failure.js.map +1 -1
- package/dist/aspect.d.ts +1 -1
- package/dist/aspect.js +5 -5
- package/dist/aspect.js.map +1 -1
- package/dist/assets.d.ts +13 -5
- package/dist/assets.js +7 -6
- package/dist/assets.js.map +1 -1
- package/dist/billing-terms.d.ts +1 -1
- package/dist/billing-terms.js +1 -1
- package/dist/billing-terms.js.map +1 -1
- package/dist/canvas-fit.js +1 -1
- package/dist/canvas-fit.js.map +1 -1
- package/dist/caption-geometry.d.ts +43 -0
- package/dist/caption-geometry.js +41 -0
- package/dist/caption-geometry.js.map +1 -0
- package/dist/contract-dispositions.d.ts +1005 -43
- package/dist/contract-dispositions.js +36 -16
- package/dist/contract-dispositions.js.map +1 -1
- package/dist/contracts/v1/workflow-brief.d.ts +2045 -38
- package/dist/contracts/v1/workflow-brief.js +2 -2
- package/dist/contracts/v1/workflow-brief.js.map +1 -1
- package/dist/contracts/v1/workflow-catalog.d.ts +2196 -72
- package/dist/contracts/v1/workflow-client.d.ts +1249 -70
- package/dist/contracts/v1/workflow-execution.d.ts +3 -3
- package/dist/contracts/v1/workflow-plan.d.ts +3294 -108
- package/dist/contracts/v1/workflow-run-read.d.ts +2 -2
- package/dist/edit-billing.d.ts +1 -0
- package/dist/edit-billing.js +3 -2
- package/dist/edit-billing.js.map +1 -1
- package/dist/edit-candidate.d.ts +2 -2
- package/dist/edit-candidate.js +2 -2
- package/dist/edit-candidate.js.map +1 -1
- package/dist/edit-document.d.ts +22 -0
- package/dist/edit-document.js +3 -0
- package/dist/edit-document.js.map +1 -1
- package/dist/edit-long-form.d.ts +2 -2
- package/dist/edit-long-form.js +3 -3
- package/dist/edit-long-form.js.map +1 -1
- package/dist/edit-revision.d.ts +10 -1
- package/dist/edit-revision.js +1 -1
- package/dist/edit-revision.js.map +1 -1
- package/dist/edit-rules.d.ts +1 -0
- package/dist/edit-rules.js +3 -0
- package/dist/edit-rules.js.map +1 -1
- package/dist/edit-shots.d.ts +2 -0
- package/dist/edit-validate.d.ts +4 -2
- package/dist/edit-validate.js +18 -2
- package/dist/edit-validate.js.map +1 -1
- package/dist/endpoint-notes.js +12 -8
- package/dist/endpoint-notes.js.map +1 -1
- package/dist/endpoints.d.ts +1 -1
- package/dist/endpoints.js +4 -1
- package/dist/endpoints.js.map +1 -1
- package/dist/errors.d.ts +3 -2
- package/dist/errors.js +5 -2
- package/dist/errors.js.map +1 -1
- package/dist/face-billing.d.ts +1 -1
- package/dist/face-billing.js +3 -3
- package/dist/face-billing.js.map +1 -1
- package/dist/face-delivery.d.ts +1 -1
- package/dist/face-delivery.js +1 -1
- package/dist/face-delivery.js.map +1 -1
- package/dist/faceless-media-record.d.ts +28 -1
- package/dist/faceless-media-record.js +16 -2
- package/dist/faceless-media-record.js.map +1 -1
- package/dist/faceless-price.d.ts +25 -6
- package/dist/faceless-price.js +13 -6
- package/dist/faceless-price.js.map +1 -1
- package/dist/faceless-public-plan.d.ts +6 -2
- package/dist/faceless-public-plan.js +11 -7
- package/dist/faceless-public-plan.js.map +1 -1
- package/dist/faceless-public.d.ts +108 -1
- package/dist/faceless-public.js +38 -5
- package/dist/faceless-public.js.map +1 -1
- package/dist/faceless-voices.d.ts +6 -6
- package/dist/faceless-voices.js +5 -5
- package/dist/faceless-voices.js.map +1 -1
- package/dist/feedback.d.ts +29 -0
- package/dist/feedback.js +29 -0
- package/dist/feedback.js.map +1 -0
- package/dist/generations.d.ts +2 -19
- package/dist/generations.js +28 -48
- package/dist/generations.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/long-form-revision.d.ts +8 -0
- package/dist/media-timebase.js +2 -2
- package/dist/media-timebase.js.map +1 -1
- package/dist/openapi.d.ts +4 -3
- package/dist/openapi.js +8 -3
- package/dist/openapi.js.map +1 -1
- package/dist/parts-pricing.d.ts +3 -3
- package/dist/parts-pricing.js +6 -6
- package/dist/parts-pricing.js.map +1 -1
- package/dist/presenter-beats.d.ts +2 -2
- package/dist/presenter-beats.js +2 -2
- package/dist/presenter-beats.js.map +1 -1
- package/dist/prices.js +5 -1
- package/dist/prices.js.map +1 -1
- package/dist/public-responses.d.ts +1 -1
- package/dist/public-responses.js +1 -1
- package/dist/public-responses.js.map +1 -1
- package/dist/rate-limit.d.ts +2 -2
- package/dist/rate-limit.js +2 -2
- package/dist/rate-limit.js.map +1 -1
- package/dist/reference-analysis.d.ts +636 -43
- package/dist/reference-analysis.js +262 -22
- package/dist/reference-analysis.js.map +1 -1
- package/dist/reference-display.d.ts +36 -0
- package/dist/reference-display.js +78 -0
- package/dist/reference-display.js.map +1 -0
- package/dist/reference-kinds.d.ts +3 -1
- package/dist/reference-kinds.js +3 -1
- package/dist/reference-kinds.js.map +1 -1
- package/dist/reference-profile.d.ts +2394 -40
- package/dist/reference-profile.js +192 -17
- package/dist/reference-profile.js.map +1 -1
- package/dist/reference-staging.d.ts +71 -57
- package/dist/reference-staging.js +9 -6
- package/dist/reference-staging.js.map +1 -1
- package/dist/reference-voice.d.ts +1 -1
- package/dist/reference-voice.js +3 -3
- package/dist/reference-voice.js.map +1 -1
- package/dist/render-backend.d.ts +1 -1
- package/dist/resolved-timeline.d.ts +201 -4
- package/dist/resolved-timeline.js +20 -8
- package/dist/resolved-timeline.js.map +1 -1
- package/dist/run-static-warnings.js +1 -1
- package/dist/run-static-warnings.js.map +1 -1
- package/dist/runs.d.ts +3 -3
- package/dist/runs.js +3 -3
- package/dist/runs.js.map +1 -1
- package/dist/scene-place.d.ts +225 -0
- package/dist/scene-place.js +120 -0
- package/dist/scene-place.js.map +1 -0
- package/dist/semantic-inserts.d.ts +1 -1
- package/dist/semantic-inserts.js +2 -2
- package/dist/semantic-inserts.js.map +1 -1
- package/dist/skills.d.ts +4685 -30
- package/dist/skills.js +153 -37
- package/dist/skills.js.map +1 -1
- package/dist/templates/index.d.ts +15 -5
- package/dist/templates/index.js +50 -6
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/size.d.ts +1 -1
- package/dist/templates/size.js +1 -1
- package/dist/templates/size.js.map +1 -1
- package/dist/templates/sounds.js +1 -1
- package/dist/templates/sounds.js.map +1 -1
- package/dist/ugc-call.d.ts +1 -1
- package/dist/ugc-call.js +2 -2
- package/dist/ugc-call.js.map +1 -1
- package/dist/ugc-delivery.d.ts +1 -1
- package/dist/ugc-delivery.js +1 -1
- package/dist/ugc-delivery.js.map +1 -1
- package/dist/ugc-interview.d.ts +122 -0
- package/dist/ugc-interview.js +260 -0
- package/dist/ugc-interview.js.map +1 -0
- package/dist/ugc-layers.d.ts +88 -6
- package/dist/ugc-layers.js +193 -5
- package/dist/ugc-layers.js.map +1 -1
- package/dist/ugc-video-media.d.ts +1 -1
- package/dist/ugc-video-media.js +1 -1
- package/dist/ugc-video-media.js.map +1 -1
- package/dist/uploads.d.ts +2 -2
- package/dist/uploads.js +2 -2
- package/dist/uploads.js.map +1 -1
- package/dist/vendor-response.d.ts +1 -1
- package/dist/vendor-response.js +1 -1
- package/dist/vendor-response.js.map +1 -1
- package/dist/voice-admission.d.ts +1450 -498
- package/dist/voice-admission.js +22 -5
- package/dist/voice-admission.js.map +1 -1
- package/dist/voice-listing.d.ts +4 -4
- package/dist/voice-listing.js +4 -5
- package/dist/voice-listing.js.map +1 -1
- package/dist/voice-pace.js +1 -1
- package/dist/voice-pace.js.map +1 -1
- package/dist/voices.d.ts +12 -12
- package/dist/voices.js +41 -20
- package/dist/voices.js.map +1 -1
- package/dist/workflows/methods/faceless-explainer.d.ts +612 -15
- package/dist/workflows/methods/kinetic-launch.d.ts +612 -15
- package/dist/workflows/methods/kinetic-launch.js +1 -1
- package/dist/workflows/methods/product-image-story.d.ts +612 -15
- package/dist/workflows/methods/reference-remake.d.ts +612 -15
- package/dist/workflows/methods/shared.d.ts +612 -15
- package/dist/workflows/methods/shared.js +2 -2
- package/dist/workflows/methods/shared.js.map +1 -1
- package/dist/workflows/methods/ugc-faq-answer.d.ts +612 -15
- package/dist/workflows/methods/ugc-product-intro.d.ts +612 -15
- package/dist/workflows/methods/website-proof-demo.d.ts +612 -15
- package/dist/workflows/methods/website-proof-demo.js +1 -1
- package/dist/workflows/reference-remake.d.ts +152 -35
- package/dist/workflows/reference-remake.js +188 -23
- package/dist/workflows/reference-remake.js.map +1 -1
- package/dist/workflows/validate.js +4 -4
- package/dist/workflows/validate.js.map +1 -1
- package/package.json +2 -3
- package/CHANGELOG.md +0 -1500
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { compositionReasons, defineWorkflowMethod, nativeQa, verticalOutput } from './shared.js';
|
|
2
2
|
export const websiteProofDemo = defineWorkflowMethod({
|
|
3
|
-
id: 'website-proof-demo', version: 1, content_digest: '
|
|
3
|
+
id: 'website-proof-demo', version: 1, content_digest: '1305004523261f45a279ac91d73b8aafcced3c9e665f9ed68e2d280ce8f5b3f7', authoring_status: 'ready', title: 'Demonstrate a verified website state',
|
|
4
4
|
purpose: 'Show a real action and result using approved captures and claims tied to a known product state.',
|
|
5
5
|
required_inputs: ['audience', 'message', 'script', 'assets'], required_features: ['exact_script', 'verified_captures', 'authored_scenes'],
|
|
6
6
|
script_rules: { structure: ['task', 'action', 'verified_result', 'next_action'], instructions: [
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { type ReferenceProfile } from '../reference-profile.js';
|
|
2
|
+
import { type ReferenceProfileInput as ReferenceProfile } from '../reference-profile.js';
|
|
3
3
|
import type { CapabilityProfile } from '../contracts/v1/workflow-catalog.js';
|
|
4
4
|
import { type WorkflowPlanDocument } from '../contracts/v1/workflow-plan.js';
|
|
5
5
|
import { type WorkflowIssue } from '../contracts/v1/workflow-primitives.js';
|
|
@@ -60,19 +60,24 @@ export declare const planReferenceRemake: z.ZodObject<{
|
|
|
60
60
|
unmapped: "unmapped";
|
|
61
61
|
}>;
|
|
62
62
|
source_kind: z.ZodEnum<{
|
|
63
|
+
banner: "banner";
|
|
63
64
|
before_after_split: "before_after_split";
|
|
64
65
|
callout: "callout";
|
|
65
66
|
checklist: "checklist";
|
|
67
|
+
comment_box: "comment_box";
|
|
66
68
|
compare: "compare";
|
|
69
|
+
concept_card: "concept_card";
|
|
67
70
|
end_card: "end_card";
|
|
68
71
|
insert_media: "insert_media";
|
|
69
72
|
layout_split: "layout_split";
|
|
73
|
+
logo_card: "logo_card";
|
|
70
74
|
lower_third: "lower_third";
|
|
71
75
|
notification: "notification";
|
|
72
76
|
other: "other";
|
|
73
77
|
overlay_media: "overlay_media";
|
|
74
78
|
particles: "particles";
|
|
75
79
|
phone_call: "phone_call";
|
|
80
|
+
price_card: "price_card";
|
|
76
81
|
product_ui: "product_ui";
|
|
77
82
|
progress: "progress";
|
|
78
83
|
quote: "quote";
|
|
@@ -154,6 +159,43 @@ export declare const planReferenceRemake: z.ZodObject<{
|
|
|
154
159
|
reacting: "reacting";
|
|
155
160
|
speaking: "speaking";
|
|
156
161
|
}>;
|
|
162
|
+
key_frame_s: z.ZodOptional<z.ZodNumber>;
|
|
163
|
+
frames_s: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
164
|
+
speaker: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
165
|
+
speakers: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
166
|
+
id: z.ZodString;
|
|
167
|
+
seconds: z.ZodNumber;
|
|
168
|
+
}, z.core.$strict>>>;
|
|
169
|
+
gaze: z.ZodOptional<z.ZodObject<{
|
|
170
|
+
direction: z.ZodEnum<{
|
|
171
|
+
camera: "camera";
|
|
172
|
+
closed: "closed";
|
|
173
|
+
down: "down";
|
|
174
|
+
mixed: "mixed";
|
|
175
|
+
not_visible: "not_visible";
|
|
176
|
+
off_left: "off_left";
|
|
177
|
+
off_right: "off_right";
|
|
178
|
+
up: "up";
|
|
179
|
+
}>;
|
|
180
|
+
frames_s: z.ZodArray<z.ZodNumber>;
|
|
181
|
+
}, z.core.$strict>>;
|
|
182
|
+
gestures: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
183
|
+
kind: z.ZodEnum<{
|
|
184
|
+
both_hands_to_viewer: "both_hands_to_viewer";
|
|
185
|
+
count_on_fingers: "count_on_fingers";
|
|
186
|
+
hold_object: "hold_object";
|
|
187
|
+
open_palms: "open_palms";
|
|
188
|
+
other: "other";
|
|
189
|
+
point_at_element: "point_at_element";
|
|
190
|
+
point_at_viewer: "point_at_viewer";
|
|
191
|
+
shrug: "shrug";
|
|
192
|
+
thumbs_up: "thumbs_up";
|
|
193
|
+
wave: "wave";
|
|
194
|
+
}>;
|
|
195
|
+
at_s: z.ZodNumber;
|
|
196
|
+
label: z.ZodString;
|
|
197
|
+
frames_s: z.ZodArray<z.ZodNumber>;
|
|
198
|
+
}, z.core.$strict>>>;
|
|
157
199
|
}, z.core.$strict>>;
|
|
158
200
|
captions: z.ZodObject<{
|
|
159
201
|
present: z.ZodBoolean;
|
|
@@ -171,6 +213,12 @@ export declare const planReferenceRemake: z.ZodObject<{
|
|
|
171
213
|
punch: "punch";
|
|
172
214
|
}>;
|
|
173
215
|
hidden_under_graphics: z.ZodBoolean;
|
|
216
|
+
box: z.ZodOptional<z.ZodObject<{
|
|
217
|
+
left: z.ZodNumber;
|
|
218
|
+
top: z.ZodNumber;
|
|
219
|
+
width: z.ZodNumber;
|
|
220
|
+
height: z.ZodNumber;
|
|
221
|
+
}, z.core.$strict>>;
|
|
174
222
|
}, z.core.$strict>;
|
|
175
223
|
pacing: z.ZodObject<{
|
|
176
224
|
layer_change_every_s: z.ZodNumber;
|
|
@@ -183,12 +231,6 @@ export declare const planReferenceRemake: z.ZodObject<{
|
|
|
183
231
|
beat_index: z.ZodNumber;
|
|
184
232
|
suggested_substitute: z.ZodString;
|
|
185
233
|
}, z.core.$strict>>;
|
|
186
|
-
analysis: z.ZodObject<{
|
|
187
|
-
models: z.ZodArray<z.ZodString>;
|
|
188
|
-
cost_usd: z.ZodOptional<z.ZodNumber>;
|
|
189
|
-
latency_ms: z.ZodNumber;
|
|
190
|
-
warnings: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
191
|
-
}, z.core.$strict>;
|
|
192
234
|
voice: z.ZodOptional<z.ZodObject<{
|
|
193
235
|
version: z.ZodLiteral<"voice-profile-v1">;
|
|
194
236
|
method: z.ZodObject<{
|
|
@@ -342,19 +384,24 @@ export declare const planReferenceRemake: z.ZodObject<{
|
|
|
342
384
|
beat_index: z.ZodNumber;
|
|
343
385
|
graphic_index: z.ZodNullable<z.ZodNumber>;
|
|
344
386
|
kind: z.ZodEnum<{
|
|
387
|
+
banner: "banner";
|
|
345
388
|
before_after_split: "before_after_split";
|
|
346
389
|
callout: "callout";
|
|
347
390
|
checklist: "checklist";
|
|
391
|
+
comment_box: "comment_box";
|
|
348
392
|
compare: "compare";
|
|
393
|
+
concept_card: "concept_card";
|
|
349
394
|
end_card: "end_card";
|
|
350
395
|
insert_media: "insert_media";
|
|
351
396
|
layout_split: "layout_split";
|
|
397
|
+
logo_card: "logo_card";
|
|
352
398
|
lower_third: "lower_third";
|
|
353
399
|
notification: "notification";
|
|
354
400
|
other: "other";
|
|
355
401
|
overlay_media: "overlay_media";
|
|
356
402
|
particles: "particles";
|
|
357
403
|
phone_call: "phone_call";
|
|
404
|
+
price_card: "price_card";
|
|
358
405
|
product_ui: "product_ui";
|
|
359
406
|
progress: "progress";
|
|
360
407
|
quote: "quote";
|
|
@@ -380,13 +427,13 @@ export declare const planReferenceRemake: z.ZodObject<{
|
|
|
380
427
|
height: z.ZodNumber;
|
|
381
428
|
}, z.core.$strict>>;
|
|
382
429
|
rect_method: z.ZodOptional<z.ZodEnum<{
|
|
383
|
-
"
|
|
384
|
-
"
|
|
385
|
-
"
|
|
386
|
-
"
|
|
387
|
-
"
|
|
388
|
-
"
|
|
389
|
-
"
|
|
430
|
+
"estimated-still": "estimated-still";
|
|
431
|
+
"estimated-video": "estimated-video";
|
|
432
|
+
"frames-box/v1": "frames-box/v1";
|
|
433
|
+
"frames-face/v1": "frames-face/v1";
|
|
434
|
+
"frames-layer/v1": "frames-layer/v1";
|
|
435
|
+
"frames-motion/v1": "frames-motion/v1";
|
|
436
|
+
"frames-split/v1": "frames-split/v1";
|
|
390
437
|
}>>;
|
|
391
438
|
rects: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
392
439
|
t_s: z.ZodNumber;
|
|
@@ -431,13 +478,13 @@ export declare const planReferenceRemake: z.ZodObject<{
|
|
|
431
478
|
displacement: z.ZodOptional<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>;
|
|
432
479
|
scale_from: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
|
|
433
480
|
method: z.ZodOptional<z.ZodEnum<{
|
|
434
|
-
"
|
|
435
|
-
"
|
|
436
|
-
"
|
|
437
|
-
"
|
|
438
|
-
"
|
|
439
|
-
"
|
|
440
|
-
"
|
|
481
|
+
"estimated-still": "estimated-still";
|
|
482
|
+
"estimated-video": "estimated-video";
|
|
483
|
+
"frames-box/v1": "frames-box/v1";
|
|
484
|
+
"frames-face/v1": "frames-face/v1";
|
|
485
|
+
"frames-layer/v1": "frames-layer/v1";
|
|
486
|
+
"frames-motion/v1": "frames-motion/v1";
|
|
487
|
+
"frames-split/v1": "frames-split/v1";
|
|
441
488
|
}>>;
|
|
442
489
|
}, z.core.$strict>>;
|
|
443
490
|
exit: z.ZodOptional<z.ZodObject<{
|
|
@@ -473,13 +520,13 @@ export declare const planReferenceRemake: z.ZodObject<{
|
|
|
473
520
|
duration_s: z.ZodOptional<z.ZodNumber>;
|
|
474
521
|
displacement: z.ZodOptional<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>;
|
|
475
522
|
method: z.ZodOptional<z.ZodEnum<{
|
|
476
|
-
"
|
|
477
|
-
"
|
|
478
|
-
"
|
|
479
|
-
"
|
|
480
|
-
"
|
|
481
|
-
"
|
|
482
|
-
"
|
|
523
|
+
"estimated-still": "estimated-still";
|
|
524
|
+
"estimated-video": "estimated-video";
|
|
525
|
+
"frames-box/v1": "frames-box/v1";
|
|
526
|
+
"frames-face/v1": "frames-face/v1";
|
|
527
|
+
"frames-layer/v1": "frames-layer/v1";
|
|
528
|
+
"frames-motion/v1": "frames-motion/v1";
|
|
529
|
+
"frames-split/v1": "frames-split/v1";
|
|
483
530
|
}>>;
|
|
484
531
|
}, z.core.$strict>>;
|
|
485
532
|
layer: z.ZodOptional<z.ZodObject<{
|
|
@@ -491,13 +538,13 @@ export declare const planReferenceRemake: z.ZodObject<{
|
|
|
491
538
|
unknown: "unknown";
|
|
492
539
|
}>;
|
|
493
540
|
method: z.ZodEnum<{
|
|
494
|
-
"
|
|
495
|
-
"
|
|
496
|
-
"
|
|
497
|
-
"
|
|
498
|
-
"
|
|
499
|
-
"
|
|
500
|
-
"
|
|
541
|
+
"estimated-still": "estimated-still";
|
|
542
|
+
"estimated-video": "estimated-video";
|
|
543
|
+
"frames-box/v1": "frames-box/v1";
|
|
544
|
+
"frames-face/v1": "frames-face/v1";
|
|
545
|
+
"frames-layer/v1": "frames-layer/v1";
|
|
546
|
+
"frames-motion/v1": "frames-motion/v1";
|
|
547
|
+
"frames-split/v1": "frames-split/v1";
|
|
501
548
|
}>;
|
|
502
549
|
covered_by: z.ZodArray<z.ZodEnum<{
|
|
503
550
|
body: "body";
|
|
@@ -594,6 +641,76 @@ export declare const planReferenceRemake: z.ZodObject<{
|
|
|
594
641
|
}>;
|
|
595
642
|
}, z.core.$strict>>;
|
|
596
643
|
}, z.core.$strict>>;
|
|
644
|
+
transcript: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
645
|
+
text: z.ZodString;
|
|
646
|
+
start_s: z.ZodNumber;
|
|
647
|
+
end_s: z.ZodNumber;
|
|
648
|
+
speaker: z.ZodOptional<z.ZodString>;
|
|
649
|
+
}, z.core.$strict>>>;
|
|
650
|
+
transcript_language: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
651
|
+
hook: z.ZodOptional<z.ZodObject<{
|
|
652
|
+
text: z.ZodString;
|
|
653
|
+
end_s: z.ZodNumber;
|
|
654
|
+
caption: z.ZodOptional<z.ZodString>;
|
|
655
|
+
}, z.core.$strict>>;
|
|
656
|
+
speakers: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
657
|
+
id: z.ZodString;
|
|
658
|
+
on_screen: z.ZodBoolean;
|
|
659
|
+
role: z.ZodEnum<{
|
|
660
|
+
guest: "guest";
|
|
661
|
+
interviewer: "interviewer";
|
|
662
|
+
narrator: "narrator";
|
|
663
|
+
other: "other";
|
|
664
|
+
presenter: "presenter";
|
|
665
|
+
}>;
|
|
666
|
+
word_count: z.ZodNumber;
|
|
667
|
+
caption: z.ZodNullable<z.ZodObject<{
|
|
668
|
+
colour: z.ZodNullable<z.ZodString>;
|
|
669
|
+
style: z.ZodString;
|
|
670
|
+
position: z.ZodNullable<z.ZodEnum<{
|
|
671
|
+
bottom: "bottom";
|
|
672
|
+
lower: "lower";
|
|
673
|
+
middle: "middle";
|
|
674
|
+
top: "top";
|
|
675
|
+
}>>;
|
|
676
|
+
box: z.ZodOptional<z.ZodObject<{
|
|
677
|
+
left: z.ZodNumber;
|
|
678
|
+
top: z.ZodNumber;
|
|
679
|
+
width: z.ZodNumber;
|
|
680
|
+
height: z.ZodNumber;
|
|
681
|
+
}, z.core.$strict>>;
|
|
682
|
+
}, z.core.$strict>>;
|
|
683
|
+
frames_s: z.ZodArray<z.ZodNumber>;
|
|
684
|
+
}, z.core.$strict>>>;
|
|
685
|
+
segments: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
686
|
+
start_s: z.ZodNumber;
|
|
687
|
+
end_s: z.ZodNumber;
|
|
688
|
+
text: z.ZodString;
|
|
689
|
+
speaker: z.ZodString;
|
|
690
|
+
mouth: z.ZodOptional<z.ZodObject<{
|
|
691
|
+
vision: z.ZodNullable<z.ZodEnum<{
|
|
692
|
+
closed: "closed";
|
|
693
|
+
moving: "moving";
|
|
694
|
+
not_visible: "not_visible";
|
|
695
|
+
}>>;
|
|
696
|
+
measured: z.ZodEnum<{
|
|
697
|
+
moving: "moving";
|
|
698
|
+
not_measured: "not_measured";
|
|
699
|
+
still: "still";
|
|
700
|
+
}>;
|
|
701
|
+
score: z.ZodNullable<z.ZodNumber>;
|
|
702
|
+
}, z.core.$strict>>;
|
|
703
|
+
frames_s: z.ZodArray<z.ZodNumber>;
|
|
704
|
+
}, z.core.$strict>>>;
|
|
705
|
+
spellings: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
706
|
+
heard: z.ZodString;
|
|
707
|
+
shown: z.ZodString;
|
|
708
|
+
start_s: z.ZodNumber;
|
|
709
|
+
end_s: z.ZodNumber;
|
|
710
|
+
}, z.core.$strict>>>;
|
|
711
|
+
analysis: z.ZodObject<{
|
|
712
|
+
warnings: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
713
|
+
}, z.core.$strip>;
|
|
597
714
|
}, z.core.$strict>;
|
|
598
715
|
script: z.ZodString;
|
|
599
716
|
actor_id: z.ZodString;
|
|
@@ -5,13 +5,15 @@ import { assetId } from '../asset-identity.js';
|
|
|
5
5
|
import { captionLanguageOf } from '../caption-language.js';
|
|
6
6
|
import { LONG_FORM_QUALIFICATION_MAX_DURATION_SEC } from '../long-form-profile.js';
|
|
7
7
|
import { estimateDurationSec } from '../pacing.js';
|
|
8
|
-
import {
|
|
8
|
+
import { referenceProfileInput } from '../reference-profile.js';
|
|
9
9
|
import { narrationPaceFromVoice, PAUSE_TRIM_CEILING_S, pauseTrimFromVoice } from '../reference-voice.js';
|
|
10
10
|
import { countActorShots, coverWordOf, lexicalTokens, planSemanticInserts, quoteOccurrences } from '../semantic-inserts.js';
|
|
11
11
|
import { actorId, callStage, isNarratedUgcInput, makeUgcInput, makeUgcInputShape, MAX_NARRATED_ACTOR_SHOTS, ugcCall } from '../skills.js';
|
|
12
12
|
import { CALL_PHONE_DEFAULTS, CALL_STAGE_SIDES } from '../ugc-call.js';
|
|
13
|
-
import { templateFaceWarnings, templateMediaOf, templateTextRefusal } from '../templates/index.js';
|
|
14
|
-
import {
|
|
13
|
+
import { TEMPLATE_KINDS, templateFaceWarnings, templateMediaOf, templateTextRefusal } from '../templates/index.js';
|
|
14
|
+
import { CAPTION_POSITION_VERTICAL, captionEdgeAt } from '../caption-geometry.js';
|
|
15
|
+
import { fitPlacedRect, place, plannedFace, presenterPlace, resolvePlace, sharedArea } from '../scene-place.js';
|
|
16
|
+
import { FACE_COVER_SHARE, faceZone, frameCanvasOf, LAYER_ENTERS, MAX_ENTER_OFFSET_S, MAX_OVERLAYS, OVERLAY_POSITIONS, overlayRect, ownPixels, rectsOverlap } from '../ugc-layers.js';
|
|
15
17
|
import { isEnglishScript, measuredPaceOf, PACE_MATCH_SHARE, VOICE_PACE_RANGE, voicesNearestPace } from '../voice-pace.js';
|
|
16
18
|
import { BREAK_TAG_SOURCE, FIXED_PACE_MODELS, resolveTtsModel } from '../voices.js';
|
|
17
19
|
import { workflowPlanDocument } from '../contracts/v1/workflow-plan.js';
|
|
@@ -19,7 +21,7 @@ import { workflowText } from '../contracts/v1/workflow-primitives.js';
|
|
|
19
21
|
import { queryWorkflowMethod } from './catalog.js';
|
|
20
22
|
// The reference-remake compiler (#857 S2a): a profile and an accepted script become a complete plan whose native input is a make_ugc input.
|
|
21
23
|
export const planReferenceRemake = z.strictObject({
|
|
22
|
-
reference_profile:
|
|
24
|
+
reference_profile: referenceProfileInput.describe('The reference_profile analyze_reference returned, or one you wrote to the same schema.'),
|
|
23
25
|
script: workflowText(10_000).describe('The accepted narration, copied into the plan unchanged.'),
|
|
24
26
|
actor_id: actorId, voice: makeUgcInputShape.voice, voice_id: makeUgcInputShape.voice_id,
|
|
25
27
|
assets: z.array(z.strictObject({ asset_id: assetId, media_type: z.enum(['image', 'video']) })).max(12).default([])
|
|
@@ -35,6 +37,36 @@ const TEXT_KINDS = new Set(['title_card', 'tag']);
|
|
|
35
37
|
const MEDIA_KINDS = { overlay_media: 'overlays', insert_media: 'inserts', layout_split: 'layout_windows' };
|
|
36
38
|
const MARKER = 'ref-long-form';
|
|
37
39
|
const GENERATE = 'pass a ready ast_* asset, or make one separately with generate_image or generate_video (its own quote), then compile again';
|
|
40
|
+
/** The saved plan keeps the reference's techniques, not its words: the remake copies no line of it, and the analysis keeps them already. */
|
|
41
|
+
const withoutWords = ({ transcript: _transcript, hook: _hook, ...profile }) => profile;
|
|
42
|
+
/** A layer sits under the captions when its top is at most this share of the height below them and its centre this share of the width off theirs. */
|
|
43
|
+
const UNDER_CAPTIONS = 0.03;
|
|
44
|
+
/** With no caption box measured, a centred layer whose top lies this far below where top-anchored captions start: a line to a short block. */
|
|
45
|
+
const UNMEASURED_CAPTIONS_BELOW = [0.03, 0.15];
|
|
46
|
+
const ENTER_OF = { pop: 'pop', fade: 'fade', slide: 'slide', scale_up: 'scale_up', tilt_in: 'tilt_in' };
|
|
47
|
+
const EXIT_OF = { cut: 'cut', fade: 'fade', slide: 'slide', pop: 'pop_out' };
|
|
48
|
+
const FRAME = { left: 0, top: 0, width: 1, height: 1 };
|
|
49
|
+
const round = (value, places = 4) => Math.round(value * 10 ** places) / 10 ** places;
|
|
50
|
+
const median = (values) => {
|
|
51
|
+
const sorted = [...values].sort((a, b) => a - b), mid = sorted.length >> 1;
|
|
52
|
+
return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
|
|
53
|
+
};
|
|
54
|
+
/** The reference presenter's face while its graphics show (#973 S5): each element's box over its face-unit twin gives the face; freezes stand in. */
|
|
55
|
+
function referenceFaceOf(profile) {
|
|
56
|
+
const staging = profile.staging, { width, height } = profile.source;
|
|
57
|
+
if (!staging)
|
|
58
|
+
return undefined;
|
|
59
|
+
const twins = staging.elements.flatMap(({ rect_hold: rect, rect_hold_face: twin }) => {
|
|
60
|
+
if (!rect || !twin)
|
|
61
|
+
return [];
|
|
62
|
+
const unit = rect.height / twin.height;
|
|
63
|
+
return [{ height: unit, x: rect.left - (twin.left * unit * height) / width, y: rect.top - twin.top * unit }];
|
|
64
|
+
});
|
|
65
|
+
const faces = twins.length ? twins : staging.freezes.map(({ face_box_start: box }) => ({ height: box.height, x: box.left + box.width / 2, y: box.top + box.height / 2 }));
|
|
66
|
+
if (!faces.length)
|
|
67
|
+
return undefined;
|
|
68
|
+
return { face_height: round(median(faces.map(face => face.height))), face_centre: [round(median(faces.map(face => face.x))), round(median(faces.map(face => face.y)))] };
|
|
69
|
+
}
|
|
38
70
|
export function referenceScriptWords(script) {
|
|
39
71
|
const tags = [...script.matchAll(new RegExp(BREAK_TAG_SOURCE, 'g'))].map(match => [match.index, match.index + match[0].length]);
|
|
40
72
|
const words = [];
|
|
@@ -163,7 +195,7 @@ export function compileReferenceRemake(input, capabilities, planSpans = proporti
|
|
|
163
195
|
}
|
|
164
196
|
}
|
|
165
197
|
const templatesOff = executor?.field_dispositions.templates === 'rejected';
|
|
166
|
-
// The reference's caption height, where the
|
|
198
|
+
// The reference's caption height, where the run draws caption_position (#884); otherwise the style's own place.
|
|
167
199
|
const captionsPlaced = executor?.field_dispositions.caption_position === 'implemented';
|
|
168
200
|
// A wide reference asks for the wide view where it can render: honoured field, a library actor (personal ones have no wide view); graphics follow that face (#884 S5d).
|
|
169
201
|
const framing = profile.presenter_framing === 'wide' && executor?.field_dispositions.presenter_framing === 'implemented'
|
|
@@ -213,6 +245,81 @@ export function compileReferenceRemake(input, capabilities, planSpans = proporti
|
|
|
213
245
|
return ranges.filter(range => words.slice(range.start, range.end).some(word => !word.tag));
|
|
214
246
|
};
|
|
215
247
|
const wordAt = (span, share) => quote(Math.min(span.end - 1, span.start + Math.max(1, Math.floor(share * (span.end - span.start)))), 2, span.end);
|
|
248
|
+
// Measured staging drawn as the reference drew it (#973 S5): a box under the captions, on the face or on the frame, its entry from our list.
|
|
249
|
+
const canvas = frameCanvasOf(DEFAULT_ASPECT_RATIO), measuredFace = referenceFaceOf(profile);
|
|
250
|
+
const presenterOk = longForm && executor?.field_dispositions.presenter_place === 'implemented';
|
|
251
|
+
const presenter = measuredFace && presenterOk ? presenterPlace.safeParse(measuredFace).data : undefined;
|
|
252
|
+
if (measuredFace && !presenter) {
|
|
253
|
+
note(0, `the reference presenter's face, ${measuredFace.face_height} of the frame's height`, !longForm
|
|
254
|
+
? 'the actor\'s own framing: framing the presenter needs long-form qualification, not available right now'
|
|
255
|
+
: presenterOk ? 'the actor\'s own framing: the face size is outside what the presenter can be framed to' : 'the actor\'s own framing');
|
|
256
|
+
}
|
|
257
|
+
const face = plannedFace(DEFAULT_ASPECT_RATIO, canvas, framing, presenter);
|
|
258
|
+
/** Elements whose staging the plan draws or names on its own; the rest get the one note below. */
|
|
259
|
+
const drawn = new Set();
|
|
260
|
+
const gaps = { entry: [], exit: [], styles: new Map(), templates: [], inferred: [] };
|
|
261
|
+
// A box never measured on the frames is an estimate, and a note says so (#930 S2).
|
|
262
|
+
const sourceOf = (element) => element.rect_method?.startsWith('estimated') ? 'vision-pass' : 'measured';
|
|
263
|
+
const elementOf = (beat, graphic) => profile.staging?.elements.find(entry => entry.beat_index === beat && entry.graphic_index === graphic && entry.rect_hold);
|
|
264
|
+
const captionsSeen = profile.captions.present && !profile.captions.hidden_under_graphics;
|
|
265
|
+
const underCaptions = (rect) => {
|
|
266
|
+
const centre = rect.left + rect.width / 2, box = profile.captions.box;
|
|
267
|
+
if (!captionsSeen)
|
|
268
|
+
return undefined;
|
|
269
|
+
if (box) {
|
|
270
|
+
const gap = rect.top - box.top - box.height;
|
|
271
|
+
return Math.abs(centre - box.left - box.width / 2) <= UNDER_CAPTIONS && gap >= -0.005 && gap <= UNDER_CAPTIONS ? { gap: round(Math.max(0, gap)) } : undefined;
|
|
272
|
+
}
|
|
273
|
+
// No box: caption lines are centred, and a top-anchored block starts at its position's height while a bottom-anchored one ends there.
|
|
274
|
+
const at = CAPTION_POSITION_VERTICAL[profile.captions.position] / 100, topEdge = captionEdgeAt(at * 100) === 'top';
|
|
275
|
+
const [low, high] = topEdge ? [at + UNMEASURED_CAPTIONS_BELOW[0], at + UNMEASURED_CAPTIONS_BELOW[1]] : [at - 0.005, at + UNDER_CAPTIONS];
|
|
276
|
+
return Math.abs(centre - 0.5) <= UNDER_CAPTIONS && rect.top >= low && rect.top <= high ? {} : undefined;
|
|
277
|
+
};
|
|
278
|
+
/** The element's place on our presenter, or 'face' when it would cover our face; `own` is a template's own rect, which never stands by captions. */
|
|
279
|
+
const stagedPlace = (element, own) => {
|
|
280
|
+
const rect = element.rect_hold, under = own ? undefined : underCaptions(rect);
|
|
281
|
+
const parsed = place.safeParse(under ? { relative_to: 'captions', side: 'below', ...(under.gap === undefined ? {} : { gap: under.gap }), width: round(rect.width) }
|
|
282
|
+
: element.rect_hold_face ? { relative_to: 'face', rect: element.rect_hold_face } : { relative_to: 'frame', rect });
|
|
283
|
+
if (!parsed.success)
|
|
284
|
+
return undefined;
|
|
285
|
+
if (under)
|
|
286
|
+
return { place: parsed.data, inferred: under.gap === undefined };
|
|
287
|
+
const box = resolvePlace(parsed.data, { canvas, face }), shown = own ? fitPlacedRect(box, ownPixels(own, canvas), canvas) : box;
|
|
288
|
+
if (sharedArea(shown, FRAME) < (shown.width * shown.height) / 2)
|
|
289
|
+
return undefined;
|
|
290
|
+
return sharedArea(shown, face) > FACE_COVER_SHARE * face.width * face.height ? 'face' : { place: parsed.data, inferred: false };
|
|
291
|
+
};
|
|
292
|
+
/** The word the reference showed it on, at the same share of our beat's words, and its measured lead or lag on that word. */
|
|
293
|
+
const enterOf = (element, beat, span) => {
|
|
294
|
+
const shown = element.entry?.first_visible_s, source = profile.beats[beat];
|
|
295
|
+
if (shown === undefined)
|
|
296
|
+
return {};
|
|
297
|
+
const said = (profile.transcript ?? []).filter(word => word.start_s >= source.start_s && word.start_s < source.end_s);
|
|
298
|
+
let at = 0;
|
|
299
|
+
said.forEach((word, index) => { if (word.start_s <= shown)
|
|
300
|
+
at = index; });
|
|
301
|
+
const share = said.length ? (at + 0.5) / said.length : Math.min(1, Math.max(0, (shown - source.start_s) / (source.end_s - source.start_s)));
|
|
302
|
+
const found = quote(span.start + Math.min(span.end - span.start - 1, Math.floor(share * (span.end - span.start))), 2, span.end);
|
|
303
|
+
if (!found)
|
|
304
|
+
return {};
|
|
305
|
+
const offset = said.length ? round(Math.min(MAX_ENTER_OFFSET_S, Math.max(-MAX_ENTER_OFFSET_S, shown - said[at].start_s)), 2) : 0;
|
|
306
|
+
return { anchor: found.anchor, cover_words: span.end - found.start, ...(offset ? { enter_offset_s: offset } : {}) };
|
|
307
|
+
};
|
|
308
|
+
const motionOf = (element, beat) => {
|
|
309
|
+
const style = element.entry?.style, leave = element.exit?.style, enter = style && ENTER_OF[style], exit = leave && EXIT_OF[leave];
|
|
310
|
+
const unmapped = (what) => gaps.styles.set(what, [...gaps.styles.get(what) ?? [], beat]);
|
|
311
|
+
if (!style || style === 'unknown')
|
|
312
|
+
gaps.entry.push(beat);
|
|
313
|
+
else if (!enter)
|
|
314
|
+
unmapped(`${style} entries`);
|
|
315
|
+
if (!leave || leave === 'unknown')
|
|
316
|
+
gaps.exit.push(beat);
|
|
317
|
+
else if (!exit)
|
|
318
|
+
unmapped(`${leave} exits`);
|
|
319
|
+
const duration = element.entry?.duration_s;
|
|
320
|
+
return { ...(enter ? { enter, ...(duration ? { enter_duration_s: Math.min(1.5, Math.max(0.05, round(duration, 2))) } : {}) } : {}),
|
|
321
|
+
...(exit && exit !== 'cut' ? { exit } : {}) };
|
|
322
|
+
};
|
|
216
323
|
const spans = planSpans({ profile, words });
|
|
217
324
|
const layers = [];
|
|
218
325
|
const pool = [...request.assets], images = () => pool.filter(asset => asset.media_type === 'image').length;
|
|
@@ -240,29 +347,52 @@ export function compileReferenceRemake(input, capabilities, planSpans = proporti
|
|
|
240
347
|
const kind = graphic.kind;
|
|
241
348
|
if (kind === 'particles' || kind === 'unmapped')
|
|
242
349
|
continue;
|
|
243
|
-
|
|
350
|
+
// A logo card is a brand picture: it shows as an overlay while an image is left in assets, else as the text card it maps to (#973 S5).
|
|
351
|
+
const room = layers.filter(layer => layer.field === 'overlays').length < MAX_OVERLAYS;
|
|
352
|
+
const wantsLogo = graphic.source_kind === 'logo_card' && longForm && !layered, logo = wantsLogo && images() > 0 && room;
|
|
353
|
+
if (wantsLogo && !logo) {
|
|
354
|
+
note(index, `logo_card (${room ? 'no image left in assets' : `a run shows at most ${MAX_OVERLAYS} overlays`})`, 'a text card with its words');
|
|
355
|
+
}
|
|
356
|
+
if (kind in MEDIA_KINDS || logo) {
|
|
357
|
+
const media = logo ? 'logo_card' : kind;
|
|
244
358
|
if (layered) {
|
|
245
359
|
note(index, `${kind} (one media layer per beat)`, 'the first one shows here');
|
|
246
360
|
continue;
|
|
247
361
|
}
|
|
362
|
+
if (MEDIA_KINDS[kind] === 'overlays' && !room) {
|
|
363
|
+
note(index, `${kind} (a run shows at most ${MAX_OVERLAYS} overlays)`, 'the presenter stays on screen on these words');
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
248
366
|
if (pool.length === 0) {
|
|
249
367
|
note(index, `${kind} (no image or video left in assets)`, GENERATE);
|
|
250
368
|
continue;
|
|
251
369
|
}
|
|
252
370
|
if (!longForm) {
|
|
253
|
-
note(index, `${kind} (media layers need long-form qualification
|
|
371
|
+
note(index, `${kind} (media layers need long-form qualification, not available right now)`, 'the presenter stays on screen on these words');
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
const asset = take(logo), field = logo ? 'overlays' : MEDIA_KINDS[kind];
|
|
375
|
+
const { kind: _kind, ...item } = { ...envelope(media), asset_id: asset.asset_id, media_type: asset.media_type };
|
|
376
|
+
const element = field === 'overlays' ? elementOf(index, beat.graphics.indexOf(graphic)) : undefined, staged = element && stagedPlace(element);
|
|
377
|
+
if (element && staged)
|
|
378
|
+
drawn.add(element.id);
|
|
379
|
+
layered = true;
|
|
380
|
+
if (element && staged && staged !== 'face') {
|
|
381
|
+
if (staged.inferred)
|
|
382
|
+
gaps.inferred.push(index);
|
|
383
|
+
layers.push({ field, beat: index, kind: media, item: { ...item, ...enterOf(element, index, span), ...motionOf(element, index), place: staged.place } });
|
|
254
384
|
continue;
|
|
255
385
|
}
|
|
256
|
-
const asset = take(), field = MEDIA_KINDS[kind], base = { ...envelope(kind), asset_id: asset.asset_id, media_type: asset.media_type };
|
|
257
|
-
const { kind: _kind, ...item } = base;
|
|
258
386
|
const position = graphic.position.replace(/^split_/, ''), wanted = OVERLAY_POSITIONS.includes(position) ? position : 'top_right';
|
|
259
387
|
// Off the face: the reference place, else a corner on its side (right by default), a band last.
|
|
260
388
|
const side = wanted.endsWith('left') ? 'left' : 'right', rank = (place) => place.endsWith(side) ? 0 : place.includes('_') ? 1 : 2;
|
|
261
389
|
const clear = (place) => !rectsOverlap(overlayRect(place, frameCanvasOf(DEFAULT_ASPECT_RATIO)), faceZone(DEFAULT_ASPECT_RATIO, framing));
|
|
262
390
|
const placed = clear(wanted) ? wanted : [...OVERLAY_POSITIONS].sort((left, right) => rank(left) - rank(right)).find(clear) ?? wanted;
|
|
263
|
-
if (
|
|
264
|
-
note(index, `${
|
|
265
|
-
|
|
391
|
+
if (staged === 'face')
|
|
392
|
+
note(index, `${media}: its ${sourceOf(element)} place, over the presenter's face`, `${placed}, to keep the face clear`);
|
|
393
|
+
else if (field === 'overlays' && placed !== position)
|
|
394
|
+
note(index, `${media} at ${graphic.position}`, `${placed}, to keep the face clear`);
|
|
395
|
+
layers.push({ field, beat: index, kind: media, item: field === 'overlays' ? { ...item, position: placed }
|
|
266
396
|
: field === 'layout_windows' ? { ...item, layout: 'split' } : item });
|
|
267
397
|
layered = true;
|
|
268
398
|
fullFrame ||= field === 'inserts';
|
|
@@ -270,7 +400,7 @@ export function compileReferenceRemake(input, capabilities, planSpans = proporti
|
|
|
270
400
|
continue;
|
|
271
401
|
}
|
|
272
402
|
if (templatesOff) {
|
|
273
|
-
note(index, `${kind} (templates are
|
|
403
|
+
note(index, `${kind} (templates are not available right now)`, 'the presenter speaks these words without it');
|
|
274
404
|
continue;
|
|
275
405
|
}
|
|
276
406
|
if (placed) {
|
|
@@ -287,9 +417,25 @@ export function compileReferenceRemake(input, capabilities, planSpans = proporti
|
|
|
287
417
|
if ((built.kind === 'phone_call' || built.kind === 'notification') && /[^\p{Script=Latin}\p{N}\p{P}\p{S}\s]/u.test(script)) {
|
|
288
418
|
note(index, `${built.kind} card text stays English ("${built.kind === 'phone_call' ? 'Incoming call' : 'Messages'}")`, 'edit the plan to change it');
|
|
289
419
|
}
|
|
290
|
-
const item = { ...envelope(built.kind),
|
|
420
|
+
const item = { ...envelope(built.kind),
|
|
421
|
+
...(built.placement ? { placement: built.placement } : {}), data: built.data };
|
|
422
|
+
// A measured box over the presenter takes the template whole, drawn as its kind draws with no place of its own (#973 S4, S5).
|
|
423
|
+
const element = elementOf(index, beat.graphics.indexOf(graphic)), definition = TEMPLATE_KINDS.find(entry => entry.kind === built.kind);
|
|
424
|
+
const { position: _place, ...unplaced } = built.data, own = definition?.data.safeParse(unplaced);
|
|
425
|
+
const staged = element && own?.success && (built.placement ?? definition.placements[0]) === 'over'
|
|
426
|
+
? stagedPlace(element, definition.rect(own.data, DEFAULT_ASPECT_RATIO, undefined, framing)) : undefined;
|
|
427
|
+
if (element && staged)
|
|
428
|
+
drawn.add(element.id);
|
|
429
|
+
if (staged)
|
|
430
|
+
item.data = unplaced;
|
|
431
|
+
if (staged === 'face')
|
|
432
|
+
note(index, `${built.kind}: its ${sourceOf(element)} place, over the presenter's face`, `the ${built.kind}'s default place, to keep the face clear`);
|
|
433
|
+
else if (staged) {
|
|
434
|
+
item.place = staged.place;
|
|
435
|
+
gaps.templates.push(index);
|
|
436
|
+
}
|
|
291
437
|
// A reference place over the face gives way to the kind's default, which every kind keeps off it.
|
|
292
|
-
if ('position' in built.data && templateFaceWarnings({ templates: [item], presenter_framing: framing }).length) {
|
|
438
|
+
if (!item.place && 'position' in built.data && templateFaceWarnings({ templates: [item], presenter_framing: framing }).length) {
|
|
293
439
|
const { position: _position, ...data } = built.data;
|
|
294
440
|
item.data = data;
|
|
295
441
|
note(index, `${built.kind} at ${graphic.position}`, `the ${built.kind}'s default place, to keep the face clear`);
|
|
@@ -407,7 +553,7 @@ export function compileReferenceRemake(input, capabilities, planSpans = proporti
|
|
|
407
553
|
if (listening >= 0 && !request.call)
|
|
408
554
|
note(listening, 'the reference\'s call needs your lines', 'the presenter keeps talking');
|
|
409
555
|
else if (listening >= 0 && !longForm)
|
|
410
|
-
note(listening, 'the reference\'s call (a call needs long-form qualification
|
|
556
|
+
note(listening, 'the reference\'s call (a call needs long-form qualification, not available right now)', 'the presenter keeps talking');
|
|
411
557
|
else if (listening >= 0 && !before)
|
|
412
558
|
note(listening, 'the reference\'s call (no sentence end with words after it to open it on)', 'the presenter keeps talking');
|
|
413
559
|
else if (listening >= 0 && before && request.call) {
|
|
@@ -416,18 +562,35 @@ export function compileReferenceRemake(input, capabilities, planSpans = proporti
|
|
|
416
562
|
phone: { ...CALL_PHONE_DEFAULTS, ...request.call.phone }, ...(stage ? { stage } : {}) };
|
|
417
563
|
for (const layer of layers.filter(layer => layer.beat === listening && layer.kind === 'phone_call'))
|
|
418
564
|
layers.splice(layers.indexOf(layer), 1);
|
|
565
|
+
// The call's own phone draws the staging, so the dropped card's word on it goes too.
|
|
566
|
+
for (const entry of notes.filter(entry => entry.beat_index === listening && entry.what.startsWith('phone_call: its ')))
|
|
567
|
+
notes.splice(notes.indexOf(entry), 1);
|
|
419
568
|
}
|
|
420
|
-
// Measured staging
|
|
421
|
-
// so once per beat and kind (rule 3).
|
|
569
|
+
// Measured staging is drawn for overlays, templates over the presenter and the call's phone: any other measured graphic the plan places keeps its
|
|
570
|
+
// default place, and says so once per beat and kind (rule 3).
|
|
422
571
|
const placed = new Set(layers.filter(layer => layer.field !== 'framing' && layer.field !== 'caption_emphasis').map(layer => layer.beat));
|
|
423
572
|
const named = new Set();
|
|
424
573
|
for (const element of profile.staging?.elements ?? []) {
|
|
425
574
|
const graphic = element.graphic_index === null ? undefined : profile.beats[element.beat_index]?.graphics[element.graphic_index];
|
|
426
575
|
const key = `${element.beat_index}:${graphic?.kind}`;
|
|
427
|
-
if (!graphic || !placed.has(element.beat_index) || named.has(key) || (
|
|
576
|
+
if (!graphic || !placed.has(element.beat_index) || named.has(key) || drawn.has(element.id)
|
|
577
|
+
|| (call && element.beat_index === listening && element.kind === 'phone_call'))
|
|
428
578
|
continue;
|
|
429
579
|
named.add(key);
|
|
430
|
-
note(element.beat_index, `${graphic.kind}: its
|
|
580
|
+
note(element.beat_index, `${graphic.kind}: its ${sourceOf(element)} place and entry (reference staging)`, 'its default place; measured staging is drawn for overlays, templates over the presenter and the call\'s phone');
|
|
581
|
+
}
|
|
582
|
+
const beatsOf = (list) => [...new Set(list)].map(beat => beat + 1).join(', ');
|
|
583
|
+
if (gaps.entry.length)
|
|
584
|
+
note(gaps.entry[0], `how the overlays on beats ${beatsOf(gaps.entry)} enter (reference staging did not measure it)`, 'a cut');
|
|
585
|
+
if (gaps.exit.length)
|
|
586
|
+
note(gaps.exit[0], `how the overlays on beats ${beatsOf(gaps.exit)} leave (reference staging did not measure it)`, 'a cut');
|
|
587
|
+
for (const [what, list] of gaps.styles) {
|
|
588
|
+
note(list[0], `${what} of the overlays on beats ${beatsOf(list)}`, what.endsWith('exits') ? 'a cut' : `a cut: overlays enter by ${LAYER_ENTERS.join(', ')}`);
|
|
589
|
+
}
|
|
590
|
+
if (gaps.templates.length)
|
|
591
|
+
note(gaps.templates[0], `the measured entry of the templates on beats ${beatsOf(gaps.templates)}`, 'each template\'s own entry');
|
|
592
|
+
if (gaps.inferred.length) {
|
|
593
|
+
note(gaps.inferred[0], `the reference's caption box, for the graphics on beats ${beatsOf(gaps.inferred)} (not measured)`, `placed under the captions: each sits centred just below where the reference's ${profile.captions.position} captions are`);
|
|
431
594
|
}
|
|
432
595
|
const aggregate = (values, keep, what, substitute) => {
|
|
433
596
|
for (const value of new Set(values.filter((entry) => entry !== undefined && keep(entry)))) {
|
|
@@ -461,7 +624,7 @@ export function compileReferenceRemake(input, capabilities, planSpans = proporti
|
|
|
461
624
|
const music = profile.beats.findIndex(beat => beat.sounds.some(sound => sound.kind === 'music'));
|
|
462
625
|
if (music >= 0)
|
|
463
626
|
note(music, 'the music bed', 'no music; Clipwright adds none yet');
|
|
464
|
-
// The narration chases the presenter's measured pace through the voice's speed
|
|
627
|
+
// The narration chases the presenter's measured pace through the voice's speed; a model without one leaves the voice as the lever.
|
|
465
628
|
const speechModel = resolveTtsModel({ voice: request.voice, voice_id: request.voice_id, script });
|
|
466
629
|
const pace = narrationPaceFromVoice(profile.voice), fixedPace = FIXED_PACE_MODELS.has(speechModel);
|
|
467
630
|
if (pace.kind === 'unmeasured')
|
|
@@ -482,7 +645,7 @@ export function compileReferenceRemake(input, capabilities, planSpans = proporti
|
|
|
482
645
|
else if (pace.clamped) {
|
|
483
646
|
note(0, `narration pace ${pace.target_wps} words/s of the reference presenter (speed ${pace.wanted} wanted)`, `voice_pace ${pace.voice_pace}, the end of the speed range: the narration may run ${pace.wanted > pace.voice_pace ? 'slower' : 'faster'} than the reference`);
|
|
484
647
|
}
|
|
485
|
-
// Speed first, then the residual: pauses longer than the presenter's word-gap medians are cut in silence only
|
|
648
|
+
// Speed first, then the residual: pauses longer than the presenter's word-gap medians are cut in silence only.
|
|
486
649
|
const pauseTrim = pauseTrimFromVoice(profile.voice), pauseTargets = pauseTrim?.targets;
|
|
487
650
|
if (!pauseTargets)
|
|
488
651
|
note(0, 'narration pauses (the reference presenter\'s pauses were not measured)', 'pauses stay as the voice speaks them');
|
|
@@ -500,6 +663,8 @@ export function compileReferenceRemake(input, capabilities, planSpans = proporti
|
|
|
500
663
|
...(captionsPlaced ? { caption_position: profile.captions.position } : {}) } : {}),
|
|
501
664
|
...field('caption_emphasis'), ...field('templates'), ...field('overlays'), ...field('inserts'), ...field('layout_windows'), ...field('framing'),
|
|
502
665
|
...(call ? { call } : {}) };
|
|
666
|
+
if (presenter && isNarratedUgcInput(native))
|
|
667
|
+
native.presenter_place = presenter;
|
|
503
668
|
// The quote refuses customer media without this policy; the note below discloses it (rule 3).
|
|
504
669
|
if (['overlays', 'inserts', 'layout_windows'].some(name => native[name]) || templateMediaOf(native.templates).length) {
|
|
505
670
|
native.broll_policy = 'anyone';
|
|
@@ -589,7 +754,7 @@ export function compileReferenceRemake(input, capabilities, planSpans = proporti
|
|
|
589
754
|
brief: { audience: request.audience ?? 'Viewers of short ads in the style of the reference',
|
|
590
755
|
message: request.message ?? script.slice(words[firstSentence.start].from, words[firstSentence.end - 1].to),
|
|
591
756
|
script, native: { skill: 'make_ugc', input: native },
|
|
592
|
-
reference_profile: { ...profile, music: '', unmapped,
|
|
757
|
+
reference_profile: { ...withoutWords(profile), music: '', unmapped,
|
|
593
758
|
beats: profile.beats.map(beat => ({ ...beat, spoken_summary: '', graphics: beat.graphics.map(graphic => ({ ...graphic, label_hint: '' })) })) } },
|
|
594
759
|
beats, claims: [], assets: [] });
|
|
595
760
|
return document.success ? { kind: 'compiled', document: document.data }
|