vidspotai-shared 1.0.91 → 1.0.93
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/lib/schemas/demoSpec.schema.d.ts +689 -0
- package/lib/schemas/demoSpec.schema.d.ts.map +1 -0
- package/lib/schemas/demoSpec.schema.js +276 -0
- package/lib/schemas/index.d.ts +1 -0
- package/lib/schemas/index.d.ts.map +1 -1
- package/lib/schemas/index.js +1 -0
- package/lib/schemas/project.schema.d.ts +78 -0
- package/lib/schemas/project.schema.d.ts.map +1 -1
- package/lib/schemas/project.schema.js +55 -1
- package/lib/schemas/videoPlan.schema.d.ts +69 -0
- package/lib/schemas/videoPlan.schema.d.ts.map +1 -1
- package/lib/schemas/videoPlan.schema.js +30 -1
- package/lib/services/agent/editClassifier.d.ts +16 -0
- package/lib/services/agent/editClassifier.d.ts.map +1 -1
- package/lib/services/agent/index.d.ts +1 -0
- package/lib/services/agent/index.d.ts.map +1 -1
- package/lib/services/agent/index.js +1 -0
- package/lib/services/agent/overlayRenderer.d.ts.map +1 -1
- package/lib/services/agent/overlayRenderer.js +8 -0
- package/lib/services/agent/planner/Planner.d.ts.map +1 -1
- package/lib/services/agent/planner/Planner.js +13 -2
- package/lib/services/agent/planner/structuralRules.d.ts.map +1 -1
- package/lib/services/agent/planner/structuralRules.js +6 -1
- package/lib/services/agent/textStylePacks.d.ts +35 -0
- package/lib/services/agent/textStylePacks.d.ts.map +1 -0
- package/lib/services/agent/textStylePacks.js +70 -0
- package/lib/services/agent/tools/composeScene.tool.d.ts +46 -0
- package/lib/services/agent/tools/composeScene.tool.d.ts.map +1 -1
- package/lib/services/agent/tools/estimateCost.tool.d.ts +16 -0
- package/lib/services/agent/tools/estimateCost.tool.d.ts.map +1 -1
- package/lib/services/agent/tools/planVideo.tool.d.ts +27 -0
- package/lib/services/agent/tools/planVideo.tool.d.ts.map +1 -1
- package/lib/services/agent/tools/render.tool.d.ts +16 -0
- package/lib/services/agent/tools/render.tool.d.ts.map +1 -1
- package/lib/services/aiGen/providers/bytedance/bytedance.service.d.ts.map +1 -1
- package/lib/services/aiGen/providers/bytedance/bytedance.service.js +8 -8
- package/lib/services/aiGen/providers/google/google.service.d.ts.map +1 -1
- package/lib/services/aiGen/providers/google/google.service.js +54 -18
- package/lib/services/aiGen/providers/google/googleApiKeys.d.ts +10 -6
- package/lib/services/aiGen/providers/google/googleApiKeys.d.ts.map +1 -1
- package/lib/services/aiGen/providers/google/googleApiKeys.js +10 -6
- package/lib/services/aiGen/providers/google/googleErrors.d.ts +17 -10
- package/lib/services/aiGen/providers/google/googleErrors.d.ts.map +1 -1
- package/lib/services/aiGen/providers/google/googleErrors.js +60 -21
- package/lib/services/aiGen/providers/google/googleKeyPool.d.ts.map +1 -1
- package/lib/services/aiGen/providers/google/googleKeyPool.js +40 -12
- package/lib/services/asr/index.d.ts +1 -0
- package/lib/services/asr/index.d.ts.map +1 -1
- package/lib/services/asr/index.js +1 -0
- package/lib/services/asr/transcribeWithFallback.d.ts +54 -0
- package/lib/services/asr/transcribeWithFallback.d.ts.map +1 -0
- package/lib/services/asr/transcribeWithFallback.js +87 -0
- package/lib/services/editor/designToProject.d.ts +2 -0
- package/lib/services/editor/designToProject.d.ts.map +1 -1
- package/lib/services/editor/designToProject.js +10 -0
- package/lib/services/editor/planToProject.d.ts.map +1 -1
- package/lib/services/editor/planToProject.helpers.d.ts +9 -3
- package/lib/services/editor/planToProject.helpers.d.ts.map +1 -1
- package/lib/services/editor/planToProject.helpers.js +17 -2
- package/lib/services/editor/planToProject.js +21 -3
- package/package.json +1 -1
|
@@ -7,6 +7,40 @@ const logger_1 = require("../../../../utils/logger");
|
|
|
7
7
|
const redis_service_1 = require("../../../redis.service");
|
|
8
8
|
const googleApiKeys_1 = require("./googleApiKeys");
|
|
9
9
|
Object.defineProperty(exports, "googleApiKeyCount", { enumerable: true, get: function () { return googleApiKeys_1.googleApiKeyCount; } });
|
|
10
|
+
/**
|
|
11
|
+
* Atomic per-key check-and-consume for the routing counters (RPM + RPD).
|
|
12
|
+
*
|
|
13
|
+
* Replaces the old GET-then-INCR in pickForSubmit, which was NON-atomic: under
|
|
14
|
+
* concurrent submits every caller read "this key has room" before any of them
|
|
15
|
+
* incremented, so they all piled onto the SAME key (usually the tiny Tier-1
|
|
16
|
+
* key0) past its ceiling while the other key sat idle — then Google 429'd and,
|
|
17
|
+
* with the failover bug, the job just failed. Checking and consuming in one
|
|
18
|
+
* Lua round-trip makes the per-key cap a hard, race-free gate.
|
|
19
|
+
*
|
|
20
|
+
* Returns { 1, newMin, dayUsed } when a slot was consumed, or
|
|
21
|
+
* { 0, minUsed, dayUsed } when this key is already at its min/day cap.
|
|
22
|
+
* A limit of 0 means "no cap" (skip that gate). The minute counter is a fixed
|
|
23
|
+
* 60s window (matches the legacy per-key behavior); the day counter expires at
|
|
24
|
+
* UTC midnight.
|
|
25
|
+
*/
|
|
26
|
+
const CONSUME_KEY_SCRIPT = `
|
|
27
|
+
local minKey = KEYS[1]
|
|
28
|
+
local dayKey = KEYS[2]
|
|
29
|
+
local minLimit = tonumber(ARGV[1])
|
|
30
|
+
local dayLimit = tonumber(ARGV[2])
|
|
31
|
+
local secsTilMidnight = tonumber(ARGV[3])
|
|
32
|
+
local minUsed = tonumber(redis.call('GET', minKey) or '0')
|
|
33
|
+
local dayUsed = tonumber(redis.call('GET', dayKey) or '0')
|
|
34
|
+
if minLimit > 0 and minUsed >= minLimit then return { 0, minUsed, dayUsed } end
|
|
35
|
+
if dayLimit > 0 and dayUsed >= dayLimit then return { 0, minUsed, dayUsed } end
|
|
36
|
+
local newMin = redis.call('INCR', minKey)
|
|
37
|
+
if newMin == 1 then redis.call('EXPIRE', minKey, 60) end
|
|
38
|
+
if dayLimit > 0 then
|
|
39
|
+
local newDay = redis.call('INCR', dayKey)
|
|
40
|
+
if newDay == 1 then redis.call('EXPIRE', dayKey, secsTilMidnight) end
|
|
41
|
+
end
|
|
42
|
+
return { 1, newMin, dayUsed }
|
|
43
|
+
`;
|
|
10
44
|
function utcDateKey() {
|
|
11
45
|
return new Date().toISOString().slice(0, 10);
|
|
12
46
|
}
|
|
@@ -134,27 +168,21 @@ class GoogleKeyPool {
|
|
|
134
168
|
if (candidates.length === 1)
|
|
135
169
|
return candidates[0];
|
|
136
170
|
const date = utcDateKey();
|
|
171
|
+
const secsTilMidnight = secsUntilMidnight();
|
|
137
172
|
for (const entry of candidates) {
|
|
138
173
|
try {
|
|
139
174
|
const perKeyMinLimit = (0, googleApiKeys_1.scaleLimitForTier)(baselineMin, entry.tier, "rpm");
|
|
140
175
|
const perKeyDayLimit = (0, googleApiKeys_1.scaleLimitForTier)(baselineDay, entry.tier, "rpd");
|
|
141
176
|
const dayKey = `gkpool:${entry.id}:${modelId}:day:${date}`;
|
|
142
177
|
const minKey = `gkpool:${entry.id}:${modelId}:min`;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
])
|
|
147
|
-
const dayUsed = dayRaw ? Number(dayRaw) : 0;
|
|
148
|
-
const minUsed = minRaw ? Number(minRaw) : 0;
|
|
149
|
-
const dayOk = perKeyDayLimit <= 0 || dayUsed < perKeyDayLimit;
|
|
150
|
-
const minOk = perKeyMinLimit <= 0 || minUsed < perKeyMinLimit;
|
|
151
|
-
if (dayOk && minOk) {
|
|
152
|
-
await this.consume(entry, modelId, date);
|
|
178
|
+
// Atomic check-and-consume: no GET-then-INCR race, so two concurrent
|
|
179
|
+
// submits can't both think key0 has room and oversubscribe it.
|
|
180
|
+
const res = (await client.eval(CONSUME_KEY_SCRIPT, 2, minKey, dayKey, String(perKeyMinLimit), String(perKeyDayLimit), String(secsTilMidnight)));
|
|
181
|
+
if (res[0] === 1)
|
|
153
182
|
return entry;
|
|
154
|
-
}
|
|
155
183
|
}
|
|
156
184
|
catch (err) {
|
|
157
|
-
logger_1.logger.warn("googleKeyPool: routing
|
|
185
|
+
logger_1.logger.warn("googleKeyPool: routing eval failed, trying next key", {
|
|
158
186
|
keyId: entry.id,
|
|
159
187
|
err: err instanceof Error ? err.message : String(err),
|
|
160
188
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/asr/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/asr/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC"}
|
|
@@ -16,3 +16,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./assemblyai.service"), exports);
|
|
18
18
|
__exportStar(require("./whisper.service"), exports);
|
|
19
|
+
__exportStar(require("./transcribeWithFallback"), exports);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { TranscribeParams, TranscribeResult } from "./assemblyai.service";
|
|
2
|
+
/**
|
|
3
|
+
* Provider-agnostic ASR entry point with automatic AssemblyAI → Whisper
|
|
4
|
+
* fallback. Mirrors the `provider: "auto"` strategy already used by the agent's
|
|
5
|
+
* generate_captions tool, but reusable from the REST controller, the MCP tool,
|
|
6
|
+
* and the picker worker.
|
|
7
|
+
*
|
|
8
|
+
* Selection rule:
|
|
9
|
+
* - AssemblyAI is the PRIMARY (cheaper $0.15/hr, native diarization, URL
|
|
10
|
+
* submission for long audio) — used whenever ASSEMBLYAI_API_KEY is set.
|
|
11
|
+
* - Whisper is the FALLBACK (reuses OPENAI_API_KEY already provisioned;
|
|
12
|
+
* ~2.4× cost, 25 MB / short-audio only, no diarization) — used when the
|
|
13
|
+
* AssemblyAI key is absent OR the AssemblyAI call hard-fails at runtime.
|
|
14
|
+
*
|
|
15
|
+
* Historically the AssemblyAI key was never provisioned (a commented-out
|
|
16
|
+
* placeholder in every env), so the REST transcribe path always threw. Routing
|
|
17
|
+
* through here makes it work today on the existing OpenAI key and auto-upgrades
|
|
18
|
+
* to AssemblyAI the moment its key lands in Doppler — no code change needed.
|
|
19
|
+
*/
|
|
20
|
+
export type AsrProviderUsed = "assemblyai" | "whisper";
|
|
21
|
+
export interface AsrTranscribeOutcome {
|
|
22
|
+
result: TranscribeResult;
|
|
23
|
+
providerUsed: AsrProviderUsed;
|
|
24
|
+
}
|
|
25
|
+
/** True when at least one ASR provider has a usable key. */
|
|
26
|
+
export declare const isAsrConfigured: () => boolean;
|
|
27
|
+
/** The provider that a sync transcribe would START with, given current keys. */
|
|
28
|
+
export declare const primaryAsrProvider: () => AsrProviderUsed;
|
|
29
|
+
/**
|
|
30
|
+
* Credits for a given provider + audio duration. `getCreditUsed` is pure
|
|
31
|
+
* pricing math, but the service constructors key-guard, so we only instantiate
|
|
32
|
+
* the provider whose key is present — which for `providerUsed` it always is,
|
|
33
|
+
* since that provider just ran.
|
|
34
|
+
*/
|
|
35
|
+
export declare const asrCreditUsed: (provider: AsrProviderUsed, audioDurationSec: number, speakerLabels?: boolean) => number;
|
|
36
|
+
/** Whether async (submit + poll) is available — AssemblyAI only. */
|
|
37
|
+
export declare const supportsAsyncAsr: () => boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Submit an async AssemblyAI job. Only valid when {@link supportsAsyncAsr} is
|
|
40
|
+
* true; Whisper has no submission endpoint.
|
|
41
|
+
*/
|
|
42
|
+
export declare const submitAsyncAsr: (params: TranscribeParams) => Promise<{
|
|
43
|
+
id: string;
|
|
44
|
+
}>;
|
|
45
|
+
/**
|
|
46
|
+
* Transcribe synchronously (poll-to-completion for AssemblyAI; single call for
|
|
47
|
+
* Whisper) with automatic fallback. Returns the result plus which provider
|
|
48
|
+
* actually produced it so callers can reconcile credits against the right rate.
|
|
49
|
+
*/
|
|
50
|
+
export declare const transcribeWithFallback: (params: TranscribeParams, opts?: {
|
|
51
|
+
timeoutMs?: number;
|
|
52
|
+
intervalMs?: number;
|
|
53
|
+
}) => Promise<AsrTranscribeOutcome>;
|
|
54
|
+
//# sourceMappingURL=transcribeWithFallback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcribeWithFallback.d.ts","sourceRoot":"","sources":["../../../src/services/asr/transcribeWithFallback.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,sBAAsB,CAAC;AAG9B;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,SAAS,CAAC;AAEvD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,gBAAgB,CAAC;IACzB,YAAY,EAAE,eAAe,CAAC;CAC/B;AAKD,4DAA4D;AAC5D,eAAO,MAAM,eAAe,QAAO,OAA0C,CAAC;AAE9E,gFAAgF;AAChF,eAAO,MAAM,kBAAkB,QAAO,eAMrC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GACxB,UAAU,eAAe,EACzB,kBAAkB,MAAM,EACxB,uBAAqB,KACpB,MAKF,CAAC;AAEF,oEAAoE;AACpE,eAAO,MAAM,gBAAgB,QAAO,OAA0B,CAAC;AAE/D;;;GAGG;AACH,eAAO,MAAM,cAAc,GACzB,QAAQ,gBAAgB,KACvB,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAKxB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GACjC,QAAQ,gBAAgB,EACxB,OAAM;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,KACrD,OAAO,CAAC,oBAAoB,CAmC9B,CAAC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.transcribeWithFallback = exports.submitAsyncAsr = exports.supportsAsyncAsr = exports.asrCreditUsed = exports.primaryAsrProvider = exports.isAsrConfigured = void 0;
|
|
4
|
+
const logger_1 = require("../../utils/logger");
|
|
5
|
+
const assemblyai_service_1 = require("./assemblyai.service");
|
|
6
|
+
const whisper_service_1 = require("./whisper.service");
|
|
7
|
+
const hasAssemblyAI = () => !!process.env.ASSEMBLYAI_API_KEY;
|
|
8
|
+
const hasWhisper = () => !!process.env.OPENAI_API_KEY;
|
|
9
|
+
/** True when at least one ASR provider has a usable key. */
|
|
10
|
+
const isAsrConfigured = () => hasAssemblyAI() || hasWhisper();
|
|
11
|
+
exports.isAsrConfigured = isAsrConfigured;
|
|
12
|
+
/** The provider that a sync transcribe would START with, given current keys. */
|
|
13
|
+
const primaryAsrProvider = () => {
|
|
14
|
+
if (hasAssemblyAI())
|
|
15
|
+
return "assemblyai";
|
|
16
|
+
if (hasWhisper())
|
|
17
|
+
return "whisper";
|
|
18
|
+
throw new Error("No ASR provider configured — set ASSEMBLYAI_API_KEY (preferred) or OPENAI_API_KEY");
|
|
19
|
+
};
|
|
20
|
+
exports.primaryAsrProvider = primaryAsrProvider;
|
|
21
|
+
/**
|
|
22
|
+
* Credits for a given provider + audio duration. `getCreditUsed` is pure
|
|
23
|
+
* pricing math, but the service constructors key-guard, so we only instantiate
|
|
24
|
+
* the provider whose key is present — which for `providerUsed` it always is,
|
|
25
|
+
* since that provider just ran.
|
|
26
|
+
*/
|
|
27
|
+
const asrCreditUsed = (provider, audioDurationSec, speakerLabels = false) => {
|
|
28
|
+
if (provider === "assemblyai") {
|
|
29
|
+
return new assemblyai_service_1.AssemblyAIService().getCreditUsed(audioDurationSec, speakerLabels);
|
|
30
|
+
}
|
|
31
|
+
return new whisper_service_1.WhisperAsrService().getCreditUsed(audioDurationSec);
|
|
32
|
+
};
|
|
33
|
+
exports.asrCreditUsed = asrCreditUsed;
|
|
34
|
+
/** Whether async (submit + poll) is available — AssemblyAI only. */
|
|
35
|
+
const supportsAsyncAsr = () => hasAssemblyAI();
|
|
36
|
+
exports.supportsAsyncAsr = supportsAsyncAsr;
|
|
37
|
+
/**
|
|
38
|
+
* Submit an async AssemblyAI job. Only valid when {@link supportsAsyncAsr} is
|
|
39
|
+
* true; Whisper has no submission endpoint.
|
|
40
|
+
*/
|
|
41
|
+
const submitAsyncAsr = async (params) => {
|
|
42
|
+
if (!hasAssemblyAI()) {
|
|
43
|
+
throw new Error("Async ASR requires AssemblyAI (ASSEMBLYAI_API_KEY not set)");
|
|
44
|
+
}
|
|
45
|
+
return new assemblyai_service_1.AssemblyAIService().submit(params);
|
|
46
|
+
};
|
|
47
|
+
exports.submitAsyncAsr = submitAsyncAsr;
|
|
48
|
+
/**
|
|
49
|
+
* Transcribe synchronously (poll-to-completion for AssemblyAI; single call for
|
|
50
|
+
* Whisper) with automatic fallback. Returns the result plus which provider
|
|
51
|
+
* actually produced it so callers can reconcile credits against the right rate.
|
|
52
|
+
*/
|
|
53
|
+
const transcribeWithFallback = async (params, opts = {}) => {
|
|
54
|
+
const runWhisper = async () => ({
|
|
55
|
+
result: await new whisper_service_1.WhisperAsrService().transcribe(params),
|
|
56
|
+
providerUsed: "whisper",
|
|
57
|
+
});
|
|
58
|
+
if (hasAssemblyAI()) {
|
|
59
|
+
try {
|
|
60
|
+
const result = await new assemblyai_service_1.AssemblyAIService().transcribe(params, opts);
|
|
61
|
+
// A hard provider error → try the fallback. A "processing" status is a
|
|
62
|
+
// legitimate long-audio timeout (caller polls later), not a failure, so
|
|
63
|
+
// we keep it on the AssemblyAI track.
|
|
64
|
+
if (result.status === "error" && hasWhisper()) {
|
|
65
|
+
logger_1.logger.warn("ASR: AssemblyAI returned error — falling back to Whisper", {
|
|
66
|
+
audioUrl: params.audioUrl,
|
|
67
|
+
err: result.errorMessage,
|
|
68
|
+
});
|
|
69
|
+
return await runWhisper();
|
|
70
|
+
}
|
|
71
|
+
return { result, providerUsed: "assemblyai" };
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
if (!hasWhisper())
|
|
75
|
+
throw err;
|
|
76
|
+
logger_1.logger.warn("ASR: AssemblyAI threw — falling back to Whisper", {
|
|
77
|
+
audioUrl: params.audioUrl,
|
|
78
|
+
err: err instanceof Error ? err.message : String(err),
|
|
79
|
+
});
|
|
80
|
+
return await runWhisper();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (hasWhisper())
|
|
84
|
+
return await runWhisper();
|
|
85
|
+
throw new Error("No ASR provider configured — set ASSEMBLYAI_API_KEY (preferred) or OPENAI_API_KEY");
|
|
86
|
+
};
|
|
87
|
+
exports.transcribeWithFallback = transcribeWithFallback;
|
|
@@ -62,6 +62,8 @@ export interface EditorDesignInput {
|
|
|
62
62
|
trackItemDetailsMap?: Record<string, DesignItemDetails>;
|
|
63
63
|
transitionIds?: string[];
|
|
64
64
|
transitionsMap?: Record<string, DesignTransition>;
|
|
65
|
+
/** Project-level master volume as the editor stores it: 0–100 (100 = unchanged). */
|
|
66
|
+
masterVolume?: number;
|
|
65
67
|
/** Server-write-zone field: editor passes it back as-is on autosave. */
|
|
66
68
|
agentRunId?: string;
|
|
67
69
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"designToProject.d.ts","sourceRoot":"","sources":["../../../src/services/editor/designToProject.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,OAAO,EAIR,MAAM,8BAA8B,CAAC;AAGtC;;;;;;;;;;;;;;;;;GAiBG;AAEH,UAAU,WAAW;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,cAAc;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,UAAU,iBAAkB,SAAQ,cAAc;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,UAAU,gBAAgB;IACxB,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,aAAa,CAAC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,gBAAgB;IACxB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC/C,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IACxD,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAClD,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA+BD,wBAAgB,eAAe,CAC7B,MAAM,EAAE,OAAO,EACf,IAAI,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACzE,OAAO,
|
|
1
|
+
{"version":3,"file":"designToProject.d.ts","sourceRoot":"","sources":["../../../src/services/editor/designToProject.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,OAAO,EAIR,MAAM,8BAA8B,CAAC;AAGtC;;;;;;;;;;;;;;;;;GAiBG;AAEH,UAAU,WAAW;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,cAAc;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,UAAU,iBAAkB,SAAQ,cAAc;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,UAAU,gBAAgB;IACxB,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,aAAa,CAAC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,gBAAgB;IACxB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC/C,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IACxD,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAClD,oFAAoF;IACpF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA+BD,wBAAgB,eAAe,CAC7B,MAAM,EAAE,OAAO,EACf,IAAI,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACzE,OAAO,CAiGT"}
|
|
@@ -110,6 +110,10 @@ function designToProject(design, meta) {
|
|
|
110
110
|
size: { width, height },
|
|
111
111
|
aspect,
|
|
112
112
|
background: editorBackgroundToProject(d.background),
|
|
113
|
+
// Editor stores master volume as 0–100; canonical project uses 0–1.
|
|
114
|
+
masterVolume: typeof d.masterVolume === "number"
|
|
115
|
+
? Math.max(0, Math.min(100, d.masterVolume)) / 100
|
|
116
|
+
: 1,
|
|
113
117
|
tracks,
|
|
114
118
|
clips,
|
|
115
119
|
transitions,
|
|
@@ -210,8 +214,14 @@ function buildClip(id, trackId, type, base, detail, display, size) {
|
|
|
210
214
|
if (!text)
|
|
211
215
|
return null;
|
|
212
216
|
const align = details.align === "left" || details.align === "right" ? details.align : "center";
|
|
217
|
+
// Mirror of storeToProject (frontend): carry the text-box width into
|
|
218
|
+
// metadata.boxWidth so the renderer wraps long text within the canvas
|
|
219
|
+
// instead of overflowing at "max-content" width. Keeps the exported MP4
|
|
220
|
+
// in sync with the editor preview.
|
|
221
|
+
const boxWidth = numOr(details.width, 0);
|
|
213
222
|
return {
|
|
214
223
|
...common,
|
|
224
|
+
...(boxWidth > 0 ? { metadata: { ...cleanMetadata, boxWidth } } : {}),
|
|
215
225
|
type: "text",
|
|
216
226
|
text,
|
|
217
227
|
fontFamily: strOr(details.fontFamily, "Poppins"),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planToProject.d.ts","sourceRoot":"","sources":["../../../src/services/editor/planToProject.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAKV,OAAO,EAIR,MAAM,8BAA8B,CAAC;AAEtC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"planToProject.d.ts","sourceRoot":"","sources":["../../../src/services/editor/planToProject.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAKV,OAAO,EAIR,MAAM,8BAA8B,CAAC;AAEtC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAehE,OAAO,EAAE,QAAQ,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAC9E,YAAY,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEpF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAwZhE"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Provenance, TextClip, Track, Transition } from "../../schemas/project.schema";
|
|
2
|
-
import type { PlannedScene, SceneAssetStrategy, TextOverlay, TransitionKind, VideoPlan } from "../../schemas/videoPlan.schema";
|
|
2
|
+
import type { PlannedScene, SceneAssetStrategy, TextOverlay, TextStylePack, TransitionKind, VideoPlan } from "../../schemas/videoPlan.schema";
|
|
3
3
|
import type { SceneRenderOutcome } from "./planToProject.types";
|
|
4
4
|
/**
|
|
5
5
|
* Pure building blocks for planToProject: track ids, strategy/transition/size
|
|
@@ -35,6 +35,12 @@ export declare function provenanceForScene(scene: PlannedScene, outcome: SceneRe
|
|
|
35
35
|
* of prior scene durations). `sceneDurationMs` clamps any overlay.endMs
|
|
36
36
|
* that overshoots the scene.
|
|
37
37
|
*/
|
|
38
|
-
export declare function buildOverlayClipsForScene(sceneIndex: number, offsetMs: number, sceneDurationMs: number, overlays: readonly TextOverlay[], frameHeight: number
|
|
39
|
-
|
|
38
|
+
export declare function buildOverlayClipsForScene(sceneIndex: number, offsetMs: number, sceneDurationMs: number, overlays: readonly TextOverlay[], frameHeight: number,
|
|
39
|
+
/**
|
|
40
|
+
* Per-video animation look. Resolves the preset for each overlay's role when
|
|
41
|
+
* the overlay didn't specify its own `preset`. Optional so regen callers that
|
|
42
|
+
* don't carry the plan still work (they fall through to "clean").
|
|
43
|
+
*/
|
|
44
|
+
stylePack?: TextStylePack): TextClip[];
|
|
45
|
+
export declare function makeTrack(id: string, kind: Track["kind"], index: number, name: string, sourceTrackId?: string): Track;
|
|
40
46
|
//# sourceMappingURL=planToProject.helpers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planToProject.helpers.d.ts","sourceRoot":"","sources":["../../../src/services/editor/planToProject.helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,QAAQ,EACR,KAAK,EACL,UAAU,EACX,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAClB,WAAW,EACX,cAAc,EACd,SAAS,EACV,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"planToProject.helpers.d.ts","sourceRoot":"","sources":["../../../src/services/editor/planToProject.helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,QAAQ,EACR,KAAK,EACL,UAAU,EACX,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAClB,WAAW,EACX,aAAa,EACb,cAAc,EACd,SAAS,EACV,MAAM,gCAAgC,CAAC;AAExC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE;;;;;;GAMG;AAIH,eAAO,MAAM,QAAQ;;;;;;;CASX,CAAC;AASX,wBAAgB,eAAe,CAAC,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAE9D;AAED,iFAAiF;AACjF,eAAO,MAAM,eAAe,sBAAsB,CAAC;AAInD,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,CAAC,QAAQ,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAW5F;AAID,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,cAAc,EAAE,UAAU,CAAC,MAAM,CAAC,GAAG,IAAI,CASjF,CAAC;AAIF,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,kBAAkB,GAAG,SAAS,GACtC,UAAU,CAiBZ;AA4CD;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,SAAS,WAAW,EAAE,EAChC,WAAW,EAAE,MAAM;AACnB;;;;GAIG;AACH,SAAS,CAAC,EAAE,aAAa,GACxB,QAAQ,EAAE,CA2CZ;AAED,wBAAgB,SAAS,CACvB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,EACnB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,aAAa,CAAC,EAAE,MAAM,GACrB,KAAK,CAYP"}
|
|
@@ -6,6 +6,7 @@ exports.sizeForAspect = sizeForAspect;
|
|
|
6
6
|
exports.provenanceForScene = provenanceForScene;
|
|
7
7
|
exports.buildOverlayClipsForScene = buildOverlayClipsForScene;
|
|
8
8
|
exports.makeTrack = makeTrack;
|
|
9
|
+
const textStylePacks_1 = require("../agent/textStylePacks");
|
|
9
10
|
/**
|
|
10
11
|
* Pure building blocks for planToProject: track ids, strategy/transition/size
|
|
11
12
|
* lookups, provenance + overlay styling, the per-scene overlay clip builder,
|
|
@@ -126,13 +127,21 @@ function fontWeightForOverlay(o) {
|
|
|
126
127
|
* of prior scene durations). `sceneDurationMs` clamps any overlay.endMs
|
|
127
128
|
* that overshoots the scene.
|
|
128
129
|
*/
|
|
129
|
-
function buildOverlayClipsForScene(sceneIndex, offsetMs, sceneDurationMs, overlays, frameHeight
|
|
130
|
+
function buildOverlayClipsForScene(sceneIndex, offsetMs, sceneDurationMs, overlays, frameHeight,
|
|
131
|
+
/**
|
|
132
|
+
* Per-video animation look. Resolves the preset for each overlay's role when
|
|
133
|
+
* the overlay didn't specify its own `preset`. Optional so regen callers that
|
|
134
|
+
* don't carry the plan still work (they fall through to "clean").
|
|
135
|
+
*/
|
|
136
|
+
stylePack) {
|
|
130
137
|
const clips = [];
|
|
131
138
|
for (const [oi, overlay] of overlays.entries()) {
|
|
132
139
|
const overlayFrom = offsetMs + Math.max(0, overlay.startMs ?? 0);
|
|
133
140
|
const overlayTo = offsetMs + Math.min(sceneDurationMs, overlay.endMs ?? sceneDurationMs);
|
|
134
141
|
if (overlayTo <= overlayFrom)
|
|
135
142
|
continue;
|
|
143
|
+
// Per-overlay preset wins; else resolve from the video's style pack + role.
|
|
144
|
+
const preset = overlay.preset ?? (0, textStylePacks_1.resolveTextPreset)(stylePack, overlay.role);
|
|
136
145
|
clips.push({
|
|
137
146
|
id: `scene-${sceneIndex}-overlay-${oi}`,
|
|
138
147
|
trackId: exports.TRACK_ID.text,
|
|
@@ -146,6 +155,8 @@ function buildOverlayClipsForScene(sceneIndex, offsetMs, sceneDurationMs, overla
|
|
|
146
155
|
color: overlay.color ?? "#ffffff",
|
|
147
156
|
align: "center",
|
|
148
157
|
backgroundColor: overlay.bgColor,
|
|
158
|
+
// First-class preset so the editor + round-trip carry it explicitly.
|
|
159
|
+
preset,
|
|
149
160
|
effects: [],
|
|
150
161
|
transform: { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0, opacity: 1 },
|
|
151
162
|
locked: false,
|
|
@@ -155,6 +166,9 @@ function buildOverlayClipsForScene(sceneIndex, offsetMs, sceneDurationMs, overla
|
|
|
155
166
|
position: overlay.position,
|
|
156
167
|
emphasis: overlay.emphasis,
|
|
157
168
|
animation: overlay.animation,
|
|
169
|
+
// Mirror preset into metadata too — readTextMetadata exposes it and
|
|
170
|
+
// some regen/diff paths only diff on metadata.
|
|
171
|
+
preset,
|
|
158
172
|
bgStyle: overlay.bgStyle,
|
|
159
173
|
sizePct: overlay.sizePct,
|
|
160
174
|
persistAcrossScenes: overlay.persistAcrossScenes ?? false,
|
|
@@ -163,7 +177,7 @@ function buildOverlayClipsForScene(sceneIndex, offsetMs, sceneDurationMs, overla
|
|
|
163
177
|
}
|
|
164
178
|
return clips;
|
|
165
179
|
}
|
|
166
|
-
function makeTrack(id, kind, index, name) {
|
|
180
|
+
function makeTrack(id, kind, index, name, sourceTrackId) {
|
|
167
181
|
return {
|
|
168
182
|
id,
|
|
169
183
|
kind,
|
|
@@ -173,5 +187,6 @@ function makeTrack(id, kind, index, name) {
|
|
|
173
187
|
hidden: false,
|
|
174
188
|
locked: false,
|
|
175
189
|
gainDb: 0,
|
|
190
|
+
...(sourceTrackId ? { sourceTrackId } : {}),
|
|
176
191
|
};
|
|
177
192
|
}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.buildOverlayClipsForScene = exports.TRACK_ID = void 0;
|
|
4
4
|
exports.planToProject = planToProject;
|
|
5
5
|
const project_schema_1 = require("../../schemas/project.schema");
|
|
6
|
+
const textStylePacks_1 = require("../agent/textStylePacks");
|
|
6
7
|
const planToProject_helpers_1 = require("./planToProject.helpers");
|
|
7
8
|
// Re-export the public surface so `export * from "./editor/planToProject"`
|
|
8
9
|
// (services/index.ts) keeps surfacing these for existing importers.
|
|
@@ -221,7 +222,7 @@ function planToProject(input) {
|
|
|
221
222
|
clips.push(voClip);
|
|
222
223
|
}
|
|
223
224
|
// Overlay text clips.
|
|
224
|
-
const overlayClips = (0, planToProject_helpers_1.buildOverlayClipsForScene)(scene.sceneIndex, offsetMs, sceneDurationMs, scene.overlays ?? [], size.height);
|
|
225
|
+
const overlayClips = (0, planToProject_helpers_1.buildOverlayClipsForScene)(scene.sceneIndex, offsetMs, sceneDurationMs, scene.overlays ?? [], size.height, plan.textStylePack);
|
|
225
226
|
if (overlayClips.length > 0)
|
|
226
227
|
hasOverlays = true;
|
|
227
228
|
for (const clip of overlayClips)
|
|
@@ -234,6 +235,10 @@ function planToProject(input) {
|
|
|
234
235
|
// change_caption_style tool): per-scene override wins over the
|
|
235
236
|
// plan-level style, which wins over the renderer defaults, per field.
|
|
236
237
|
const cs = { ...plan.captionStyle, ...scene.captionStyle };
|
|
238
|
+
// Caption preset: explicit captionStyle.preset wins, else resolve from
|
|
239
|
+
// the video's style pack. Carried first-class so the editor + round-trip
|
|
240
|
+
// preserve it.
|
|
241
|
+
const captionPreset = cs?.preset ?? (0, textStylePacks_1.resolveCaptionPreset)(plan.textStylePack);
|
|
237
242
|
const captionClip = {
|
|
238
243
|
id: `scene-${scene.sceneIndex}-caption`,
|
|
239
244
|
trackId: planToProject_helpers_1.TRACK_ID.caption,
|
|
@@ -249,11 +254,19 @@ function planToProject(input) {
|
|
|
249
254
|
...(cs?.fontWeight !== undefined ? { fontWeight: cs.fontWeight } : {}),
|
|
250
255
|
color: cs?.color ?? "#ffffff",
|
|
251
256
|
...(cs?.highlightColor ? { highlightColor: cs.highlightColor } : {}),
|
|
257
|
+
...(captionPreset ? { preset: captionPreset } : {}),
|
|
252
258
|
language: "en",
|
|
253
259
|
effects: [],
|
|
254
260
|
transform: { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0, opacity: 1 },
|
|
255
261
|
locked: false,
|
|
256
|
-
|
|
262
|
+
// parentId / sourceUrl link the caption to its source VO clip — the same
|
|
263
|
+
// convention editor-made captions use, so the editor groups, regenerates
|
|
264
|
+
// (replace-in-place by parentId), and links them identically (1:1).
|
|
265
|
+
metadata: {
|
|
266
|
+
sceneIndex: scene.sceneIndex,
|
|
267
|
+
parentId: `scene-${scene.sceneIndex}-vo`,
|
|
268
|
+
...(outcome?.voiceoverUrl ? { sourceUrl: outcome.voiceoverUrl } : {}),
|
|
269
|
+
},
|
|
257
270
|
};
|
|
258
271
|
clips.push(captionClip);
|
|
259
272
|
}
|
|
@@ -382,8 +395,11 @@ function planToProject(input) {
|
|
|
382
395
|
tracks.push((0, planToProject_helpers_1.makeTrack)(planToProject_helpers_1.TRACK_ID.music, "audio", trackIndex++, "Music"));
|
|
383
396
|
if (hasOverlays)
|
|
384
397
|
tracks.push((0, planToProject_helpers_1.makeTrack)(planToProject_helpers_1.TRACK_ID.text, "text", trackIndex++, "Text"));
|
|
398
|
+
// Caption track is tagged with its SOURCE GROUP (the voiceover track) so the
|
|
399
|
+
// editor treats agent-made captions exactly like editor-made ones: one caption
|
|
400
|
+
// row per source group, and regen/new captions route back onto it.
|
|
385
401
|
if (hasCaptions)
|
|
386
|
-
tracks.push((0, planToProject_helpers_1.makeTrack)(planToProject_helpers_1.TRACK_ID.caption, "caption", trackIndex++, "Captions"));
|
|
402
|
+
tracks.push((0, planToProject_helpers_1.makeTrack)(planToProject_helpers_1.TRACK_ID.caption, "caption", trackIndex++, "Voiceover · Captions", planToProject_helpers_1.TRACK_ID.voiceover));
|
|
387
403
|
const durationMs = Math.max(1, offsetMs);
|
|
388
404
|
return {
|
|
389
405
|
schemaVersion: project_schema_1.PROJECT_SCHEMA_VERSION,
|
|
@@ -393,6 +409,8 @@ function planToProject(input) {
|
|
|
393
409
|
fps: 30,
|
|
394
410
|
size,
|
|
395
411
|
aspect: plan.aspect,
|
|
412
|
+
// Agent plans have no master-volume concept yet; 1 = unchanged.
|
|
413
|
+
masterVolume: 1,
|
|
396
414
|
background: { type: "color", value: "#000000" },
|
|
397
415
|
tracks,
|
|
398
416
|
clips,
|