@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,82 +1,82 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
80
|
|
|
81
81
|
from __future__ import annotations
|
|
82
82
|
|
|
@@ -86,9 +86,9 @@ import os
|
|
|
86
86
|
import sys
|
|
87
87
|
from pathlib import Path
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
from registry_loader import (
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
from registry_loader import (
|
|
92
92
|
EXIT_BACKEND_UNREACHABLE,
|
|
93
93
|
EXIT_NOT_AUTHENTICATED,
|
|
94
94
|
RegistryAuthError,
|
|
@@ -97,40 +97,109 @@ from registry_loader import ( # noqa: E402
|
|
|
97
97
|
list_templates as load_visible_templates,
|
|
98
98
|
registry_page_urls,
|
|
99
99
|
)
|
|
100
|
-
from showcase_loader import load_showcases, summarize_group
|
|
100
|
+
from showcase_loader import load_showcases, summarize_group
|
|
101
|
+
|
|
102
|
+
|
|
101
103
|
|
|
102
|
-
# Samples per template in list views (table count + JSON items). The detail
|
|
103
|
-
# view (--template-id) returns up to _DETAIL_SHOWCASES.
|
|
104
104
|
_LIST_SHOWCASES = 3
|
|
105
105
|
_DETAIL_SHOWCASES = 20
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
|
|
108
|
+
|
|
109
109
|
_SUMMARY_HINT_CHARS = 200
|
|
110
110
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
114
|
_MAX_FULL_DUMP_CHARS = 60_000
|
|
115
115
|
|
|
116
116
|
|
|
117
|
-
|
|
117
|
+
_PAYLOAD_STYLES = ("visual-overlay", "slide", "carousel-caption")
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _caps(tpl: dict) -> dict:
|
|
121
|
+
caps = tpl.get("capabilities")
|
|
122
|
+
return caps if isinstance(caps, dict) else {}
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _effective_payload_style(tpl: dict) -> str:
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
declared = _caps(tpl).get("payloadStyle")
|
|
129
|
+
if declared in _PAYLOAD_STYLES:
|
|
130
|
+
return declared
|
|
131
|
+
reqs = tpl.get("assetRequirements") or []
|
|
132
|
+
return "visual-overlay" if ("image" in reqs or "video" in reqs) else "slide"
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _source_doc_kind(tpl: dict) -> str | None:
|
|
136
|
+
hydration = _caps(tpl).get("payloadHydration")
|
|
137
|
+
if isinstance(hydration, dict):
|
|
138
|
+
kind = hydration.get("docKind")
|
|
139
|
+
return str(kind).lower() if kind else "any"
|
|
140
|
+
return None
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _exclusion_reasons(
|
|
144
|
+
tpl: dict,
|
|
145
|
+
*,
|
|
146
|
+
tag: str | None = None,
|
|
147
|
+
aspect: str | None = None,
|
|
148
|
+
language: str | None = None,
|
|
149
|
+
user_media: str | None = None,
|
|
150
|
+
source_doc: str | None = None,
|
|
151
|
+
narration: str | None = None,
|
|
152
|
+
) -> list[str]:
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
reasons: list[str] = []
|
|
118
160
|
if tag:
|
|
119
161
|
needle = tag.lower()
|
|
120
162
|
tags = [str(t).lower() for t in (tpl.get("styleTags") or [])]
|
|
121
163
|
if not any(needle in t or t in needle for t in tags):
|
|
122
|
-
|
|
164
|
+
reasons.append(f"no styleTag matches {tag!r}")
|
|
123
165
|
if aspect:
|
|
124
166
|
want = aspect.lower()
|
|
125
167
|
ratios = [str(r).lower() for r in (tpl.get("supportedAspectRatios") or [])]
|
|
126
168
|
if want not in ratios:
|
|
127
|
-
|
|
169
|
+
reasons.append(f"aspect ratio {aspect} not supported ({', '.join(ratios) or 'none declared'})")
|
|
128
170
|
if language:
|
|
129
|
-
|
|
171
|
+
|
|
130
172
|
langs = [str(l).lower() for l in (tpl.get("contentLanguage") or [])]
|
|
131
173
|
if langs and language.lower() not in langs:
|
|
132
|
-
|
|
133
|
-
|
|
174
|
+
reasons.append(f"content language {language} not supported ({', '.join(langs)})")
|
|
175
|
+
|
|
176
|
+
style = _effective_payload_style(tpl)
|
|
177
|
+
if user_media == "yes" and style == "slide":
|
|
178
|
+
reasons.append("no place for the user's own images/videos (payloadStyle=slide)")
|
|
179
|
+
if user_media == "no" and style == "carousel-caption":
|
|
180
|
+
reasons.append("needs the user's own images/videos (payloadStyle=carousel-caption)")
|
|
181
|
+
|
|
182
|
+
doc_kind = _source_doc_kind(tpl)
|
|
183
|
+
if source_doc:
|
|
184
|
+
want_doc = source_doc.lower()
|
|
185
|
+
if want_doc == "none":
|
|
186
|
+
if doc_kind is not None:
|
|
187
|
+
reasons.append("builds every scene from a source document the user has not given (capabilities.payloadHydration)")
|
|
188
|
+
elif doc_kind is None:
|
|
189
|
+
reasons.append(f"does not build scenes from a {want_doc} document")
|
|
190
|
+
elif doc_kind not in ("any", want_doc):
|
|
191
|
+
reasons.append(f"builds scenes from a {doc_kind} document, not {want_doc}")
|
|
192
|
+
|
|
193
|
+
needs_narration = _caps(tpl).get("needsNarration", True) is not False
|
|
194
|
+
if narration == "yes" and not needs_narration:
|
|
195
|
+
reasons.append("has no narration (capabilities.needsNarration=false)")
|
|
196
|
+
if narration == "no" and needs_narration:
|
|
197
|
+
reasons.append("is narration-driven (needsNarration is not false)")
|
|
198
|
+
return reasons
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _matches(tpl: dict, tag: str | None, aspect: str | None, language: str | None) -> bool:
|
|
202
|
+
return not _exclusion_reasons(tpl, tag=tag, aspect=aspect, language=language)
|
|
134
203
|
|
|
135
204
|
|
|
136
205
|
def _status_of(tpl: dict) -> str:
|
|
@@ -148,23 +217,25 @@ def _truncate(text: object, limit: int) -> str | None:
|
|
|
148
217
|
|
|
149
218
|
|
|
150
219
|
def _summarize(tpl: dict) -> dict:
|
|
151
|
-
"""The "choose a template" view: identity + selection criteria only.
|
|
152
220
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
|
|
157
226
|
variants = tpl.get("variants")
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
caps =
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
caps = _caps(tpl)
|
|
163
232
|
caps_summary = {
|
|
164
233
|
k: caps[k]
|
|
165
|
-
for k in ("payloadStyle", "needsNarration", "durationStrategy", "narrationDriver")
|
|
234
|
+
for k in ("payloadStyle", "needsNarration", "durationStrategy", "narrationDriver", "sceneStrategy")
|
|
166
235
|
if k in caps
|
|
167
236
|
}
|
|
237
|
+
if (doc_kind := _source_doc_kind(tpl)) is not None:
|
|
238
|
+
caps_summary["sourceDocKind"] = doc_kind
|
|
168
239
|
summary = {
|
|
169
240
|
"templateId": tpl.get("templateId"),
|
|
170
241
|
"name": tpl.get("name"),
|
|
@@ -177,6 +248,10 @@ def _summarize(tpl: dict) -> dict:
|
|
|
177
248
|
"capabilities": caps_summary,
|
|
178
249
|
"llmHintPreview": _truncate(tpl.get("llmHint"), _SUMMARY_HINT_CHARS),
|
|
179
250
|
}
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
if isinstance(tpl.get("notFor"), str) and tpl["notFor"].strip():
|
|
254
|
+
summary["notFor"] = tpl["notFor"].strip()
|
|
180
255
|
hint = tpl.get("llmHint")
|
|
181
256
|
if isinstance(hint, str) and len(hint.strip()) > _SUMMARY_HINT_CHARS:
|
|
182
257
|
summary["llmHintTruncated"] = True
|
|
@@ -184,30 +259,30 @@ def _summarize(tpl: dict) -> dict:
|
|
|
184
259
|
|
|
185
260
|
|
|
186
261
|
def _open_in_browser(url: str) -> None:
|
|
187
|
-
"""Best-effort browser open after the URL has already been printed.
|
|
188
262
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
|
|
193
268
|
if not sys.stdout.isatty() or os.environ.get("REMIXMATE_NO_BROWSER", "").strip():
|
|
194
269
|
return
|
|
195
270
|
try:
|
|
196
271
|
import webbrowser
|
|
197
272
|
|
|
198
273
|
webbrowser.open(url, new=2)
|
|
199
|
-
except Exception:
|
|
274
|
+
except Exception:
|
|
200
275
|
pass
|
|
201
276
|
|
|
202
277
|
|
|
203
278
|
def _page_url(template_id: object, page_urls: dict[str, str], groups: dict | None) -> str | None:
|
|
204
|
-
"""Web detail page link for one template.
|
|
205
279
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
|
|
211
286
|
if not isinstance(template_id, str):
|
|
212
287
|
return None
|
|
213
288
|
if page_urls.get(template_id):
|
|
@@ -219,9 +294,9 @@ def _page_url(template_id: object, page_urls: dict[str, str], groups: dict | Non
|
|
|
219
294
|
|
|
220
295
|
def _report_unknown_ids(missing: list[str], templates: list[dict]) -> None:
|
|
221
296
|
for tid in missing:
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
|
|
225
300
|
gated = get_template(tid, include_all_statuses=True)
|
|
226
301
|
if gated is not None:
|
|
227
302
|
print(
|
|
@@ -243,7 +318,7 @@ def _detail_hint(template_id: str = "<templateId>") -> str:
|
|
|
243
318
|
)
|
|
244
319
|
|
|
245
320
|
|
|
246
|
-
|
|
321
|
+
|
|
247
322
|
|
|
248
323
|
_SLIDE_EXPAND_ALL = "all"
|
|
249
324
|
|
|
@@ -256,7 +331,7 @@ def _slide_schema_ids(tpl: dict) -> list[str]:
|
|
|
256
331
|
|
|
257
332
|
|
|
258
333
|
def _menu_entry(schema: dict, line_sync_key: str | None) -> dict:
|
|
259
|
-
|
|
334
|
+
|
|
260
335
|
props = schema.get("properties") if isinstance(schema.get("properties"), dict) else {}
|
|
261
336
|
entry: dict = {}
|
|
262
337
|
if schema.get("description"):
|
|
@@ -264,7 +339,7 @@ def _menu_entry(schema: dict, line_sync_key: str | None) -> dict:
|
|
|
264
339
|
entry["fields"] = list(props.keys())
|
|
265
340
|
if schema.get("required"):
|
|
266
341
|
entry["required"] = schema["required"]
|
|
267
|
-
|
|
342
|
+
|
|
268
343
|
list_key = line_sync_key if line_sync_key in props else None
|
|
269
344
|
if list_key is None:
|
|
270
345
|
for k, v in props.items():
|
|
@@ -283,8 +358,8 @@ def _menu_entry(schema: dict, line_sync_key: str | None) -> dict:
|
|
|
283
358
|
lst["maxItems"] = arr["maxItems"]
|
|
284
359
|
entry["list"] = lst
|
|
285
360
|
if line_sync_key:
|
|
286
|
-
|
|
287
|
-
|
|
361
|
+
|
|
362
|
+
|
|
288
363
|
entry["narration"] = (
|
|
289
364
|
f"items — one audio.narration.items line per templateData.{line_sync_key} entry, no text"
|
|
290
365
|
)
|
|
@@ -295,7 +370,7 @@ def _menu_entry(schema: dict, line_sync_key: str | None) -> dict:
|
|
|
295
370
|
|
|
296
371
|
|
|
297
372
|
def _apply_slide_view(tpl: dict, slide_ids: list[str] | None) -> dict:
|
|
298
|
-
|
|
373
|
+
|
|
299
374
|
ss = tpl.get("slideSchemas")
|
|
300
375
|
if not isinstance(ss, dict):
|
|
301
376
|
return tpl
|
|
@@ -333,20 +408,20 @@ def _apply_slide_view(tpl: dict, slide_ids: list[str] | None) -> dict:
|
|
|
333
408
|
return out
|
|
334
409
|
|
|
335
410
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
|
|
350
425
|
_EXAMPLES_DIR = Path(__file__).resolve().parent.parent / "video_dsl" / "schema" / "examples"
|
|
351
426
|
|
|
352
427
|
|
|
@@ -358,7 +433,7 @@ def _collect_examples() -> list[dict]:
|
|
|
358
433
|
if not entry.is_file():
|
|
359
434
|
continue
|
|
360
435
|
name = entry.name
|
|
361
|
-
|
|
436
|
+
|
|
362
437
|
if name.endswith(".dsl.json"):
|
|
363
438
|
kind = "dsl"
|
|
364
439
|
base = name[: -len(".dsl.json")]
|
|
@@ -367,7 +442,7 @@ def _collect_examples() -> list[dict]:
|
|
|
367
442
|
base = name[: -len(".binding.json")]
|
|
368
443
|
else:
|
|
369
444
|
continue
|
|
370
|
-
|
|
445
|
+
|
|
371
446
|
if "." in base:
|
|
372
447
|
template_id, variant = base.split(".", 1)
|
|
373
448
|
else:
|
|
@@ -376,7 +451,7 @@ def _collect_examples() -> list[dict]:
|
|
|
376
451
|
bucket["files"].append({
|
|
377
452
|
"kind": kind,
|
|
378
453
|
"variant": variant,
|
|
379
|
-
"path": str(entry.relative_to(_EXAMPLES_DIR.parent.parent.parent)),
|
|
454
|
+
"path": str(entry.relative_to(_EXAMPLES_DIR.parent.parent.parent)),
|
|
380
455
|
"absolutePath": str(entry),
|
|
381
456
|
"sizeBytes": entry.stat().st_size,
|
|
382
457
|
})
|
|
@@ -402,6 +477,14 @@ def main() -> None:
|
|
|
402
477
|
ap.add_argument("--filter-tag", help="Keep templates whose styleTags match this substring.")
|
|
403
478
|
ap.add_argument("--filter-aspect", help="Keep templates supporting this aspect ratio (e.g. 9:16).")
|
|
404
479
|
ap.add_argument("--filter-language", help="Keep templates tagged with this content language (zh|en|...).")
|
|
480
|
+
ap.add_argument("--user-media", choices=("yes", "no"),
|
|
481
|
+
help="Shortlist: yes = the user handed over their own images/videos (drop templates "
|
|
482
|
+
"with nowhere to put them); no = they did not (drop templates that need them).")
|
|
483
|
+
ap.add_argument("--source-doc", metavar="KIND",
|
|
484
|
+
help="Shortlist: the user gave a source document of this kind (e.g. pptx) — keep only "
|
|
485
|
+
"templates that build scenes from one; 'none' drops those templates.")
|
|
486
|
+
ap.add_argument("--narration", choices=("yes", "no"),
|
|
487
|
+
help="Shortlist: yes = the video needs voice-over; no = the user wants none.")
|
|
405
488
|
ap.add_argument("--include-beta", action="store_true", help="Also show beta templates (default: stable only).")
|
|
406
489
|
ap.add_argument("--json-output", action="store_true", help='Emit JSON ({"templates":[...]} or {"examples":[...]}) instead of a table.')
|
|
407
490
|
ap.add_argument("--open", dest="open_id", metavar="ID",
|
|
@@ -412,28 +495,28 @@ def main() -> None:
|
|
|
412
495
|
if args.open_id and (args.template_ids or args.list_examples or args.list_templates):
|
|
413
496
|
ap.error("--open cannot be combined with --template-id / --list-examples / --list-templates")
|
|
414
497
|
|
|
415
|
-
|
|
416
|
-
|
|
498
|
+
|
|
499
|
+
|
|
417
500
|
if args.include_beta:
|
|
418
501
|
os.environ["ENABLE_BETA_TEMPLATES"] = "1"
|
|
419
502
|
|
|
420
|
-
|
|
503
|
+
|
|
421
504
|
if args.list_examples:
|
|
422
505
|
examples = _collect_examples()
|
|
423
506
|
if args.json_output:
|
|
424
507
|
print(json.dumps({"examples": examples}, ensure_ascii=False))
|
|
425
508
|
return
|
|
426
509
|
if not examples:
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
|
|
437
520
|
print(
|
|
438
521
|
f"ℹ️ No local reference DSLs under {_EXAMPLES_DIR} — that directory is "
|
|
439
522
|
"optional and is not part of the published package.\n"
|
|
@@ -465,12 +548,12 @@ def main() -> None:
|
|
|
465
548
|
try:
|
|
466
549
|
templates = load_visible_templates()
|
|
467
550
|
except RegistryAuthError as exc:
|
|
468
|
-
|
|
469
|
-
|
|
551
|
+
|
|
552
|
+
|
|
470
553
|
print(f"❌ {exc}", file=sys.stderr)
|
|
471
554
|
sys.exit(EXIT_NOT_AUTHENTICATED)
|
|
472
555
|
except RegistryUnreachableError as exc:
|
|
473
|
-
|
|
556
|
+
|
|
474
557
|
print(f"❌ {exc}", file=sys.stderr)
|
|
475
558
|
sys.exit(EXIT_BACKEND_UNREACHABLE)
|
|
476
559
|
except RuntimeError as exc:
|
|
@@ -480,10 +563,10 @@ def main() -> None:
|
|
|
480
563
|
by_id = {t.get("templateId"): t for t in templates}
|
|
481
564
|
page_urls = registry_page_urls()
|
|
482
565
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
|
|
487
570
|
if args.open_id:
|
|
488
571
|
if args.open_id not in by_id:
|
|
489
572
|
_report_unknown_ids([args.open_id], templates)
|
|
@@ -502,11 +585,11 @@ def main() -> None:
|
|
|
502
585
|
_open_in_browser(page_url)
|
|
503
586
|
return
|
|
504
587
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
|
|
510
593
|
if args.template_ids:
|
|
511
594
|
picked: list[dict] = []
|
|
512
595
|
missing: list[str] = []
|
|
@@ -536,13 +619,13 @@ def main() -> None:
|
|
|
536
619
|
or s == _SLIDE_EXPAND_ALL] or None)
|
|
537
620
|
for t in picked
|
|
538
621
|
]
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
|
|
543
626
|
detail: dict = {"templates": picked}
|
|
544
|
-
|
|
545
|
-
|
|
627
|
+
|
|
628
|
+
|
|
546
629
|
groups = load_showcases([str(t.get("templateId")) for t in picked], _DETAIL_SHOWCASES)
|
|
547
630
|
links = {
|
|
548
631
|
str(t.get("templateId")): url
|
|
@@ -558,10 +641,26 @@ def main() -> None:
|
|
|
558
641
|
print(json.dumps(detail, ensure_ascii=False))
|
|
559
642
|
return
|
|
560
643
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
644
|
+
constraints = {
|
|
645
|
+
"tag": args.filter_tag,
|
|
646
|
+
"aspect": args.filter_aspect,
|
|
647
|
+
"language": args.filter_language,
|
|
648
|
+
"user_media": args.user_media,
|
|
649
|
+
"source_doc": args.source_doc,
|
|
650
|
+
"narration": args.narration,
|
|
651
|
+
}
|
|
652
|
+
filtering = any(v for v in constraints.values())
|
|
653
|
+
visible: list[dict] = []
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
excluded: list[dict] = []
|
|
658
|
+
for t in templates:
|
|
659
|
+
reasons = _exclusion_reasons(t, **constraints)
|
|
660
|
+
if reasons:
|
|
661
|
+
excluded.append({"templateId": t.get("templateId"), "name": t.get("name"), "reasons": reasons})
|
|
662
|
+
else:
|
|
663
|
+
visible.append(t)
|
|
565
664
|
|
|
566
665
|
if args.json_output:
|
|
567
666
|
if args.full:
|
|
@@ -584,25 +683,26 @@ def main() -> None:
|
|
|
584
683
|
s = _summarize(t)
|
|
585
684
|
if url := _page_url(t.get("templateId"), page_urls, groups):
|
|
586
685
|
s["pageUrl"] = url
|
|
587
|
-
|
|
588
|
-
|
|
686
|
+
|
|
687
|
+
|
|
589
688
|
if groups is not None and t.get("templateId") in groups:
|
|
590
689
|
s["showcases"] = summarize_group(groups[t["templateId"]], _LIST_SHOWCASES)
|
|
591
690
|
summaries.append(s)
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
))
|
|
691
|
+
listing: dict = {
|
|
692
|
+
"templates": summaries,
|
|
693
|
+
"detailHint": _detail_hint(),
|
|
694
|
+
}
|
|
695
|
+
if filtering:
|
|
696
|
+
listing["excluded"] = excluded
|
|
697
|
+
print(json.dumps(listing, ensure_ascii=False))
|
|
599
698
|
return
|
|
600
699
|
|
|
601
700
|
if not visible:
|
|
602
701
|
print(
|
|
603
702
|
"⚠️ No templates match the current filters "
|
|
604
|
-
"(check --include-beta or relax --filter-*).",
|
|
703
|
+
"(check --include-beta or relax --filter-* / --user-media / --source-doc / --narration).",
|
|
605
704
|
)
|
|
705
|
+
_print_excluded(excluded)
|
|
606
706
|
return
|
|
607
707
|
|
|
608
708
|
groups = load_showcases([str(t.get("templateId")) for t in visible], _LIST_SHOWCASES)
|
|
@@ -627,8 +727,11 @@ def main() -> None:
|
|
|
627
727
|
hint = _truncate(tpl.get("llmHint"), _SUMMARY_HINT_CHARS)
|
|
628
728
|
if hint:
|
|
629
729
|
print(f"{' ' * 22} ↳ {hint}")
|
|
630
|
-
|
|
631
|
-
|
|
730
|
+
not_for = tpl.get("notFor")
|
|
731
|
+
if isinstance(not_for, str) and not_for.strip():
|
|
732
|
+
print(f"{' ' * 22} ↳ not for: {not_for.strip()}")
|
|
733
|
+
|
|
734
|
+
|
|
632
735
|
group = (groups or {}).get(tpl.get("templateId"))
|
|
633
736
|
if group is not None:
|
|
634
737
|
print(f"{' ' * 22} ↳ samples: {int(group.get('showcaseCount') or 0)}")
|
|
@@ -644,6 +747,15 @@ def main() -> None:
|
|
|
644
747
|
)
|
|
645
748
|
if any_link:
|
|
646
749
|
print(f"Watch samples / layouts in the browser: open a detail link, or --open {first_id}")
|
|
750
|
+
_print_excluded(excluded)
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
def _print_excluded(excluded: list[dict]) -> None:
|
|
754
|
+
if not excluded:
|
|
755
|
+
return
|
|
756
|
+
print(f"\nExcluded by the current filters ({len(excluded)}):")
|
|
757
|
+
for item in excluded:
|
|
758
|
+
print(f" {item.get('templateId')}: {'; '.join(item.get('reasons') or [])}")
|
|
647
759
|
|
|
648
760
|
|
|
649
761
|
if __name__ == "__main__":
|