@remixmate/cli 0.13.1 → 0.13.3
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/dist/manifest.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"version": "0.13.
|
|
4
|
-
"generatedAt": "2026-10-
|
|
3
|
+
"version": "0.13.3",
|
|
4
|
+
"generatedAt": "2026-10-25T14:33:26.570Z",
|
|
5
5
|
"skills": [
|
|
6
6
|
{
|
|
7
7
|
"id": "doc-parse",
|
|
@@ -625,6 +625,7 @@
|
|
|
625
625
|
"motion": {
|
|
626
626
|
"type": "string",
|
|
627
627
|
"enum": [
|
|
628
|
+
"auto",
|
|
628
629
|
"static",
|
|
629
630
|
"kenburns-in",
|
|
630
631
|
"kenburns-out",
|
|
@@ -632,7 +633,7 @@
|
|
|
632
633
|
"pan-right",
|
|
633
634
|
"float"
|
|
634
635
|
],
|
|
635
|
-
"description": "Camera motion for every scene (scenes[].animationHints.motion), replacing the skeleton's alternating push-in / pull-out. **Must** pass when the user picked one: `运镜效果: 缓慢推近(kenburns-in)` → `kenburns-in
|
|
636
|
+
"description": "Camera motion for every scene (scenes[].animationHints.motion), replacing the skeleton's alternating push-in / pull-out. **Must** pass when the user picked one: `运镜效果: 缓慢推近(kenburns-in)` → `kenburns-in`; `运镜效果: 自动搭配(auto)` → `auto` (the template varies the motion scene by scene; do not write animationHints.motion yourself then)."
|
|
636
637
|
},
|
|
637
638
|
"ending_payload": {
|
|
638
639
|
"type": "array",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remixmate/cli",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.3",
|
|
4
4
|
"description": "AI media generation skills for Claude Code / Codex — 12 skills covering image, video, voice, digital human, web screenshot, web recording, script, template registry, rendering, Jianying export, and video deconstruction.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -489,7 +489,7 @@ python3 <SkillDir>/scripts/gen_script.py \
|
|
|
489
489
|
| `--payload KEY=VALUE` | Template-specific card choice, repeatable. `KEY` is a dotted path in `customPayloadSchema` (or `capabilities.frameLevelKeys`), e.g. `<field>=<value>`, `<object>.<field>=<value>`. Value is converted to the declared type (true/false, numbers) and checked against enum / range; undeclared keys are rejected. Written to every scene with sibling keys kept, applied before fit-caption duration estimation. | template default |
|
|
490
490
|
| `--ending-payload KEY=VALUE` | Same as `--payload`, but written only to the ending scene, e.g. `<object>.<field>=<closing line>`. On countdown templates it also appends an ending scene after #1 (it carries the #1 index; fill its narration and the item field `capabilities.countdown.itemProp` names). Errors when the script has no ending scene. | none |
|
|
491
491
|
| `--avatar-id` | Digital-human avatar id (`dh_xxx`) for templates declaring `capabilities.digitalHuman`. Adds asset `asset-avatar` (`type: avatar`, `source: gen-digital-human`, `payload.avatarId`) and sets `visuals.avatar.assetRef` on every scene whose slot reads it — one continuous presenter driven by the narration. Placement: template default, or `--payload digitalHuman.<field>=…` (a photo-only presenter is `--payload digitalHuman.posterUrl=<url>` without this flag). | none |
|
|
492
|
-
| `--motion` | Camera motion for every scene (`animationHints.motion`): `static` / `kenburns-in` / `kenburns-out` / `pan-left` / `pan-right` / `float`. | skeleton alternates in / out |
|
|
492
|
+
| `--motion` | Camera motion for every scene (`animationHints.motion`): `static` / `kenburns-in` / `kenburns-out` / `pan-left` / `pan-right` / `float`. `auto` removes the motion from every scene so the template picks (image-slide rotates push / pan / pull / pan); nothing is locked then. | skeleton alternates in / out |
|
|
493
493
|
| `--font-id` | Font library key (`font.uniq_id`), written to `global.font.fontId`; ab-render resolves and installs the real font at render time. Pass it whenever the user picked a font — the replicate card shows it as `名字(id)`, e.g. `字体: <名字>(<uniqId>)`; pass the id in the parentheses. Never pass a CSS family or the display name: an unknown key falls back to the template font with no error. | template font |
|
|
494
494
|
| `--font-name` | Display name of the picked font → `global.font.fontName` (display only, never used for rendering). Pass it with `--font-id`. | — |
|
|
495
495
|
| `--list-fonts` | Print the live font library (`/font/page`: id, name, lang, category, license) and exit; no script is generated and `--topic` is not needed. The ids it prints are the only valid `--font-id` values — use it when the user asks which fonts exist or names a font without its id. | — |
|
|
@@ -1104,7 +1104,38 @@ def _estimate_fit_caption_duration(
|
|
|
1104
1104
|
return max(parts) + duration_model.get("tailPaddingSec", 0)
|
|
1105
1105
|
|
|
1106
1106
|
|
|
1107
|
-
|
|
1107
|
+
|
|
1108
|
+
|
|
1109
|
+
_READING_UNIT_RE = re.compile(r"[\u3400-\u9fff\uf900-\ufaff]|[A-Za-z0-9]+")
|
|
1110
|
+
|
|
1111
|
+
|
|
1112
|
+
def _reading_units(text: str) -> int:
|
|
1113
|
+
return len(_READING_UNIT_RE.findall(text or ""))
|
|
1114
|
+
|
|
1115
|
+
|
|
1116
|
+
def _sequence_floor_frames(sequence: dict, preset_timing: dict, text: str, fps: int) -> int:
|
|
1117
|
+
|
|
1118
|
+
|
|
1119
|
+
|
|
1120
|
+
|
|
1121
|
+
|
|
1122
|
+
min_sec = sequence.get("minSec")
|
|
1123
|
+
if not (isinstance(min_sec, (int, float)) and min_sec > 0):
|
|
1124
|
+
floor = preset_timing.get("readingFloor")
|
|
1125
|
+
units = _reading_units(text)
|
|
1126
|
+
if not isinstance(floor, dict) or units == 0:
|
|
1127
|
+
return 0
|
|
1128
|
+
min_sec = floor["baseSec"] + floor["secPerUnit"] * units
|
|
1129
|
+
return math.floor(min_sec * fps + 0.5)
|
|
1130
|
+
|
|
1131
|
+
|
|
1132
|
+
def _estimate_fit_images_duration(custom_payload: dict, model: dict | None, fps: int = 30,
|
|
1133
|
+
headline: str = "") -> float:
|
|
1134
|
+
|
|
1135
|
+
|
|
1136
|
+
|
|
1137
|
+
|
|
1138
|
+
|
|
1108
1139
|
|
|
1109
1140
|
|
|
1110
1141
|
|
|
@@ -1138,7 +1169,22 @@ def _estimate_fit_images_duration(custom_payload: dict, model: dict | None, fps:
|
|
|
1138
1169
|
return max(1, math.floor(item.get("holdSec", item["sourceDurationSec"]) * fps + 0.5))
|
|
1139
1170
|
return max(min_frames, math.floor(item.get("holdSec", base_hold) * fps + 0.5))
|
|
1140
1171
|
|
|
1141
|
-
|
|
1172
|
+
frames = [item_frames(item) for item in sequence["items"]]
|
|
1173
|
+
natural = sum(frames)
|
|
1174
|
+
|
|
1175
|
+
title_override = ((sequence.get("text") or {}).get("title") or {}).get("text")
|
|
1176
|
+
candidates = (title_override, custom_payload.get("title"),
|
|
1177
|
+
(custom_payload.get("templateData") or {}).get("title"), headline)
|
|
1178
|
+
text = next((c for c in candidates if isinstance(c, str) and c.strip()), "")
|
|
1179
|
+
floor_frames = _sequence_floor_frames(sequence, preset_timing, text, fps)
|
|
1180
|
+
if floor_frames > natural:
|
|
1181
|
+
if all(item.get("type") == "video" for item in sequence["items"]):
|
|
1182
|
+
rate = timing.get("floorMinVideoRate")
|
|
1183
|
+
if rate is None:
|
|
1184
|
+
raise ValueError(f"fit-images with a floor requires durationModel.{key}.floorMinVideoRate")
|
|
1185
|
+
floor_frames = min(floor_frames, sum(math.floor(f / rate) for f in frames))
|
|
1186
|
+
natural = max(natural, floor_frames)
|
|
1187
|
+
return natural / fps
|
|
1142
1188
|
|
|
1143
1189
|
|
|
1144
1190
|
def _probe_sequence_video_duration(url: str) -> float | None:
|
|
@@ -1391,7 +1437,7 @@ def _build_carousel_caption_dsl(
|
|
|
1391
1437
|
|
|
1392
1438
|
effective_duration = duration
|
|
1393
1439
|
if duration_strategy == "fit-images":
|
|
1394
|
-
effective_duration = _estimate_fit_images_duration(custom_payload, duration_model)
|
|
1440
|
+
effective_duration = _estimate_fit_images_duration(custom_payload, duration_model, headline=headline)
|
|
1395
1441
|
if duration_strategy == "fit-caption":
|
|
1396
1442
|
est = _estimate_fit_caption_duration(custom_payload, duration_model, template_id)
|
|
1397
1443
|
if est > 0:
|
|
@@ -1873,18 +1919,23 @@ def _apply_scene_payload(
|
|
|
1873
1919
|
|
|
1874
1920
|
writes = _parse_payload_specs(template_config, payload_specs)
|
|
1875
1921
|
if motion:
|
|
1876
|
-
allowed = ("static", "kenburns-in", "kenburns-out", "pan-left", "pan-right", "float")
|
|
1922
|
+
allowed = ("auto", "static", "kenburns-in", "kenburns-out", "pan-left", "pan-right", "float")
|
|
1877
1923
|
if motion not in allowed:
|
|
1878
1924
|
raise ValueError(f"--motion {motion!r}: choose one of: {', '.join(allowed)}")
|
|
1879
1925
|
|
|
1880
1926
|
for scene in dsl.get("scenes", []):
|
|
1881
1927
|
_write_payload(scene.setdefault("customPayload", {}), writes)
|
|
1882
|
-
if
|
|
1883
|
-
|
|
1884
|
-
|
|
1928
|
+
hints = scene.get("animationHints") if isinstance(scene.get("animationHints"), dict) else None
|
|
1929
|
+
if motion == "auto":
|
|
1930
|
+
|
|
1931
|
+
|
|
1932
|
+
if hints is not None:
|
|
1933
|
+
hints.pop("motion", None)
|
|
1934
|
+
elif motion:
|
|
1935
|
+
scene["animationHints"] = {**(hints or {}), "motion": motion}
|
|
1885
1936
|
for parts, _ in writes:
|
|
1886
1937
|
_lock_scene_path(dsl, "customPayload." + ".".join(parts))
|
|
1887
|
-
if motion:
|
|
1938
|
+
if motion and motion != "auto":
|
|
1888
1939
|
_lock_scene_path(dsl, "animationHints.motion")
|
|
1889
1940
|
|
|
1890
1941
|
|
|
@@ -2817,7 +2868,8 @@ Examples:
|
|
|
2817
2868
|
parser.add_argument(
|
|
2818
2869
|
"--motion", default=None,
|
|
2819
2870
|
help="Camera motion for every scene (animationHints.motion): static / kenburns-in / kenburns-out / "
|
|
2820
|
-
"pan-left / pan-right / float. Replaces the skeleton's alternating default."
|
|
2871
|
+
"pan-left / pan-right / float. Replaces the skeleton's alternating default. `auto` removes the "
|
|
2872
|
+
"motion from every scene so the template picks (image-slide rotates push / pan / pull / pan).",
|
|
2821
2873
|
)
|
|
2822
2874
|
parser.add_argument("--allow-digital-human", action="store_true", help="Allow digital-human assets")
|
|
2823
2875
|
parser.add_argument("--avatar-id", default=None, help="Digital-human avatar id (dh_xxx) for templates declaring capabilities.digitalHuman: adds one gen-digital-human asset referenced by every scene's visuals.avatar.assetRef (one continuous presenter driven by the narration). Placement stays the template default unless set with --payload digitalHuman.<field>=...")
|
|
@@ -83,6 +83,7 @@
|
|
|
83
83
|
"motion": {
|
|
84
84
|
"type": "string",
|
|
85
85
|
"enum": [
|
|
86
|
+
"auto",
|
|
86
87
|
"static",
|
|
87
88
|
"kenburns-in",
|
|
88
89
|
"kenburns-out",
|
|
@@ -90,7 +91,7 @@
|
|
|
90
91
|
"pan-right",
|
|
91
92
|
"float"
|
|
92
93
|
],
|
|
93
|
-
"description": "Camera motion for every scene (scenes[].animationHints.motion), replacing the skeleton's alternating push-in / pull-out. **Must** pass when the user picked one: `运镜效果: 缓慢推近(kenburns-in)` → `kenburns-in
|
|
94
|
+
"description": "Camera motion for every scene (scenes[].animationHints.motion), replacing the skeleton's alternating push-in / pull-out. **Must** pass when the user picked one: `运镜效果: 缓慢推近(kenburns-in)` → `kenburns-in`; `运镜效果: 自动搭配(auto)` → `auto` (the template varies the motion scene by scene; do not write animationHints.motion yourself then)."
|
|
94
95
|
},
|
|
95
96
|
"ending_payload": {
|
|
96
97
|
"type": "array",
|