@remixmate/cli 0.9.72 → 0.9.74
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/LICENSE +45 -21
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/argv.js +1 -51
- package/dist/auth/auth-lock.js +1 -100
- package/dist/auth/auto-login.js +7 -75
- package/dist/auth/commands.js +40 -290
- package/dist/auth/credential-store.js +1 -143
- package/dist/auth/device-flow-runner.js +13 -110
- package/dist/auth/device-flow.js +1 -132
- package/dist/auth/ensure.js +1 -62
- package/dist/auth/environment.js +1 -69
- package/dist/auth/pending-store.js +1 -45
- package/dist/auth/resolve.js +1 -44
- package/dist/billing.js +3 -76
- package/dist/capabilities.js +1 -75
- package/dist/cli.js +39 -211
- package/dist/diagnostics.js +2 -70
- package/dist/doctor.js +9 -188
- package/dist/errors.js +1 -33
- package/dist/exec.js +4 -47
- package/dist/handlers/gen-digital-human.js +33 -264
- package/dist/handlers/gen-image.js +9 -113
- package/dist/handlers/gen-post.js +15 -192
- package/dist/handlers/gen-video.js +7 -185
- package/dist/handlers/gen-voice.js +19 -133
- package/dist/handlers/index.js +1 -20
- package/dist/handlers/shared.js +1 -66
- package/dist/handlers/template-init.js +1 -21
- package/dist/http.js +3 -155
- package/dist/index.js +1 -8
- package/dist/install.js +40 -448
- package/dist/manifest.json +44 -3
- package/dist/progress.js +2 -15
- package/dist/project/commands.js +34 -156
- package/dist/project/host.js +1 -151
- package/dist/project/resolve.js +1 -65
- package/dist/project/store.js +2 -97
- package/dist/project/take.js +1 -106
- package/dist/project/trace.js +5 -278
- package/dist/registry.js +3 -70
- package/dist/runner.js +7 -225
- package/dist/skill-schema.js +1 -194
- package/dist/template/args.js +1 -69
- package/dist/template/check.js +2 -30
- package/dist/template/commands.js +34 -163
- package/dist/template/constants.js +1 -46
- package/dist/template/init.js +5 -133
- package/dist/template/list.js +1 -46
- package/dist/template/preview.js +7 -318
- package/dist/template/proc.js +1 -43
- package/dist/template/project.js +6 -161
- package/dist/template/publish.js +5 -185
- package/dist/template/render-client.js +2 -79
- package/dist/template/upgrade.js +4 -123
- package/dist/text.js +1 -15
- package/package.json +2 -35
- package/skills/doc-parse/scripts/extract_via_render.py +32 -32
- package/skills/export-jianying/scripts/gen_jianying_draft.py +188 -188
- package/skills/gen-script/SKILL.md +23 -1
- package/skills/gen-script/scripts/gen_script.py +644 -551
- package/skills/gen-script/skill.json +12 -0
- package/skills/gen-script/version.json +1 -1
- package/skills/prepare-video-assets/scripts/prepare_video_assets.py +21 -21
- package/skills/render-video/scripts/_chrome_vendor.py +17 -17
- package/skills/render-video/scripts/_digital_human_segments.py +34 -34
- package/skills/render-video/scripts/_video_probe.py +33 -33
- package/skills/render-video/scripts/_vod_polling.py +13 -13
- package/skills/render-video/scripts/remote_renderer_client.py +42 -42
- package/skills/render-video/scripts/render_video.py +639 -639
- package/skills/render-video/scripts/required_props.py +37 -37
- package/skills/render-video/scripts/upload_video.py +35 -35
- package/skills/render-video/version.json +1 -1
- package/skills/template-registry/SKILL.md +16 -0
- package/skills/template-registry/scripts/countdown_spec.py +92 -92
- package/skills/template-registry/scripts/doc_extract.py +40 -40
- package/skills/template-registry/scripts/list_templates.py +303 -191
- package/skills/template-registry/scripts/log_diagnostics.py +1 -1
- package/skills/template-registry/scripts/match_template.py +87 -87
- package/skills/template-registry/scripts/payload_hydrator.py +89 -89
- package/skills/template-registry/scripts/registry_loader.py +159 -159
- package/skills/template-registry/scripts/render_job_client.py +75 -75
- package/skills/template-registry/scripts/showcase_loader.py +44 -44
- package/skills/template-registry/skill.json +4 -1
- package/skills/template-registry/version.json +1 -1
- package/skills/template-registry/video_dsl/runtime/__init__.py +8 -8
- package/skills/template-registry/video_dsl/runtime/dsl_validator.py +329 -329
- package/skills/template-registry/video_dsl/runtime/narration_budget.py +36 -36
- package/skills/template-registry/video_dsl/runtime/payload_contract.py +7 -7
- package/skills/template-registry/video_dsl/runtime/prompt_enhancer.py +98 -98
- package/skills/template-registry/video_dsl/runtime/timeline_compiler.py +127 -127
- package/skills/template-registry/video_dsl/schema/video-dsl-v1alpha1.json +14 -1
- package/skills/video-parser/scripts/analyze_video.py +173 -173
- package/skills/video-parser/scripts/deconstruct_video.py +91 -91
- package/skills/video-parser/scripts/parse_via_render.py +21 -21
- package/skills/web-screenshot/scripts/_media_screenshot/__init__.py +4 -4
- package/skills/web-screenshot/scripts/_media_screenshot/bootstrap.py +7 -7
- package/skills/web-screenshot/scripts/_media_screenshot/browser.py +4 -4
- package/skills/web-screenshot/scripts/_media_screenshot/cli_args.py +4 -4
- package/skills/web-screenshot/scripts/_media_screenshot/js_loader.py +2 -2
- package/skills/web-screenshot/scripts/_media_screenshot/overlay.py +1 -1
- package/skills/web-screenshot/scripts/_media_screenshot/reader.py +40 -40
- package/skills/web-screenshot/scripts/_media_screenshot/recording.py +11 -11
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/__init__.py +8 -8
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/arrow.py +2 -2
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/caption.py +1 -1
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/click.py +1 -1
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/code_lines.py +5 -5
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/focus.py +1 -1
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/highlight.py +3 -3
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/hover.py +1 -1
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/redact.py +1 -1
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/scroll.py +1 -1
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/sequence.py +1 -1
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/title_card.py +1 -1
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/type.py +1 -1
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/wait.py +1 -1
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/zoom.py +2 -2
- package/skills/web-screenshot/scripts/_media_screenshot/screenshot.py +15 -15
- package/skills/web-screenshot/scripts/_media_screenshot/storyboard.py +2 -2
- package/skills/web-screenshot/scripts/_media_screenshot/template.py +11 -11
- package/skills/web-screenshot/scripts/_media_screenshot/trim.py +10 -10
- package/skills/web-screenshot/scripts/_media_screenshot/urlguard.py +22 -22
- package/skills/web-screenshot/scripts/_media_upload.py +33 -33
- package/skills/web-screenshot/scripts/read_page.py +15 -15
- package/skills/web-screenshot/scripts/record.py +26 -26
- package/skills/web-screenshot/scripts/screenshot.py +5 -5
- package/dist/argv.d.ts +0 -15
- package/dist/auth/auth-lock.d.ts +0 -26
- package/dist/auth/auto-login.d.ts +0 -20
- package/dist/auth/commands.d.ts +0 -8
- package/dist/auth/credential-store.d.ts +0 -60
- package/dist/auth/device-flow-runner.d.ts +0 -58
- package/dist/auth/device-flow.d.ts +0 -52
- package/dist/auth/ensure.d.ts +0 -55
- package/dist/auth/environment.d.ts +0 -38
- package/dist/auth/pending-store.d.ts +0 -33
- package/dist/auth/resolve.d.ts +0 -30
- package/dist/billing.d.ts +0 -44
- package/dist/capabilities.d.ts +0 -63
- package/dist/cli.d.ts +0 -13
- package/dist/diagnostics.d.ts +0 -24
- package/dist/doctor.d.ts +0 -22
- package/dist/errors.d.ts +0 -29
- package/dist/exec.d.ts +0 -18
- package/dist/handlers/gen-digital-human.d.ts +0 -17
- package/dist/handlers/gen-image.d.ts +0 -20
- package/dist/handlers/gen-post.d.ts +0 -22
- package/dist/handlers/gen-video.d.ts +0 -19
- package/dist/handlers/gen-voice.d.ts +0 -26
- package/dist/handlers/index.d.ts +0 -20
- package/dist/handlers/shared.d.ts +0 -26
- package/dist/handlers/template-init.d.ts +0 -10
- package/dist/http.d.ts +0 -72
- package/dist/index.d.ts +0 -9
- package/dist/install.d.ts +0 -71
- package/dist/progress.d.ts +0 -23
- package/dist/project/commands.d.ts +0 -19
- package/dist/project/host.d.ts +0 -91
- package/dist/project/resolve.d.ts +0 -43
- package/dist/project/store.d.ts +0 -31
- package/dist/project/take.d.ts +0 -50
- package/dist/project/trace.d.ts +0 -98
- package/dist/registry.d.ts +0 -30
- package/dist/runner.d.ts +0 -39
- package/dist/skill-schema.d.ts +0 -138
- package/dist/template/args.d.ts +0 -25
- package/dist/template/check.d.ts +0 -23
- package/dist/template/commands.d.ts +0 -25
- package/dist/template/constants.d.ts +0 -39
- package/dist/template/init.d.ts +0 -28
- package/dist/template/list.d.ts +0 -23
- package/dist/template/preview.d.ts +0 -60
- package/dist/template/proc.d.ts +0 -26
- package/dist/template/project.d.ts +0 -47
- package/dist/template/publish.d.ts +0 -43
- package/dist/template/render-client.d.ts +0 -39
- package/dist/template/upgrade.d.ts +0 -44
- package/dist/text.d.ts +0 -8
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
"""
|
|
2
|
-
时间线编译器 - 将 DSL + TemplateBinding 编译为 Remotion 可消费的 RenderPlan。
|
|
3
1
|
|
|
4
|
-
负责: 帧号计算、字幕切片、转场注入、层级分配。
|
|
5
2
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
15
|
|
|
16
16
|
import re
|
|
17
17
|
from datetime import datetime, timezone
|
|
@@ -19,51 +19,51 @@ from typing import Optional
|
|
|
19
19
|
|
|
20
20
|
CHARS_PER_SECOND = 5.0
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
25
|
_EN_SENTENCE_END = (
|
|
26
26
|
r"(?:(?<=[.!?])|(?<=[.!?][\"'\u201d\u2019)]))"
|
|
27
27
|
r"(?<!\bMr\.)(?<!\bMs\.)(?<!\bDr\.)(?<!\bSt\.)(?<!\bMrs\.)(?<!\be\.g\.)(?<!\bi\.e\.)(?<!\bvs\.)"
|
|
28
28
|
r"(?=\s)"
|
|
29
29
|
)
|
|
30
30
|
_PRIMARY_BREAK = re.compile(r"(?<=[。!?;\n])|" + _EN_SENTENCE_END)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
35
|
_SECONDARY_BREAK = re.compile(r"(?<=[,,::])")
|
|
36
36
|
_PRIMARY_TAIL = re.compile(
|
|
37
37
|
r"(?:[。!?;\n]|(?<!\bMr)(?<!\bMs)(?<!\bDr)(?<!\bSt)(?<!\bMrs)(?<!\bvs)[.!?][\"'\u201d\u2019)]?)\s*$"
|
|
38
38
|
)
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
60
|
MAX_SEGMENT_CHARS = 30
|
|
61
|
-
|
|
61
|
+
|
|
62
62
|
MIN_SEGMENT_CHARS = 6
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
_TRAILING_PUNCT = re.compile(r"[。!?;,,、::..…]+$")
|
|
66
|
-
|
|
66
|
+
|
|
67
67
|
_EMPHASIS_MARKS = re.compile(r"\*\*")
|
|
68
68
|
|
|
69
69
|
RESOLUTION_MAP = {
|
|
@@ -90,54 +90,54 @@ def _is_latin_word_char(ch: str) -> bool:
|
|
|
90
90
|
|
|
91
91
|
|
|
92
92
|
def _breakable(prev_token: str, next_token: str) -> bool:
|
|
93
|
-
"""两个 token 之间断开是否安全 —— 拉丁↔拉丁即同一词组内部,不安全。
|
|
94
93
|
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
97
|
if not prev_token or not next_token:
|
|
98
98
|
return True
|
|
99
99
|
return not (_is_latin_word_char(prev_token[-1]) and _is_latin_word_char(next_token[0]))
|
|
100
100
|
|
|
101
101
|
|
|
102
102
|
def _concat_segments(left: str, right: str) -> str:
|
|
103
|
-
"""拼接两个字幕段,必要时补回被切分吃掉的那个空格。
|
|
104
103
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
109
109
|
if _breakable(left, right):
|
|
110
110
|
return left + right
|
|
111
111
|
return left + " " + right
|
|
112
112
|
|
|
113
113
|
|
|
114
114
|
def _split_at_spaces(text: str, max_len: int) -> list[str]:
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
|
|
133
133
|
if len(text) <= max_len:
|
|
134
134
|
return [text]
|
|
135
135
|
|
|
136
136
|
parts: list[str] = []
|
|
137
|
-
buf: list[str] = []
|
|
137
|
+
buf: list[str] = []
|
|
138
138
|
for word in text.split(" "):
|
|
139
139
|
if buf and len(" ".join(buf + [word])) > max_len:
|
|
140
|
-
|
|
140
|
+
|
|
141
141
|
cut = len(buf)
|
|
142
142
|
for i in range(len(buf), 0, -1):
|
|
143
143
|
following = word if i == len(buf) else buf[i]
|
|
@@ -153,28 +153,28 @@ def _split_at_spaces(text: str, max_len: int) -> list[str]:
|
|
|
153
153
|
|
|
154
154
|
|
|
155
155
|
def _merge_short(segments: list[str], min_len: int) -> list[str]:
|
|
156
|
-
"""Merge fragments shorter than *min_len* into a neighbour.
|
|
157
156
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
|
|
163
163
|
if len(segments) <= 1:
|
|
164
164
|
return segments
|
|
165
165
|
merged: list[str] = [segments[0]]
|
|
166
166
|
for seg in segments[1:]:
|
|
167
167
|
prev_is_sentence_end = bool(_PRIMARY_TAIL.search(merged[-1]))
|
|
168
168
|
if len(merged[-1]) < min_len:
|
|
169
|
-
|
|
169
|
+
|
|
170
170
|
merged[-1] = _concat_segments(merged[-1], seg)
|
|
171
171
|
elif len(seg) < min_len and not prev_is_sentence_end:
|
|
172
|
-
|
|
172
|
+
|
|
173
173
|
merged[-1] = _concat_segments(merged[-1], seg)
|
|
174
174
|
else:
|
|
175
|
-
|
|
175
|
+
|
|
176
176
|
merged.append(seg)
|
|
177
|
-
|
|
177
|
+
|
|
178
178
|
if len(merged) > 1 and len(merged[-1]) < min_len:
|
|
179
179
|
if not _PRIMARY_TAIL.search(merged[-2]):
|
|
180
180
|
merged[-2] = _concat_segments(merged[-2], merged.pop())
|
|
@@ -182,28 +182,28 @@ def _merge_short(segments: list[str], min_len: int) -> list[str]:
|
|
|
182
182
|
|
|
183
183
|
|
|
184
184
|
def assign_sentence_indices(raw_texts: list[str]) -> list[int]:
|
|
185
|
-
"""给每个字幕/TTS 段标注它属于原旁白的第几**句**(0-based)。
|
|
186
185
|
|
|
187
|
-
为什么必须单独标:一段 **不等于** 一句。``segment_narration`` 在句末断行之后,
|
|
188
|
-
还会把超过 ``MAX_SEGMENT_CHARS`` 的长句在逗号处继续切 —— 那是为了让 TTS 按真实
|
|
189
|
-
停顿返回时间戳,跟"句"没关系。于是 35 字的一句话会变成 3 段。
|
|
190
186
|
|
|
191
|
-
靠下标把「第 i 个动作」对齐到「第 i 段字幕」的模板(math-principles-expl 的
|
|
192
|
-
``steps``、ppt-to-video 的 ``beats``)因此会整体错位,而且是**静默**的:
|
|
193
|
-
2026-09-23 线上一条鸡兔同笼成片里 10 句旁白被切成约 19 段,板面 10 步在音频
|
|
194
|
-
播到一半就走完,之后画面冻结、旁白继续,全程零报错、终态 completed。
|
|
195
187
|
|
|
196
|
-
判据:一段是某句的**末段**,当且仅当它以句末标点(``。!?;`` 或换行)结尾。
|
|
197
|
-
这与 ``_PRIMARY_BREAK`` 是同一个集合 —— 切的时候用它断,标的时候用它认,
|
|
198
|
-
所以两边不会漂移。
|
|
199
188
|
|
|
200
|
-
⚠️ 必须传**未去尾标点**的原文。``_strip_trailing_punct`` 之后句末标点就没了,
|
|
201
|
-
那时所有段都会被认成同一句。
|
|
202
189
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
|
|
207
207
|
indices: list[int] = []
|
|
208
208
|
sentence = 0
|
|
209
209
|
for text in raw_texts:
|
|
@@ -214,10 +214,10 @@ def assign_sentence_indices(raw_texts: list[str]) -> list[int]:
|
|
|
214
214
|
|
|
215
215
|
|
|
216
216
|
def count_narration_sentences(text: str) -> int:
|
|
217
|
-
"""数一段旁白有多少句。与 ``assign_sentence_indices`` 同一判据。
|
|
218
217
|
|
|
219
|
-
|
|
220
|
-
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
|
|
221
221
|
if not text or not text.strip():
|
|
222
222
|
return 0
|
|
223
223
|
chunks = [s for s in _PRIMARY_BREAK.split(text.strip()) if s.strip()]
|
|
@@ -225,26 +225,26 @@ def count_narration_sentences(text: str) -> int:
|
|
|
225
225
|
|
|
226
226
|
|
|
227
227
|
def _plain_len(text: str) -> int:
|
|
228
|
-
|
|
228
|
+
|
|
229
229
|
return len(_EMPHASIS_MARKS.sub("", text))
|
|
230
230
|
|
|
231
231
|
|
|
232
232
|
def _strip_trailing_punct(text: str) -> str:
|
|
233
|
-
|
|
233
|
+
|
|
234
234
|
return _TRAILING_PUNCT.sub("", text)
|
|
235
235
|
|
|
236
236
|
|
|
237
237
|
def segment_narration(text: str) -> list[str]:
|
|
238
|
-
"""Split narration into **TTS lines**, cutting at speech pauses.
|
|
239
238
|
|
|
240
|
-
Use before TTS: ``"\\n".join(segment_narration(text))`` sends to Minimax
|
|
241
|
-
so it returns per-line timestamps.
|
|
242
239
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
|
|
248
248
|
if not text:
|
|
249
249
|
return []
|
|
250
250
|
raw = _PRIMARY_BREAK.split(text.strip())
|
|
@@ -267,14 +267,14 @@ def segment_narration(text: str) -> list[str]:
|
|
|
267
267
|
def split_subtitle_from_lines(
|
|
268
268
|
lines: list[str], duration_frames: int, fps: int, padding_frames: int = 10
|
|
269
269
|
) -> list[dict]:
|
|
270
|
-
"""Fallback subtitle timing for pre-split narration lines.
|
|
271
270
|
|
|
272
|
-
Preserves exactly ``len(lines)`` segments (no secondary comma-split,
|
|
273
|
-
no short-fragment merging). Used when narration was authored as
|
|
274
|
-
``{intro, items, outro}`` and TTS timestamps are unavailable.
|
|
275
271
|
|
|
276
|
-
|
|
277
|
-
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
|
|
278
278
|
segments = [s.strip() for s in lines if s and s.strip()]
|
|
279
279
|
if not segments:
|
|
280
280
|
return []
|
|
@@ -285,7 +285,7 @@ def split_subtitle_from_lines(
|
|
|
285
285
|
max(duration_frames - 2 * padding_frames, fps),
|
|
286
286
|
max(estimated_audio_frames, fps),
|
|
287
287
|
)
|
|
288
|
-
|
|
288
|
+
|
|
289
289
|
sentence_indices = assign_sentence_indices(segments)
|
|
290
290
|
result: list[dict] = []
|
|
291
291
|
current_frame = padding_frames
|
|
@@ -303,11 +303,11 @@ def split_subtitle_from_lines(
|
|
|
303
303
|
|
|
304
304
|
|
|
305
305
|
def split_subtitle(text: str, duration_frames: int, fps: int, padding_frames: int = 10) -> list[dict]:
|
|
306
|
-
"""Split narration text into subtitle segments aligned to frames.
|
|
307
306
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
|
|
311
311
|
segments = segment_narration(text)
|
|
312
312
|
if not segments:
|
|
313
313
|
return []
|
|
@@ -318,7 +318,7 @@ def split_subtitle(text: str, duration_frames: int, fps: int, padding_frames: in
|
|
|
318
318
|
max(duration_frames - 2 * padding_frames, fps),
|
|
319
319
|
max(estimated_audio_frames, fps),
|
|
320
320
|
)
|
|
321
|
-
|
|
321
|
+
|
|
322
322
|
sentence_indices = assign_sentence_indices(segments)
|
|
323
323
|
result: list[dict] = []
|
|
324
324
|
current_frame = padding_frames
|
|
@@ -337,7 +337,7 @@ def split_subtitle(text: str, duration_frames: int, fps: int, padding_frames: in
|
|
|
337
337
|
|
|
338
338
|
|
|
339
339
|
def _resolve_root_composition(binding: dict, ratio: str) -> str:
|
|
340
|
-
|
|
340
|
+
|
|
341
341
|
entry_map = binding.get("remotionEntry", {})
|
|
342
342
|
if entry_map:
|
|
343
343
|
return entry_map.get(ratio, next(iter(entry_map.values()), "MainVideo"))
|
|
@@ -345,7 +345,7 @@ def _resolve_root_composition(binding: dict, ratio: str) -> str:
|
|
|
345
345
|
|
|
346
346
|
|
|
347
347
|
def compile_timeline(dsl: dict, binding: dict) -> dict:
|
|
348
|
-
|
|
348
|
+
|
|
349
349
|
fps = dsl.get("global", {}).get("fps", 30)
|
|
350
350
|
ratio = dsl.get("global", {}).get("aspectRatio", "16:9")
|
|
351
351
|
resolution = dsl.get("global", {}).get("resolution", "1080p")
|
|
@@ -389,9 +389,9 @@ def compile_timeline(dsl: dict, binding: dict) -> dict:
|
|
|
389
389
|
|
|
390
390
|
scene_bind = binding_map.get(scene_id, {})
|
|
391
391
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
|
|
395
395
|
|
|
396
396
|
narration = scene.get("audio", {}).get("narration", {})
|
|
397
397
|
|
|
@@ -54,7 +54,20 @@
|
|
|
54
54
|
"description": "整体风格标签,如 科技感、清新、商务、卡通"
|
|
55
55
|
},
|
|
56
56
|
"audience": { "type": "string" },
|
|
57
|
-
"description": { "type": "string" }
|
|
57
|
+
"description": { "type": "string" },
|
|
58
|
+
"templateSelection": {
|
|
59
|
+
"type": "object",
|
|
60
|
+
"description": "模板是怎么定下来的(gen_script --template-source / --template-candidates 写入)。user-explicit = 用户点名;user-confirmed = agent 推荐、用户确认。未经确认的 auto 选择不会出现在这里:gen_script 直接拒绝生成。",
|
|
61
|
+
"required": ["source"],
|
|
62
|
+
"properties": {
|
|
63
|
+
"source": { "type": "string", "enum": ["user-explicit", "user-confirmed"] },
|
|
64
|
+
"candidates": {
|
|
65
|
+
"type": "array",
|
|
66
|
+
"items": { "type": "string" },
|
|
67
|
+
"description": "给用户看过的候选 templateId,推荐的排第一"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
58
71
|
}
|
|
59
72
|
},
|
|
60
73
|
"GlobalConfig": {
|