@remixmate/cli 0.13.8 → 0.13.10
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/handlers/gen-digital-human.js +18 -18
- package/dist/handlers/gen-voice.js +19 -15
- package/dist/manifest.json +142 -13
- package/package.json +1 -1
- package/skills/gen-digital-human/SKILL.md +10 -2
- package/skills/gen-digital-human/skill.json +33 -4
- package/skills/gen-script/SKILL.md +6 -0
- package/skills/gen-script/scripts/gen_script.py +382 -7
- package/skills/gen-script/skill.json +35 -0
- package/skills/gen-voice/SKILL.md +22 -10
- package/skills/gen-voice/skill.json +55 -6
- package/skills/render-video/scripts/render_video.py +100 -1
- package/skills/render-video/skill.json +4 -0
- package/skills/template-registry/scripts/registry_loader.py +99 -0
- package/skills/template-registry/video_dsl/runtime/dsl_validator.py +57 -10
- package/skills/template-registry/video_dsl/runtime/presenter_speech.py +104 -0
- package/skills/template-registry/video_dsl/runtime/timeline_compiler.py +1 -0
- package/skills/template-registry/video_dsl/schema/video-dsl-v1alpha1.json +26 -0
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
var
|
|
2
|
-
`);return}if(
|
|
1
|
+
var b=Object.defineProperty;var u=(t,e)=>b(t,"name",{value:e,configurable:!0});import{mmPost as p,pollUntil as S,resolveHttpContext as M,SkillError as i}from"../http.js";import{getCapabilities as N}from"../capabilities.js";import{emitProgress as v}from"../progress.js";import{isTrue as g,toNumber as I}from"./shared.js";const E=["video","image"],k=["hifly","heygen"],y="hifly";function c(t){return t==null?"":String(t).trim()}u(c,"toId");async function O(t,e){const r=g(e.mine),a=!r&&g(e.all),n=!r&&!a,o={current:1,pageSize:100};n?o.featured=!0:a&&(o.source=c(e.source)||y),r&&(o.mine=!0),e.gender&&(o.gender=String(e.gender)),e.name&&(o.name=String(e.name));const f=await p(t,"/digital-human/avatar/page",o,{timeoutMs:3e4}),l=f.list??[];if(g(e.json_output)){process.stdout.write(JSON.stringify({avatars:l})+`
|
|
2
|
+
`);return}if(l.length===0){process.stdout.write(n?`⚠️ No curated public avatars match. Try --all for the full inventory, or --mine for your own.
|
|
3
3
|
`:`⚠️ No avatars available
|
|
4
4
|
`);return}if(n){process.stdout.write(`
|
|
5
5
|
${"ID".padEnd(24)} ${"Name".padEnd(16)} ${"Gender".padEnd(8)} ${"Ratio".padEnd(6)} ${"Scenes".padEnd(22)} Voice
|
|
6
6
|
`),process.stdout.write(`${"-".repeat(24)} ${"-".repeat(16)} ${"-".repeat(8)} ${"-".repeat(6)} ${"-".repeat(22)} ${"-".repeat(12)}
|
|
7
|
-
`);for(const d of
|
|
7
|
+
`);for(const d of l)process.stdout.write(`${c(d.id).padEnd(24)} ${String(d.name??"").padEnd(16)} ${String(d.gender??"").padEnd(8)} ${String(d.aspectRatio??"").padEnd(6)} ${(d.tags??[]).join(",").padEnd(22)} ${d.defaultVoiceId||"-"}
|
|
8
8
|
`)}else{process.stdout.write(`
|
|
9
9
|
${"ID".padEnd(24)} ${"Name".padEnd(16)} ${"Source".padEnd(14)} ${"Gender".padEnd(8)} ${"Ratio".padEnd(6)} Clone
|
|
10
10
|
`),process.stdout.write(`${"-".repeat(24)} ${"-".repeat(16)} ${"-".repeat(14)} ${"-".repeat(8)} ${"-".repeat(6)} ${"-".repeat(8)}
|
|
11
|
-
`);for(const d of
|
|
12
|
-
`)}const
|
|
13
|
-
${
|
|
11
|
+
`);for(const d of l)process.stdout.write(`${c(d.id).padEnd(24)} ${String(d.name??"").padEnd(16)} ${String(d.source??"").padEnd(14)} ${String(d.gender??"").padEnd(8)} ${String(d.aspectRatio??"").padEnd(6)} ${d.cloneStatus||"-"}
|
|
12
|
+
`)}const s=f.pager?.total??l.length;process.stdout.write(`
|
|
13
|
+
${l.length} avatar(s) shown${s>l.length?` of ${s}; narrow with --name`:""}
|
|
14
14
|
`),process.stdout.write(n?`💡 Use --avatar-id <ID> to pick an avatar; pass its Voice as --voice-id for a matching voice
|
|
15
15
|
`:`💡 Use --avatar-id <ID> to pick an avatar for generation
|
|
16
|
-
`)}
|
|
16
|
+
`)}u(O,"listAvatars");async function R(t,e){for(const r of[{mine:!0},{featured:!0}]){const n=((await p(t,"/digital-human/avatar/page",{current:1,pageSize:100,...r},{timeoutMs:3e4})).list??[]).find(o=>c(o.id)===e);if(n)return n}return{id:e,source:y}}u(R,"fetchAvatarInfo");function L(t,e){const r=(t.source??"").trim()||y;if(!e.includes(r))throw new i(`❌ avatar uses provider "${r}", which is not available (supported: ${e.join("/")}). Pick another avatar from --list-avatars.`);return r}u(L,"resolveSource");async function T(t,e){const r=c(e.clone_url);if(!/^https?:\/\//i.test(r))throw new i("❌ --clone needs --clone-url <public http(s) URL of a video or portrait image> (local files are not uploaded)");const a=c(e.name);if(!a)throw new i("❌ --clone needs --name <avatar name>");const n=c(e.clone_type)||"video";if(!E.includes(n))throw new i(`❌ --clone-type must be one of ${E.join(" / ")}, received: ${n}`);const o=c(e.clone_provider)||y;if(!k.includes(o))throw new i(`❌ --clone-provider must be one of ${k.join(" / ")}, received: ${o}`);if(o==="heygen"&&n!=="image")throw new i("❌ HeyGen avatars are created from a photo only; add --clone-type image");const f={name:a,sourceType:n,fileUrl:r};o!==y&&(f.provider=o),e.thumbnail_url&&(f.thumbnailUrl=c(e.thumbnail_url)),e.gender&&(f.gender=c(e.gender)),v({phase:"gen-digital-human:clone-request",sourceType:n,provider:o});const l=await p(t,"/digital-human/avatar/clone",f,{timeoutMs:6e4}),s=c(l.id);if(!s)throw new i(`❌ could not parse the new avatar id; API returned: ${JSON.stringify(l)}`);v({phase:"gen-digital-human:clone-async",avatarId:s});const d=g(e.json_output);if(e.wait!==void 0&&!g(e.wait)){if(d){process.stdout.write(JSON.stringify({avatarId:s,cloneStatus:l.cloneStatus??"cloning"})+`
|
|
17
17
|
`);return}process.stdout.write(`
|
|
18
|
-
🧬 Clone submitted, avatar id: ${
|
|
19
|
-
`),process.stdout.write(`💡 Check progress with --clone-status --avatar-id ${
|
|
20
|
-
`);return}let
|
|
18
|
+
🧬 Clone submitted, avatar id: ${s}
|
|
19
|
+
`),process.stdout.write(`💡 Check progress with --clone-status --avatar-id ${s}
|
|
20
|
+
`);return}let m;try{m=await S(async()=>{const w=await p(t,"/digital-human/avatar/clone-status",{id:s},{timeoutMs:3e4}),$=w.cloneStatus??"unknown";if(v({phase:"gen-digital-human:clone-poll",avatarId:s,status:$}),$==="done")return w;if($==="failed")throw new i(`❌ avatar clone failed: ${w.cloneError||"unknown error"}`);return null},{intervalMs:1e4,timeoutMs:20*6e4})}catch(w){throw w instanceof i&&/polling timed out/.test(w.message)?new i(`❌ avatar ${s} is still cloning; rerun --clone-status --avatar-id ${s} later`):w}if(d){process.stdout.write(JSON.stringify({avatarId:s,cloneStatus:m.cloneStatus})+`
|
|
21
21
|
`);return}process.stdout.write(`
|
|
22
|
-
✅ Avatar cloned, id: ${
|
|
23
|
-
`),process.stdout.write(`💡 Generate with --avatar-id ${
|
|
24
|
-
`)}
|
|
22
|
+
✅ Avatar cloned, id: ${s}
|
|
23
|
+
`),process.stdout.write(`💡 Generate with --avatar-id ${s} --text "..."
|
|
24
|
+
`)}u(T,"cloneAvatar");async function j(t,e,r){const a=await p(t,"/digital-human/avatar/clone-status",{id:e},{timeoutMs:3e4}),n=a.cloneStatus||"unknown";if(r){process.stdout.write(JSON.stringify({avatarId:e,cloneStatus:n,error:a.cloneError||null})+`
|
|
25
25
|
`);return}process.stdout.write(`
|
|
26
26
|
clone status: ${n}
|
|
27
27
|
`),n==="failed"&&process.stdout.write(` failure reason: ${a.cloneError||"unknown error"}
|
|
28
|
-
`)}
|
|
28
|
+
`)}u(j,"checkCloneStatus");async function C(t,e,r){const a=await p(t,"/digital-human/video/status",{generationId:e},{timeoutMs:3e4}),n=a.status??"unknown";if(r){process.stdout.write(JSON.stringify({status:n,url:a.videoUrl??null,generationId:e})+`
|
|
29
29
|
`);return}if(process.stdout.write(`
|
|
30
30
|
task status: ${n}
|
|
31
31
|
`),a.videoUrl&&process.stdout.write(` video URL: ${a.videoUrl}
|
|
32
|
-
`),n==="failed"){const
|
|
33
|
-
`)}}
|
|
32
|
+
`),n==="failed"){const o=a.errorMessage??a.error??a.message??"unknown error";process.stdout.write(` failure reason: ${o}
|
|
33
|
+
`)}}u(C,"checkStatus");const D=30*6e4;async function J(t,e){return await S(async()=>{const a=await p(t,"/digital-human/video/status",{generationId:e},{timeoutMs:3e4}),n=a.status??"unknown";if(v({phase:"gen-digital-human:poll",generationId:e,status:n}),n==="completed")return a.videoUrl??"";if(n==="failed"){const o=a.errorMessage??a.error??a.message??"unknown error";throw new i(`❌ video generation failed: ${o}`)}return null},{intervalMs:5e3,timeoutMs:D}).catch(a=>{throw a instanceof i&&/polling timed out/.test(a.message)?new i(`${a.message}; still generating on the server — resume with --check-status --generation-id ${e}`):a})}u(J,"pollVideoStatus");function P(t){const e=String(t.video_url??"").trim(),r=String(t.audio_url??"").trim(),a=String(t.lipsync_mode??"speed");for(const[n,o]of[["--video-url",e],["--audio-url",r]])if(!/^https?:\/\//i.test(o))throw new i(`❌ lip-sync needs a public http(s) URL for ${n}, received: ${o}`);if(a!=="speed"&&a!=="precision")throw new i(`❌ --lipsync-mode must be speed or precision, received: ${a}`);return{videoUrl:e,audioUrl:r,mode:a}}u(P,"buildLipsyncRequest");async function H(t,e){const r=await M(e.skillName,{apiBaseUrl:t.api_base_url,privateToken:t.priv_token,preflightToken:e.auth?.token}),a=g(t.json_output);if(g(t.list_avatars))return O(r,t);if(g(t.clone))return T(r,t);if(g(t.clone_status)){const h=c(t.avatar_id);if(!h)throw new i("❌ clone-status mode requires --avatar-id");return j(r,h,a)}if(g(t.check_status)){const h=I(t.generation_id,"generation_id");if(h===void 0)throw new i("❌ check-status mode requires --generation-id");return C(r,h,a)}if(t.video_url){const h=P(t);v({phase:"gen-digital-human:request",mode:"lipsync",lipsyncMode:h.mode});const _=await p(r,"/digital-human/lipsync/generate",h,{timeoutMs:6e4});if(!_.generationId)throw new i(`❌ could not parse generationId; API returned: ${JSON.stringify(_)}`);return U(r,_.generationId,a)}const n=c(t.avatar_id);if(!n)throw new i("❌ provide --avatar-id to pick the avatar (use --list-avatars to see available avatars)");const o=(t.audio_url??"").trim(),f=t.text??"";if(o&&!/^https?:\/\//i.test(o))throw new i(`❌ --audio-url needs a public http(s) URL (local files are not uploaded), received: ${o}`);if(!o&&!f.trim())throw new i("❌ in TTS mode provide --text, or use --audio-url for audio-driven mode");const l=(await N(t.api_base_url)).digitalHuman;v({phase:"gen-digital-human:resolve-avatar",avatarId:n});const s=await R(r,n);if(s.cloneStatus==="cloning")throw new i(`❌ avatar ${n} is still cloning; check with --clone-status --avatar-id ${n}`);if(s.cloneStatus==="failed")throw new i(`❌ avatar ${n} failed to clone (${s.cloneError||"unknown error"}); clone it again`);const d=L(s,l.sources),m={avatarId:n,source:d};o?m.audioUrl=o:(m.text=f,m.voiceId=c(t.voice_id)||c(s.defaultVoiceId)||l.defaultVoiceId,t.voice_name&&(m.voiceName=String(t.voice_name))),v({phase:"gen-digital-human:request",avatarId:n,source:d,mode:o?"audio":"tts"});const w=await p(r,"/digital-human/video/generate",m,{timeoutMs:6e4}),$=w.generationId;if(!$)throw new i(`❌ could not parse generationId; API returned: ${JSON.stringify(w)}`);return U(r,$,a)}u(H,"genDigitalHuman");async function U(t,e,r){v({phase:"gen-digital-human:async",generationId:e});const a=await J(t,e);if(!a)throw new i(`❌ video generated but no URL was returned; rerun --check-status --generation-id ${e} later`);if(r){process.stdout.write(JSON.stringify({url:a,generationId:e})+`
|
|
34
34
|
`);return}process.stdout.write(`
|
|
35
35
|
🔗 Video URL (renders directly, no download needed):
|
|
36
|
-
${
|
|
36
|
+
${a}
|
|
37
37
|
`),process.stdout.write(`
|
|
38
38
|
🎉 Digital-human video generation done!
|
|
39
|
-
`)}
|
|
39
|
+
`)}u(U,"finishGeneration");export{P as buildLipsyncRequest,H as genDigitalHuman};
|
|
@@ -1,19 +1,23 @@
|
|
|
1
|
-
var
|
|
2
|
-
`);return}process.stdout.write(
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
`);
|
|
6
|
-
|
|
7
|
-
${
|
|
8
|
-
`)
|
|
9
|
-
`)
|
|
10
|
-
|
|
11
|
-
`)
|
|
12
|
-
`)
|
|
13
|
-
|
|
1
|
+
var T=Object.defineProperty;var l=(e,o)=>T(e,"name",{value:o,configurable:!0});import{mmPost as w,resolveHttpContext as $,SkillError as h}from"../http.js";import{getCapabilities as b}from"../capabilities.js";import{emitProgress as A}from"../progress.js";import{isTrue as m,toNumber as y}from"./shared.js";const L=[{id:"Chinese (Mandarin)_Male_Announcer",language:"zh",name:"Mandarin Male Announcer (legacy default)"},{id:"english-male-friendly-01",language:"en",name:"Friendly Male (English, TBD)"}],E=100,_=1,x=3,M={male:"男",m:"男",man:"男",female:"女",f:"女",woman:"女"};function f(e){if(e==null)return;const o=String(e).trim();return o||void 0}l(f,"optString");function v(e,o){return(e.tagItems??[]).filter(t=>t.category===o&&t.name).map(t=>t.name).join("/")}l(v,"tagsOf");function N(e){const o=y(e.page,"page")??1;if(!Number.isInteger(o)||o<1)throw new h(`❌ page must be a positive integer, received: ${e.page}`);const t={current:o,pageSize:E},p=f(e.name);p&&(t.name=p);const r=f(e.language);r&&(t.language=r);const i=f(e.gender);if(i&&(t.gender=M[i.toLowerCase()]??i),m(e.mine))return{path:"/voice/collect/myPage",body:t};const s=f(e.provider)?.toLowerCase();s&&(t.provider=s);const a=f(e.voice_type)?.toLowerCase();return a&&(t.voiceType=a),{path:"/voice/page",body:t}}l(N,"buildVoiceListRequest");async function k(e,o){const{path:t,body:p}=N(o),r=await w(e,t,p,{timeoutMs:3e4}),i=r.list??[],s=t!=="/voice/page",a=Number(p.current),g=r.pager?.total??i.length;if(m(o.json_output)){process.stdout.write(JSON.stringify({page:a,total:g,voices:i.map(n=>({id:n.uniqId??"",name:n.name??"",provider:n.provider??"",type:n.voiceType??"",gender:v(n,x),language:v(n,_)}))})+`
|
|
2
|
+
`);return}if(i.length===0){process.stdout.write(a>1?`⚠️ No voices on page ${a} (total ${g})
|
|
3
|
+
`:s?`⚠️ You have no cloned voices
|
|
4
|
+
`:`⚠️ No voices match these filters
|
|
5
|
+
`);return}const d=Math.min(48,Math.max(30,...i.map(n=>(n.uniqId??"").length)));process.stdout.write(`
|
|
6
|
+
${"Voice ID".padEnd(d)} ${"Provider".padEnd(10)} ${"Type".padEnd(6)} ${"Gender".padEnd(6)} ${"Language".padEnd(12)} Name
|
|
7
|
+
`),process.stdout.write(`${"-".repeat(d)} ${"-".repeat(10)} ${"-".repeat(6)} ${"-".repeat(6)} ${"-".repeat(12)} ${"-".repeat(20)}
|
|
8
|
+
`);for(const n of i)process.stdout.write(`${(n.uniqId??"").padEnd(d)} ${(n.provider??"").padEnd(10)} ${(n.voiceType??"").padEnd(6)} ${v(n,x).padEnd(6)} ${v(n,_).padEnd(12)} ${n.name??""}
|
|
9
|
+
`);const u=(a-1)*E+i.length;process.stdout.write(`
|
|
10
|
+
${i.length} voice(s) on page ${a}, ${g} in total
|
|
11
|
+
`),g>u&&process.stdout.write(`💡 More voices: add --page ${a+1}, or narrow with --provider / --gender / --language / --name
|
|
12
|
+
`),process.stdout.write(`💡 Use --voice-id <id> to select a voice; the provider is picked from the voice automatically
|
|
13
|
+
`)}l(k,"listVoicesRemote");function C(){process.stderr.write(`⚠️ --local prints a hardcoded 2-entry fallback list, NOT the available voices. Drop --local to query the live catalog.
|
|
14
|
+
`);for(const e of L)process.stdout.write(`${e.id} ${e.language} ${e.name}
|
|
15
|
+
`)}l(C,"listVoicesLocal");async function R(e,o){if(m(e.list_voices)){if(m(e.local))return C();const c=await $(o.skillName,{apiBaseUrl:e.api_base_url,privateToken:e.priv_token,preflightToken:o.auth?.token});return k(c,e)}const t=e.text;if(!t||typeof t!="string"||!t.trim())throw new h("❌ Please provide --text to specify the text to synthesize");const p=e.api_base_url,r=(await b(p)).voice,i=e.voice_id??r.defaultVoiceId,s=y(e.speed,"speed")??1;if(s<r.speedMin||s>r.speedMax)throw new h(`❌ speed must be in the range ${r.speedMin}~${r.speedMax}`);const a=e.language_boost?.trim()||void 0,g=await $(o.skillName,{apiBaseUrl:p,privateToken:e.priv_token,preflightToken:o.auth?.token});A({phase:"gen-voice:request",voiceId:i,speed:s});const d=await w(g,"/tool/tts",{provider:r.provider,text:t,voiceSetting:{voiceId:i,speed:s},outputFormat:"url",...a?{languageBoost:a}:{}},{timeoutMs:12e4}),u=d.audio;if(!u)throw new h(`❌ API response did not contain an audio URL: ${JSON.stringify(d)}`);if(m(e.json_output)){process.stdout.write(JSON.stringify({url:u,audio_length_ms:d.extraInfo?.audioLength??null,subtitles:(d.subtitles??[]).map(c=>({text:c.text??"",timeBegin:c.timeBegin??0,timeEnd:c.timeEnd??0,...typeof c.textBegin=="number"&&typeof c.textEnd=="number"?{textBegin:c.textBegin,textEnd:c.textEnd}:{}}))})+`
|
|
16
|
+
`);return}const n=d.extraInfo?.audioLength;typeof n=="number"&&process.stdout.write(`
|
|
17
|
+
🕐 Audio length: ${(n/1e3).toFixed(1)} s
|
|
14
18
|
`),process.stdout.write(`
|
|
15
19
|
🔗 Audio URL (plays directly, no download needed):
|
|
16
|
-
${
|
|
20
|
+
${u}
|
|
17
21
|
`),process.stdout.write(`
|
|
18
22
|
🎉 TTS synthesis done!
|
|
19
|
-
`)}
|
|
23
|
+
`)}l(R,"genVoice");export{N as buildVoiceListRequest,R as genVoice};
|
package/dist/manifest.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"version": "0.13.
|
|
4
|
-
"generatedAt": "2026-
|
|
3
|
+
"version": "0.13.10",
|
|
4
|
+
"generatedAt": "2026-11-05T14:34:43.632Z",
|
|
5
5
|
"skills": [
|
|
6
6
|
{
|
|
7
7
|
"id": "doc-parse",
|
|
@@ -203,7 +203,15 @@
|
|
|
203
203
|
},
|
|
204
204
|
"all": {
|
|
205
205
|
"type": "boolean",
|
|
206
|
-
"description": "With list_avatars=true: list
|
|
206
|
+
"description": "With list_avatars=true: list one provider's full public inventory (HiFly by default, 500+, mostly uncurated, no scene tags) instead of the curated public avatars. Only for when the curated set has nothing suitable."
|
|
207
|
+
},
|
|
208
|
+
"source": {
|
|
209
|
+
"type": "string",
|
|
210
|
+
"enum": [
|
|
211
|
+
"hifly",
|
|
212
|
+
"heygen"
|
|
213
|
+
],
|
|
214
|
+
"description": "With list_avatars=true and all=true: which provider's full public inventory to list. Defaults to hifly. heygen is only available when the capabilities catalog lists it."
|
|
207
215
|
},
|
|
208
216
|
"name": {
|
|
209
217
|
"type": "string",
|
|
@@ -223,7 +231,7 @@
|
|
|
223
231
|
},
|
|
224
232
|
"clone": {
|
|
225
233
|
"type": "boolean",
|
|
226
|
-
"description": "Clone mode: create a custom HiFly
|
|
234
|
+
"description": "Clone mode: create a custom avatar (HiFly by default, or HeyGen with clone_provider=heygen) from a video (free) or a portrait image (costs credits per clone, refunded automatically if cloning fails; see the pricing page). Requires clone_url and name. Waits until cloning finishes unless wait=false."
|
|
227
235
|
},
|
|
228
236
|
"clone_url": {
|
|
229
237
|
"type": "string",
|
|
@@ -237,6 +245,14 @@
|
|
|
237
245
|
],
|
|
238
246
|
"description": "With clone=true: source type. Defaults to video."
|
|
239
247
|
},
|
|
248
|
+
"clone_provider": {
|
|
249
|
+
"type": "string",
|
|
250
|
+
"enum": [
|
|
251
|
+
"hifly",
|
|
252
|
+
"heygen"
|
|
253
|
+
],
|
|
254
|
+
"description": "With clone=true: where to create the avatar. hifly (default) accepts a video or a photo; heygen accepts a photo only (clone_type=image, jpg/png ≤ 32MB) and gives more natural lip-sync for non-Chinese speech at a higher per-second price. Only use heygen when the capabilities catalog lists it."
|
|
255
|
+
},
|
|
240
256
|
"thumbnail_url": {
|
|
241
257
|
"type": "string",
|
|
242
258
|
"description": "With clone=true: optional cover image URL shown in avatar lists (image clones default to the image itself). The output aspect ratio always follows the cloned source; it cannot be chosen at generation time."
|
|
@@ -253,13 +269,25 @@
|
|
|
253
269
|
"type": "string",
|
|
254
270
|
"description": "Narration text (TTS mode, ≤ 10000 chars). Synthesized with voice_id, then drives the avatar; the speech must last at least 5 s. Ignored when audio_url is given."
|
|
255
271
|
},
|
|
272
|
+
"video_url": {
|
|
273
|
+
"type": "string",
|
|
274
|
+
"description": "Lip-sync mode: public http(s) URL of the video to re-voice. Needs audio_url. Replaces avatar_id."
|
|
275
|
+
},
|
|
276
|
+
"lipsync_mode": {
|
|
277
|
+
"type": "string",
|
|
278
|
+
"enum": [
|
|
279
|
+
"speed",
|
|
280
|
+
"precision"
|
|
281
|
+
],
|
|
282
|
+
"description": "Lip-sync quality, default speed. precision costs about 4x as much."
|
|
283
|
+
},
|
|
256
284
|
"audio_url": {
|
|
257
285
|
"type": "string",
|
|
258
286
|
"description": "Audio-driven mode: public http(s) URL of mp3 / m4a / wav, 5 s – 30 min, ≤ 100 MB. Takes precedence over text / voice_id."
|
|
259
287
|
},
|
|
260
288
|
"voice_id": {
|
|
261
289
|
"type": "string",
|
|
262
|
-
"description": "Voice id (TTS mode). Shares the catalog with gen-voice (including cloned voices) — call gen_voice with list_voices=true to see available ids rather than inventing one."
|
|
290
|
+
"description": "Voice id (TTS mode). Optional: defaults to the avatar's own voice (a curated avatar's suggested voice, or a HeyGen digital twin's cloned voice), else the service default. Shares the catalog with gen-voice (any provider, including cloned voices) — call gen_voice with list_voices=true to see available ids rather than inventing one."
|
|
263
291
|
},
|
|
264
292
|
"voice_name": {
|
|
265
293
|
"type": "string",
|
|
@@ -285,7 +313,9 @@
|
|
|
285
313
|
"avatar_id",
|
|
286
314
|
"text",
|
|
287
315
|
"voice_id",
|
|
288
|
-
"audio_url"
|
|
316
|
+
"audio_url",
|
|
317
|
+
"video_url",
|
|
318
|
+
"lipsync_mode"
|
|
289
319
|
],
|
|
290
320
|
"advanced": [],
|
|
291
321
|
"hidden": [
|
|
@@ -294,11 +324,13 @@
|
|
|
294
324
|
"clone",
|
|
295
325
|
"clone_url",
|
|
296
326
|
"clone_type",
|
|
327
|
+
"clone_provider",
|
|
297
328
|
"thumbnail_url",
|
|
298
329
|
"wait",
|
|
299
330
|
"clone_status",
|
|
300
331
|
"mine",
|
|
301
332
|
"all",
|
|
333
|
+
"source",
|
|
302
334
|
"name",
|
|
303
335
|
"gender",
|
|
304
336
|
"check_status",
|
|
@@ -607,6 +639,43 @@
|
|
|
607
639
|
"type": "string",
|
|
608
640
|
"description": "Media fill mode for templates declaring capabilities.mediaBinding.options.fit; one of that option's enum in the template's customPayloadSchema (its descriptions explain each value). Beats the preset's own fit. **Must** pass when the user picked it: `显示方式: <名称>(<id>)` → pass `<id>`; `auto` keeps the template / preset default."
|
|
609
641
|
},
|
|
642
|
+
"source": {
|
|
643
|
+
"type": "string",
|
|
644
|
+
"description": "Presenter source, for templates that declare capabilities.sources: digital-human, lip-sync or real-footage. Omit to use the template's default."
|
|
645
|
+
},
|
|
646
|
+
"template_style": {
|
|
647
|
+
"type": "string",
|
|
648
|
+
"description": "Visual style for templates that ship several, e.g. talking-head-studio: douyin-bold, news-anchor, live-ecommerce."
|
|
649
|
+
},
|
|
650
|
+
"presenter_video": {
|
|
651
|
+
"type": "string",
|
|
652
|
+
"description": "URL of the user's talking-head video. Required with source real-footage or lip-sync."
|
|
653
|
+
},
|
|
654
|
+
"lipsync_mode": {
|
|
655
|
+
"type": "string",
|
|
656
|
+
"enum": [
|
|
657
|
+
"speed",
|
|
658
|
+
"precision"
|
|
659
|
+
],
|
|
660
|
+
"description": "Lip-sync quality for source lip-sync, default speed. precision costs about 4x as much."
|
|
661
|
+
},
|
|
662
|
+
"broll_asset": {
|
|
663
|
+
"type": "array",
|
|
664
|
+
"items": {
|
|
665
|
+
"type": "string"
|
|
666
|
+
},
|
|
667
|
+
"description": "URLs of images or videos the user picked as b-roll from their asset library (the replicate card's 空镜素材 asset lines), on any source. Not scene_images: scene_images treats every entry as an image."
|
|
668
|
+
},
|
|
669
|
+
"broll_mode": {
|
|
670
|
+
"type": "string",
|
|
671
|
+
"enum": [
|
|
672
|
+
"auto",
|
|
673
|
+
"ai-image",
|
|
674
|
+
"ai-video",
|
|
675
|
+
"none"
|
|
676
|
+
],
|
|
677
|
+
"description": "Scenes without a broll_asset (the replicate card's 其余画面): auto (the visual style's default), ai-image, ai-video (at most 3 scenes, the rest stay images) or none."
|
|
678
|
+
},
|
|
610
679
|
"narration": {
|
|
611
680
|
"type": "string",
|
|
612
681
|
"enum": [
|
|
@@ -752,6 +821,10 @@
|
|
|
752
821
|
"background_preset",
|
|
753
822
|
"sequence_preset",
|
|
754
823
|
"sequence_fit",
|
|
824
|
+
"source",
|
|
825
|
+
"template_style",
|
|
826
|
+
"presenter_video",
|
|
827
|
+
"lipsync_mode",
|
|
755
828
|
"narration",
|
|
756
829
|
"background_asset",
|
|
757
830
|
"payload",
|
|
@@ -769,6 +842,8 @@
|
|
|
769
842
|
"subheadline",
|
|
770
843
|
"carousel_items",
|
|
771
844
|
"scene_images",
|
|
845
|
+
"broll_asset",
|
|
846
|
+
"broll_mode",
|
|
772
847
|
"caption_lines",
|
|
773
848
|
"stub_image_url",
|
|
774
849
|
"stub_video_url",
|
|
@@ -925,11 +1000,11 @@
|
|
|
925
1000
|
"toolName": "gen_voice",
|
|
926
1001
|
"tier": "atomic",
|
|
927
1002
|
"category": "asset",
|
|
928
|
-
"title": "Text-to-Speech
|
|
929
|
-
"summary": "Text-to-speech (TTS): synthesize narration audio from text
|
|
1003
|
+
"title": "Text-to-Speech",
|
|
1004
|
+
"summary": "Text-to-speech (TTS): synthesize narration audio from text. The voice catalog spans several providers (MiniMax, 豆包/Volcengine, HeyGen, and the caller's own cloned voices); the provider is picked from the voice id automatically. Returns the persisted audio URL — no download needed.",
|
|
930
1005
|
"triggers": [
|
|
931
1006
|
"AI voice-over, TTS, text-to-speech, generate narration audio",
|
|
932
|
-
"Use
|
|
1007
|
+
"Use MiniMax / 豆包 (Doubao) / HeyGen voices for speech synthesis",
|
|
933
1008
|
"Read a piece of text aloud / produce an audio file from text"
|
|
934
1009
|
],
|
|
935
1010
|
"entry": {
|
|
@@ -951,15 +1026,57 @@
|
|
|
951
1026
|
},
|
|
952
1027
|
"voice_id": {
|
|
953
1028
|
"type": "string",
|
|
954
|
-
"description": "Voice id. Default 'Chinese (Mandarin)_Male_Announcer'. When unsure, call with list_voices=true first
|
|
1029
|
+
"description": "Voice id from list_voices (any provider; synthesis routes to the voice's own provider). Default 'Chinese (Mandarin)_Male_Announcer'. When unsure, call with list_voices=true first — do not invent ids, and do not declare a voice unavailable unless it is missing from the filtered list."
|
|
955
1030
|
},
|
|
956
1031
|
"speed": {
|
|
957
1032
|
"type": "number",
|
|
958
|
-
"description": "Speech rate, 0.5-2.0"
|
|
1033
|
+
"description": "Speech rate, 0.5-2.0. Some HeyGen cloned voices ignore it."
|
|
959
1034
|
},
|
|
960
1035
|
"list_voices": {
|
|
961
1036
|
"type": "boolean",
|
|
962
|
-
"description": "List
|
|
1037
|
+
"description": "List voices and exit (one page of up to 100, with provider / type / gender / language columns and the total). This queries the live voice service and is the authoritative catalog. The public catalog has a few hundred voices, so narrow it with provider / gender / language / name, or page through with page; mine=true lists the caller's own cloned voices instead."
|
|
1038
|
+
},
|
|
1039
|
+
"mine": {
|
|
1040
|
+
"type": "boolean",
|
|
1041
|
+
"description": "With list_voices=true: list the caller's own cloned voices instead of the public catalog (provider / voice_type do not apply)."
|
|
1042
|
+
},
|
|
1043
|
+
"provider": {
|
|
1044
|
+
"type": "string",
|
|
1045
|
+
"enum": [
|
|
1046
|
+
"minimax",
|
|
1047
|
+
"volcengine",
|
|
1048
|
+
"heygen"
|
|
1049
|
+
],
|
|
1050
|
+
"description": "With list_voices=true: only voices from this provider (volcengine = 豆包). Not needed for synthesis."
|
|
1051
|
+
},
|
|
1052
|
+
"voice_type": {
|
|
1053
|
+
"type": "string",
|
|
1054
|
+
"enum": [
|
|
1055
|
+
"system",
|
|
1056
|
+
"clone"
|
|
1057
|
+
],
|
|
1058
|
+
"description": "With list_voices=true: system (stock) or clone voices."
|
|
1059
|
+
},
|
|
1060
|
+
"gender": {
|
|
1061
|
+
"type": "string",
|
|
1062
|
+
"enum": [
|
|
1063
|
+
"male",
|
|
1064
|
+
"female"
|
|
1065
|
+
],
|
|
1066
|
+
"description": "With list_voices=true: filter by gender."
|
|
1067
|
+
},
|
|
1068
|
+
"language": {
|
|
1069
|
+
"type": "string",
|
|
1070
|
+
"description": "With list_voices=true: filter by language tag, exact tag as shown in the Language column (e.g. 中文-普通话, 中文-粤语, 英语, 日语)."
|
|
1071
|
+
},
|
|
1072
|
+
"name": {
|
|
1073
|
+
"type": "string",
|
|
1074
|
+
"description": "With list_voices=true: fuzzy-filter by voice name."
|
|
1075
|
+
},
|
|
1076
|
+
"page": {
|
|
1077
|
+
"type": "integer",
|
|
1078
|
+
"minimum": 1,
|
|
1079
|
+
"description": "With list_voices=true: page number (100 per page). Default 1."
|
|
963
1080
|
},
|
|
964
1081
|
"json_output": {
|
|
965
1082
|
"type": "boolean",
|
|
@@ -978,7 +1095,14 @@
|
|
|
978
1095
|
],
|
|
979
1096
|
"hidden": [
|
|
980
1097
|
"json_output",
|
|
981
|
-
"list_voices"
|
|
1098
|
+
"list_voices",
|
|
1099
|
+
"mine",
|
|
1100
|
+
"provider",
|
|
1101
|
+
"voice_type",
|
|
1102
|
+
"gender",
|
|
1103
|
+
"language",
|
|
1104
|
+
"name",
|
|
1105
|
+
"page"
|
|
982
1106
|
]
|
|
983
1107
|
}
|
|
984
1108
|
},
|
|
@@ -1104,6 +1228,10 @@
|
|
|
1104
1228
|
"minimum": 1,
|
|
1105
1229
|
"description": "Render job id (positive integer, e.g. 8). This is the integer N parsed from prepare_video_assets's stdout line '📦 render job jobId: N' (also tolerated: legacy zh '📦 渲染任务 jobId: N'). Never pass 0, a placeholder string, or descriptive text."
|
|
1106
1230
|
},
|
|
1231
|
+
"restyle": {
|
|
1232
|
+
"type": "string",
|
|
1233
|
+
"description": "Render the same video again in another visual style (a template_style id). Reuses the script, assets and timing, so nothing is regenerated. Only for templates with visual styles such as talking-head-studio."
|
|
1234
|
+
},
|
|
1107
1235
|
"save_job": {
|
|
1108
1236
|
"type": "boolean",
|
|
1109
1237
|
"description": "Persist the render Manifest back to the database under the same jobId (default true). Auto-degrades to false when PRIV_TOKEN is missing. Pass false to opt out explicitly."
|
|
@@ -1138,6 +1266,7 @@
|
|
|
1138
1266
|
"job_id"
|
|
1139
1267
|
],
|
|
1140
1268
|
"advanced": [
|
|
1269
|
+
"restyle",
|
|
1141
1270
|
"save_job",
|
|
1142
1271
|
"upload_title",
|
|
1143
1272
|
"no_upload",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remixmate/cli",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.10",
|
|
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",
|
|
@@ -19,7 +19,7 @@ triggers:
|
|
|
19
19
|
|
|
20
20
|
# Digital-Human Skill
|
|
21
21
|
|
|
22
|
-
Wraps ab-api's digital-human endpoints (the same ones the web studio uses), authenticated with the **Tianyan privateToken**.
|
|
22
|
+
Wraps ab-api's digital-human endpoints (the same ones the web studio uses), authenticated with the **Tianyan privateToken**. Providers are **HiFly** (default) and, when the capabilities catalog lists it, **HeyGen**: avatars are cloned from a video or photo (HeyGen: photo only), then driven by audio. Each avatar belongs to one provider and always renders there — you never pick the provider at generation time. HeyGen costs more per second (the curated list and the studio picker mark HeyGen avatars); prefer it only for non-Chinese speech or when the user asks. Generation is async — the handler submits the task and polls `/digital-human/video/status` until completion.
|
|
23
23
|
|
|
24
24
|
## Capabilities
|
|
25
25
|
|
|
@@ -57,6 +57,7 @@ remixmate gen-digital-human --list-avatars # curated public a
|
|
|
57
57
|
remixmate gen-digital-human --list-avatars --gender female # curated, filtered by gender
|
|
58
58
|
remixmate gen-digital-human --list-avatars --mine # your cloned avatars
|
|
59
59
|
remixmate gen-digital-human --list-avatars --all --name "教师" # full HiFly inventory (500+; always narrow with --name)
|
|
60
|
+
remixmate gen-digital-human --list-avatars --all --source heygen --name "Anna" # full HeyGen public inventory
|
|
60
61
|
```
|
|
61
62
|
|
|
62
63
|
The default list is the **curated public library**: avatars an admin picked, with a cover, scene tags (`education` / `medical` / `news` / `business` / `culture` / `outdoor` / `lifestyle` / `festival` / `ecommerce`) and usually a suggested voice. Prefer it — pick the avatar whose scenes match the script, and pass its `Voice` column as `--voice-id` unless the user chose a voice. Fall back to `--all` only when nothing curated fits; those entries have no tags or suggested voice.
|
|
@@ -94,6 +95,9 @@ remixmate gen-digital-human --clone --name "My presenter" --clone-url "https://e
|
|
|
94
95
|
# Portrait photo clone (charged per clone)
|
|
95
96
|
remixmate gen-digital-human --clone --clone-type image --name "My presenter" --clone-url "https://example.com/me.jpg"
|
|
96
97
|
|
|
98
|
+
# HeyGen photo avatar (photo only, jpg/png ≤ 32MB; more natural lip-sync for English and other languages)
|
|
99
|
+
remixmate gen-digital-human --clone --clone-type image --clone-provider heygen --name "My presenter" --clone-url "https://example.com/me.jpg"
|
|
100
|
+
|
|
97
101
|
# Submit without waiting, then poll
|
|
98
102
|
remixmate gen-digital-human --clone --wait false --name "My presenter" --clone-url "https://example.com/me.mp4"
|
|
99
103
|
remixmate gen-digital-human --clone-status --avatar-id dh_xxx
|
|
@@ -109,12 +113,16 @@ remixmate gen-digital-human --clone-status --avatar-id dh_xxx
|
|
|
109
113
|
| `--name` | list / clone | Fuzzy filter; with `--clone` the new avatar's name (required) | — |
|
|
110
114
|
| `--avatar-id` | generate / clone-status | Avatar id (required) | — |
|
|
111
115
|
| `-t` / `--text` | generate | Script (text-driven) | — |
|
|
112
|
-
| `--voice-id` | generate | Voice id (text-driven) |
|
|
116
|
+
| `--voice-id` | generate | Voice id (text-driven) | the avatar's own voice (curated suggestion / HeyGen twin's cloned voice), else the service default |
|
|
113
117
|
| `--voice-name` | generate | Voice display name (recordkeeping only) | none |
|
|
114
118
|
| `--audio-url` | generate | Audio URL (audio-driven) | none |
|
|
119
|
+
| `--video-url` | generate | Lip-sync an existing video instead of an avatar; needs `--audio-url` | none |
|
|
120
|
+
| `--lipsync-mode` | generate | `speed` or `precision` (about 4x the price) | `speed` |
|
|
115
121
|
| `--check-status` / `--generation-id` | status | Poll a generation job | — |
|
|
116
122
|
| `--clone` / `--clone-url` | clone | Clone mode and source URL (required) | — |
|
|
117
123
|
| `--clone-type` | clone | `video` / `image` | `video` |
|
|
124
|
+
| `--clone-provider` | clone | `hifly` / `heygen` (heygen needs `--clone-type image`) | `hifly` |
|
|
125
|
+
| `--source` | list with `--all` | Which provider's full inventory: `hifly` / `heygen` | `hifly` |
|
|
118
126
|
| `--thumbnail-url` | clone | Cover shown in lists | image itself / none |
|
|
119
127
|
| `--wait` | clone | `false` to return right after submission | wait |
|
|
120
128
|
| `--clone-status` | clone-status | Report clone status of `--avatar-id` | — |
|
|
@@ -29,7 +29,15 @@
|
|
|
29
29
|
},
|
|
30
30
|
"all": {
|
|
31
31
|
"type": "boolean",
|
|
32
|
-
"description": "With list_avatars=true: list
|
|
32
|
+
"description": "With list_avatars=true: list one provider's full public inventory (HiFly by default, 500+, mostly uncurated, no scene tags) instead of the curated public avatars. Only for when the curated set has nothing suitable."
|
|
33
|
+
},
|
|
34
|
+
"source": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"enum": [
|
|
37
|
+
"hifly",
|
|
38
|
+
"heygen"
|
|
39
|
+
],
|
|
40
|
+
"description": "With list_avatars=true and all=true: which provider's full public inventory to list. Defaults to hifly. heygen is only available when the capabilities catalog lists it."
|
|
33
41
|
},
|
|
34
42
|
"name": {
|
|
35
43
|
"type": "string",
|
|
@@ -49,7 +57,7 @@
|
|
|
49
57
|
},
|
|
50
58
|
"clone": {
|
|
51
59
|
"type": "boolean",
|
|
52
|
-
"description": "Clone mode: create a custom HiFly
|
|
60
|
+
"description": "Clone mode: create a custom avatar (HiFly by default, or HeyGen with clone_provider=heygen) from a video (free) or a portrait image (costs credits per clone, refunded automatically if cloning fails; see the pricing page). Requires clone_url and name. Waits until cloning finishes unless wait=false."
|
|
53
61
|
},
|
|
54
62
|
"clone_url": {
|
|
55
63
|
"type": "string",
|
|
@@ -63,6 +71,14 @@
|
|
|
63
71
|
],
|
|
64
72
|
"description": "With clone=true: source type. Defaults to video."
|
|
65
73
|
},
|
|
74
|
+
"clone_provider": {
|
|
75
|
+
"type": "string",
|
|
76
|
+
"enum": [
|
|
77
|
+
"hifly",
|
|
78
|
+
"heygen"
|
|
79
|
+
],
|
|
80
|
+
"description": "With clone=true: where to create the avatar. hifly (default) accepts a video or a photo; heygen accepts a photo only (clone_type=image, jpg/png ≤ 32MB) and gives more natural lip-sync for non-Chinese speech at a higher per-second price. Only use heygen when the capabilities catalog lists it."
|
|
81
|
+
},
|
|
66
82
|
"thumbnail_url": {
|
|
67
83
|
"type": "string",
|
|
68
84
|
"description": "With clone=true: optional cover image URL shown in avatar lists (image clones default to the image itself). The output aspect ratio always follows the cloned source; it cannot be chosen at generation time."
|
|
@@ -79,13 +95,22 @@
|
|
|
79
95
|
"type": "string",
|
|
80
96
|
"description": "Narration text (TTS mode, ≤ 10000 chars). Synthesized with voice_id, then drives the avatar; the speech must last at least 5 s. Ignored when audio_url is given."
|
|
81
97
|
},
|
|
98
|
+
"video_url": {
|
|
99
|
+
"type": "string",
|
|
100
|
+
"description": "Lip-sync mode: public http(s) URL of the video to re-voice. Needs audio_url. Replaces avatar_id."
|
|
101
|
+
},
|
|
102
|
+
"lipsync_mode": {
|
|
103
|
+
"type": "string",
|
|
104
|
+
"enum": ["speed", "precision"],
|
|
105
|
+
"description": "Lip-sync quality, default speed. precision costs about 4x as much."
|
|
106
|
+
},
|
|
82
107
|
"audio_url": {
|
|
83
108
|
"type": "string",
|
|
84
109
|
"description": "Audio-driven mode: public http(s) URL of mp3 / m4a / wav, 5 s – 30 min, ≤ 100 MB. Takes precedence over text / voice_id."
|
|
85
110
|
},
|
|
86
111
|
"voice_id": {
|
|
87
112
|
"type": "string",
|
|
88
|
-
"description": "Voice id (TTS mode). Shares the catalog with gen-voice (including cloned voices) — call gen_voice with list_voices=true to see available ids rather than inventing one."
|
|
113
|
+
"description": "Voice id (TTS mode). Optional: defaults to the avatar's own voice (a curated avatar's suggested voice, or a HeyGen digital twin's cloned voice), else the service default. Shares the catalog with gen-voice (any provider, including cloned voices) — call gen_voice with list_voices=true to see available ids rather than inventing one."
|
|
89
114
|
},
|
|
90
115
|
"voice_name": {
|
|
91
116
|
"type": "string",
|
|
@@ -111,7 +136,9 @@
|
|
|
111
136
|
"avatar_id",
|
|
112
137
|
"text",
|
|
113
138
|
"voice_id",
|
|
114
|
-
"audio_url"
|
|
139
|
+
"audio_url",
|
|
140
|
+
"video_url",
|
|
141
|
+
"lipsync_mode"
|
|
115
142
|
],
|
|
116
143
|
"advanced": [],
|
|
117
144
|
"hidden": [
|
|
@@ -120,11 +147,13 @@
|
|
|
120
147
|
"clone",
|
|
121
148
|
"clone_url",
|
|
122
149
|
"clone_type",
|
|
150
|
+
"clone_provider",
|
|
123
151
|
"thumbnail_url",
|
|
124
152
|
"wait",
|
|
125
153
|
"clone_status",
|
|
126
154
|
"mine",
|
|
127
155
|
"all",
|
|
156
|
+
"source",
|
|
128
157
|
"name",
|
|
129
158
|
"gender",
|
|
130
159
|
"check_status",
|
|
@@ -487,6 +487,12 @@ python3 <SkillDir>/scripts/gen_script.py \
|
|
|
487
487
|
| `--sequence-preset` | Style preset for templates declaring `capabilities.mediaBinding.options.preset`; one of that option's enum in the template's `customPayloadSchema` (`none` keeps the template defaults). Written next to the media list; the preset sets transitions, motion, text layout and pacing, and the scene duration follows. **Must** pass when the user picked a style preset on the replicate card (pass the id in parentheses, e.g. `风格预设: <名称>(<id>)` → `<id>`); otherwise the choice is silently dropped. Rejected on templates without that option. |
|
|
488
488
|
| `--sequence-fit` | Fill mode for templates declaring `capabilities.mediaBinding.options.fit`; one of that option's enum (`auto` keeps the template / preset default). Beats the preset's own fit. **Must** pass when the user picked it on the replicate card (`显示方式: <名称>(<id>)` → `<id>`). |
|
|
489
489
|
| `--narration` | `auto` (the template's `capabilities.needsNarration` decides), `on` or `off`. Templates where narration is optional default to none; **must** pass `on` when the user picked narration on the replicate card (`旁白: <名称>(narrated)`), otherwise the narration written later is dropped without an error. `on` adds the narration slot, a `gen-voice` asset and subtitles; on `fit-images` templates that declare `customPayload.minDurationSec` it also writes the media's natural length there, so a short narration does not compress the media (a longer one stretches the scene). |
|
|
490
|
+
| `--template-style` | Visual style for templates that ship several (`talking-head-studio`: `douyin-bold`, `news-anchor`, `live-ecommerce`, `minimal-talk`, `interview-serif`, `gold-quote`, `lecture`, `soft-glow`, `cinema-talk`). |
|
|
491
|
+
| `--presenter-video` | URL of the user's talking-head video, with `--source real-footage` (used as is, no cutting) or `--source lip-sync` (re-voiced with the script). |
|
|
492
|
+
| `--broll-asset` | Repeatable. URL of an image or video the user picked as b-roll from their asset library (replicate card field `空镜素材`). Works on every `--source`. Use the description on the asset line to anchor it to the sentence it matches with `atText`; if one fits nowhere, remove it and tell the user which. |
|
|
493
|
+
| `--broll-mode` | Scenes with no `--broll-asset`: `auto` (the visual style's default), `ai-image`, `ai-video` (at most 3 scenes, the rest stay images) or `none`. Pass the replicate card field `其余画面`. |
|
|
494
|
+
| `--lipsync-mode` | `speed` (default) or `precision` (about 4x the price), with `--source lip-sync`. |
|
|
495
|
+
| `--source` | Presenter source for templates that declare `capabilities.sources`: `digital-human`, `lip-sync` or `real-footage`. Omit to use the template's default. Writes `global.presenter.source` and `global.configLock`; capabilities, `llmHint` and the payload schema then follow that source. |
|
|
490
496
|
| `--background-asset` | URL of the user's background image or video (`.mp4/.mov/.webm/.m4v` → video). Added as existing asset `asset-background`; every scene's `customPayload.background` gets `preset: image` + `assetRef`, or `preset: video` + `videoAssetRef`. Conflicts with a different `--background-preset`; rejected when the template's background enum lacks that kind. | none |
|
|
491
497
|
| `--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 |
|
|
492
498
|
| `--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 |
|