@conceptcraft/mindframes 0.1.38 → 0.1.39
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/index.js +7 -7
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -469,7 +469,7 @@ import { DEFAULT_FPS } from './audio/constants';
|
|
|
469
469
|
|
|
470
470
|
### 9c. Spawn Per-Scene Builder Agents
|
|
471
471
|
|
|
472
|
-
**For EACH scene** from the parsed plan, spawn a \`video-scene-builder\` agent using the Task tool (model:
|
|
472
|
+
**For EACH scene** from the parsed plan, spawn a \`video-scene-builder\` agent using the Task tool (model: opus).
|
|
473
473
|
|
|
474
474
|
**Construct the Task prompt for each scene with these sections:**
|
|
475
475
|
|
|
@@ -2770,7 +2770,7 @@ ${i}`}function Io(n){return n.replace(/\bAskUserQuestion\b/g,"ask the user direc
|
|
|
2770
2770
|
name: video-scene-reviewer
|
|
2771
2771
|
description: Strict QA reviewer for a single Remotion scene. Compares code against the video plan checking ColorGrading, transitions, icons, logos, spring physics, idle states, VO-sync, and phase structure. Returns a pass/fail checklist.
|
|
2772
2772
|
tools: Read, Glob
|
|
2773
|
-
model:
|
|
2773
|
+
model: sonnet
|
|
2774
2774
|
permissionMode: plan
|
|
2775
2775
|
---
|
|
2776
2776
|
|
|
@@ -2867,7 +2867,7 @@ Priority fixes:
|
|
|
2867
2867
|
name: video-scene-builder
|
|
2868
2868
|
description: Builds a single Remotion video scene (.tsx) from a video plan section. Receives global context, scene spec, audio timing, and rhythm cues. Writes the scene file, spawns a reviewer, and iterates until 85%+ quality. One agent per scene.
|
|
2869
2869
|
tools: Read, Write, Edit, Glob, Grep, Task, Bash
|
|
2870
|
-
model:
|
|
2870
|
+
model: opus
|
|
2871
2871
|
skills:
|
|
2872
2872
|
- frontend-design
|
|
2873
2873
|
- remotion-best-practices
|
|
@@ -3050,7 +3050,7 @@ function AnimatedCamera() {
|
|
|
3050
3050
|
name: video-audio-director
|
|
3051
3051
|
description: Generates all audio assets (voiceover TTS, background music, sound effects) and builds the timing manifest that reconciles planned vs actual durations. One agent per video, runs before scene building.
|
|
3052
3052
|
tools: Read, Bash, Write
|
|
3053
|
-
model:
|
|
3053
|
+
model: opus
|
|
3054
3054
|
permissionMode: acceptEdits
|
|
3055
3055
|
---
|
|
3056
3056
|
|
|
@@ -3210,7 +3210,7 @@ This saves money and time \u2014 generation is expensive, downloading is free.
|
|
|
3210
3210
|
name: video-continuity-editor
|
|
3211
3211
|
description: Post-build cross-scene QA. Checks transition continuity, color grade flow, timing alignment between Main.tsx and manifest, import consistency, brand coherence, rhythm flow, and music cue alignment. Runs after all scenes are built.
|
|
3212
3212
|
tools: Read, Glob, Grep
|
|
3213
|
-
model:
|
|
3213
|
+
model: sonnet
|
|
3214
3214
|
permissionMode: plan
|
|
3215
3215
|
---
|
|
3216
3216
|
|
|
@@ -3321,7 +3321,7 @@ FIXES:
|
|
|
3321
3321
|
name: video-branding-director
|
|
3322
3322
|
description: Extracts brand identity (logos, colors, fonts) from a URL or existing profiles. Searches saved brands, extracts new ones via CLI, downloads assets locally, and writes branding.json. Returns success or failure. One agent per video, runs before brief/plan generation.
|
|
3323
3323
|
tools: Read, Bash, Write
|
|
3324
|
-
model:
|
|
3324
|
+
model: opus
|
|
3325
3325
|
permissionMode: acceptEdits
|
|
3326
3326
|
---
|
|
3327
3327
|
|
|
@@ -3742,7 +3742,7 @@ execSync("${e} video generate plan --project .", { stdio: "inherit" });
|
|
|
3742
3742
|
const args = process.argv.slice(2).join(" ");
|
|
3743
3743
|
execSync("${e} video export --project . " + args, { stdio: "inherit" });
|
|
3744
3744
|
`;await ae(j(r,"plan.ts"),a,"utf-8"),await ae(j(r,"export.ts"),s,"utf-8")}async function Zo(){return process.stdin.isTTY?null:new Promise(n=>{let t="",e=!1;process.stdin.setEncoding("utf-8"),process.stdin.on("data",o=>{t+=o}),process.stdin.on("end",()=>{e||(e=!0,n(t.trim()||null))}),process.stdin.on("error",()=>{e||(e=!0,n(null))}),setTimeout(()=>{!e&&!t&&(e=!0,n(null))},500)})}function fa(n){return n.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function ga(n){let t={shortTitle:"Video",elements:[],audio:[],text:[],scenes:[]},e=!0;return n.forEach(o=>{let r=o.startTime*1e3,i=o.endTime*1e3,a=o.durationInSeconds*1e3;t.scenes.push({name:o.name||`Scene ${o.id}`,text:o.text,startMs:r,endMs:i}),o.imagePath?(t.elements.push({startMs:r,endMs:i,imageUrl:o.imagePath,enterTransition:"blur",exitTransition:"blur",animations:[{type:"scale",from:e?1.3:1,to:e?1:1.3,startMs:0,endMs:a}]}),e=!e):o.videoPath&&t.elements.push({startMs:r,endMs:i,videoUrl:o.videoPath,enterTransition:"blur",exitTransition:"blur",animations:[]}),o.audioPath&&t.audio.push({startMs:r,endMs:i,audioUrl:o.audioPath}),o.timestamps&&t.text.push({startMs:r,endMs:i,text:o.text,position:"bottom",animations:[],timestamps:o.timestamps})}),t}async function It(n,t){if(n.startsWith("data:")){let r=n.match(/^data:[^;]+;base64,(.+)$/);if(!r)throw new Error("Invalid data URL format");let i=Buffer.from(r[1],"base64");await ae(t,i);return}let e=await fetch(n);if(!e.ok)throw new Error(`Failed to download: ${e.status}`);let o=await e.arrayBuffer();await ae(t,Buffer.from(o))}function ha(n){try{let o=new URL(n).pathname.split(".").pop()?.toLowerCase();if(o&&["jpg","jpeg","png","gif","webp"].includes(o))return o}catch{}return"jpg"}var ya=new se("assets").description("Generate assets (voiceover per scene, music, images, video)").option("-s, --script <text>","Narration script (legacy single-script mode)").option("--script-file <path>","Path to script file (legacy) or scenes JSON").option("--scenes <path>","Path to scenes.json (defaults to <project>/public/scenes.json)").option("-p, --project <dir>","Project directory (defaults to current directory; uses <dir>/public for output)").option("-t, --topic <text>","Topic for image search").option("-v, --voice <name>","TTS voice ID (ElevenLabs: Rachel, Josh, Adam; OpenAI: alloy, nova; Gemini: Kore, Puck)").option("-m, --music-prompt <text>","Music description").option("-n, --num-images <number>","Number of images to search/download","5").option("-o, --output <dir>","Output directory","./public").option("-f, --format <format>","Output format: human, json, quiet","human").action(async n=>{let t=n.format,e=t==="human"?Ze("Initializing...").start():null,o=n.project?Qe(process.cwd(),n.project):null;try{let r=await Zo(),i=null;if(n.scenes&&!n.scriptFile&&(n.scriptFile=n.scenes),o&&(n.output=j(o,"public"),n.scriptFile?n.scriptFile=Qe(o,n.scriptFile):!n.script&&!r&&(n.scriptFile=j(o,"public","scenes.json"))),r)try{let b=JSON.parse(r);b.scenes&&Array.isArray(b.scenes)&&(i=b)}catch{}if(!i&&n.scriptFile)try{let b=await Rt(n.scriptFile,"utf-8"),q=JSON.parse(b);q.scenes&&Array.isArray(q.scenes)&&(i=q)}catch{}let a=i?.voice||n.voice,s=i?.voiceId,c=i?.provider,d=i?.model,m=i?.voiceInstructions,C=i?.musicPrompt||n.musicPrompt||"uplifting background music, positive energy",h=i?.musicDuration,f=j(n.output,"audio"),w=j(n.output,"images"),$=j(n.output,"videos");e&&(e.text="Creating directories..."),await Be(f,{recursive:!0}),await Be(w,{recursive:!0}),await Be($,{recursive:!0});let k=0,y=[],R=0,T=[],g=[];if(i&&i.scenes.length>0){t==="human"&&(e?.stop(),p(`Processing ${i.scenes.length} scenes...`),e?.start());let b=i.scenes.map((X,L)=>{let re=X.script;return X.voiceStyle&&(re=`${X.voiceStyle} ${re}`),{text:re,id:`scene-${L}`}});e&&(e.text="Generating speech for all scenes...");let q=await Wn({texts:b,options:{provider:c,voice:a,voiceId:s,model:d,voiceSettings:i.voiceSettings,instructions:m}});k+=q.totalCost;let K=0;for(let X=0;X<i.scenes.length;X++){let L=i.scenes[X],re=fa(L.name),Z=q.results[X];e&&(e.text=`[${L.name}] Saving audio...`);let Et=j(f,`${re}.${Z.format}`);await ae(Et,Z.audioData);let we=Z.duration,cr=Math.round(we*_e),We={id:X+1,name:L.name,text:L.script,wordCount:L.script.split(/\s+/).length,startTime:K,endTime:K+we,durationInSeconds:we,durationInFrames:cr,audioPath:`audio/${re}.${Z.format}`,timestamps:Z.timestamps};if(L.imageQuery){e&&(e.text=`[${L.name}] Searching image...`);try{let W=await dt({query:L.imageQuery,options:{maxResults:1,size:"large",safeSearch:!0}}),Ye=W.data.results.flatMap(G=>G.results);if(k+=W.data.totalCost,Ye.length>0){let G=Ye[0],He=ha(G.url),me=`${re}.${He}`,Pt=j(w,me);await It(G.url,Pt),We.imagePath=`images/${me}`,T.push({path:`images/${me}`,url:G.url,width:G.width,height:G.height,query:L.imageQuery})}}catch(W){t==="human"&&(e?.stop(),I(`[${L.name}] Image search failed: ${W instanceof Error?W.message:"Unknown"}`),e?.start())}}if(L.videoQuery){e&&(e.text=`[${L.name}] Searching video...`);try{let W=await _t({query:L.videoQuery,options:{maxResults:1,license:"free"}}),Ye=W.data.results.flatMap(G=>G.results);if(k+=W.data.totalCost,Ye.length>0){let G=Ye[0],He=G.previewUrl||G.downloadUrl;if(He){let me=`${re}.mp4`,Pt=j($,me);await It(He,Pt),We.videoPath=`videos/${me}`,g.push({path:`videos/${me}`,url:He,width:G.width,height:G.height,duration:G.duration,query:L.videoQuery})}}}catch(W){t==="human"&&(e?.stop(),I(`[${L.name}] Video search failed: ${W instanceof Error?W.message:"Unknown"}`),e?.start())}}if(y.push(We),K+=we,R+=we,t==="human"){e?.stop();let W=[`audio: ${we.toFixed(1)}s`,We.imagePath?"image":null,We.videoPath?"video":null].filter(Boolean).join(", ");v(` ${L.name}: ${W}`),e?.start()}}}else{let b=n.script;if(n.scriptFile)try{b=await Rt(n.scriptFile,"utf-8")}catch(Z){e?.stop(),l(`Failed to read script file: ${Z instanceof Error?Z.message:"Unknown error"}`),process.exit(S.INVALID_INPUT)}(!b||b.trim().length===0)&&(e?.stop(),l("Provide scenes via stdin JSON, --scenes/--script-file with scenes JSON, or --script for legacy mode"),process.exit(S.INVALID_INPUT)),b=b.trim();let q=n.topic||b.split(".")[0].slice(0,50);e&&(e.text="Generating voiceover...");let K=await ct({text:b,options:{voice:a}}),X=j(f,`voiceover.${K.format}`);await ae(X,K.audioData),k+=K.cost,R=K.duration;let L=la(b);y=da(L,K.duration,_e,K.timestamps).map((Z,Et)=>({...Z,name:`Section${Et+1}`,audioPath:`audio/voiceover.${K.format}`})),t==="human"&&(e?.stop(),v(`Voiceover: ${X} (${K.duration.toFixed(1)}s)`),e?.start())}e&&(e.text="Creating timeline...");let A=ga(y),B=Math.max(A.audio.length>0?Math.max(...A.audio.map(b=>b.endMs)):0,A.text.length>0?Math.max(...A.text.map(b=>b.endMs)):0,A.elements.length>0?Math.max(...A.elements.map(b=>b.endMs)):0),z=B/1e3,oe=h?Math.min(300,Math.max(3,h)):Math.min(300,Math.ceil(z));console.log("[Music Generation] Requesting music:",{prompt:C,requestedDuration:oe,totalAudioDuration:R,actualVideoDuration:z,timelineDurationMs:B});let ve,Ge;e&&(e.text="Generating music and thumbnail...");let ir=`YouTube thumbnail style: BIG BOLD short headline text (like "$50K/MONTH"), dramatic engaging scene, arrows pointing to key elements, high contrast, vibrant colors, clean composition, 16:9. Video context: ${y.map(b=>b.text).join(" ")}`,At=[],ar=(async()=>{if(oe<3)return t==="human"&&(e?.stop(),I(`Video duration (${z.toFixed(1)}s) is too short for music generation (minimum 3s).`),e?.start()),null;try{let b=await lt({prompt:C,duration:oe});if(b.status!=="completed"&&b.status!=="failed"&&(b=await de(()=>ze(b.requestId),60,2e3)),b.status==="failed")throw new Error(b.error||"Unknown error");let q=j(f,"music.mp3");return b.audioUrl&&await It(b.audioUrl,q),{path:"audio/music.mp3",duration:b.duration||oe,prompt:C,cost:b.cost||0}}catch(b){return t==="human"&&(e?.stop(),I(`Music generation failed: ${b.message}`),e?.start()),null}})();At.push(ar);let sr=(async()=>{try{let b=await Jn({prompt:ir,options:{width:1280,height:720}});if(b.success&&b.data.url){let q=j(n.output,"thumbnail.jpg");return await It(b.data.url,q),k+=b.data.cost||0,q}return null}catch(b){return t==="human"&&(e?.stop(),I(`Thumbnail generation failed: ${b.message}`),e?.start()),null}})();At.push(sr);let[xe,hn]=await Promise.all(At);xe&&(ve=xe,k+=xe.cost||0,t==="human"&&(e?.stop(),v(`Music: ${j(f,"music.mp3")} (${xe.duration}s)`),xe.duration<z&&I(`Music duration (${xe.duration.toFixed(1)}s) is shorter than video duration (${z.toFixed(1)}s).`),e?.start())),hn&&(Ge=hn,t==="human"&&(e?.stop(),v(`Thumbnail: ${Ge}`),e?.start())),e&&(e.text="Writing manifest...");let yn=Math.round(z*_e),bn={music:ve,thumbnail:Ge?"thumbnail.jpg":void 0,images:T,videos:g,scenes:y,timeline:A,totalDurationInFrames:yn,fps:_e,totalCost:k,createdAt:new Date().toISOString()},Ot=j(n.output,"video-manifest.json");if(await ae(Ot,JSON.stringify(bn,null,2)),e?.stop(),t==="json"){let{totalCost:b,...q}=bn;V(q);return}if(t==="quiet"){console.log(Ot);return}console.log(),v("Video assets created successfully!"),console.log(),p(`Scenes: ${y.length} (${yn} frames at ${_e}fps)`);for(let b of y){let q=[b.audioPath?"audio":null,b.imagePath?"image":null,b.videoPath?"video":null].filter(Boolean).join(", ");p(` - ${b.name}: ${b.durationInSeconds.toFixed(1)}s [${q}]`)}p(`Music: ${ve?.path} (${ve?.duration}s)`),p(`Manifest: ${Ot}`),console.log(),p(`Total cost: $${k.toFixed(4)}`)}catch(r){e?.stop(),l(r instanceof Error?r.message:"Unknown error"),process.exit(S.GENERAL_ERROR)}}),ba=new se("find").description("Find stock video clips (supporting)").argument("<query>","Search query").option("-n, --max-results <count>","Maximum number of results","10").option("-o, --orientation <type>","Video orientation: landscape, portrait, square, any","any").option("-l, --license <type>","License type: free, premium, any","free").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(n,t)=>{let{maxResults:e,orientation:o,license:r,format:i}=t,a=i==="human"?Ze("Finding videos...").start():null;try{let s=await _t({query:n,options:{maxResults:parseInt(e,10),orientation:o,license:r}});a?.stop();let c=s.data.results.flatMap(d=>d.results);if(i==="json"){let{totalCost:d,...m}=s.data;V({...s,data:m});return}if(i==="quiet"){c.forEach(d=>{console.log(d.previewUrl||d.thumbnailUrl)});return}if(c.length===0){p("No videos found");return}v(`Found ${c.length} videos for "${n}"`),console.log(),c.forEach((d,m)=>{console.log(`[${m+1}] ${d.title}`),console.log(` URL: ${d.previewUrl||d.thumbnailUrl}`),console.log(` Duration: ${d.duration}s | Size: ${d.width}x${d.height}`),console.log(` Provider: ${d.provider}`),console.log()}),p(`Total cost: $${s.data.totalCost.toFixed(4)}`)}catch(s){a?.stop(),l(s instanceof Error?s.message:"Unknown error"),process.exit(S.GENERAL_ERROR)}}),va=new se("new").description("Create a new video project from a template").argument("<name>","Project directory name").option("-t, --template <repo>","GitHub repo (user/repo#commit)",aa).option("--type <type>","Video type: youtube (16:9), tiktok (9:16), or audiogram (1:1)","youtube").option("--no-install","Skip dependency install").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(n,t)=>{let e=t.format,o=e==="human"?Ze("Initializing video project...").start():null;try{let r=je();await Be(r,{recursive:!0});let i=Ct(n),a=t.type,s=await dn();if(s.includes(n)){let f=Ho(n,s);o?.stop(),l(`Project "${n}" already exists at: ${i}`),p(`Try: ${u.commands[0]} video generate new ${f}`),process.exit(S.INVALID_INPUT)}/^[a-zA-Z0-9_-]+\/[a-zA-Z0-9_.-]+(#[a-zA-Z0-9_.\/-]+)?$/.test(t.template)||(o?.stop(),l(`Invalid template format: "${t.template}". Expected format: owner/repo or owner/repo#branch`),process.exit(S.INVALID_INPUT)),["youtube","tiktok","audiogram"].includes(a)||(o?.stop(),l(`Invalid type "${a}". Use youtube, tiktok, or audiogram.`),process.exit(S.INVALID_INPUT)),o&&(o.text=`Cloning template from ${t.template}...`);let[d,m]=t.template.split("#"),C=m?`-b ${m}`:"";try{qe(`git clone ${C} --depth 1 https://github.com/${d}.git "${i}"`,{stdio:"pipe"}),await un(j(i,".git"),{recursive:!0,force:!0})}catch(f){o?.stop(),l(`Failed to clone template: ${f instanceof Error?f.message:"Unknown error"}`),process.exit(S.GENERAL_ERROR)}e==="human"&&(o?.stop(),v(`Template downloaded to ${n}/`),o?.start());let h;try{h=await ca(i)}catch{o?.stop(),l("template.manifest.json not found or invalid in template repo"),process.exit(S.INVALID_INPUT)}if(h.propsSchema&&h.propsSchema!=="video-plan.v1"&&(o?.stop(),l(`Template propsSchema must be video-plan.v1 (found ${h.propsSchema})`),process.exit(S.INVALID_INPUT)),o&&(o.text="Writing scaffold files..."),await pa(i,a,u.commands[0]),t.install){let f=Qo()?"bun":"npm";o&&(o.text=`Installing dependencies with ${f}...`),await new Promise((w,$)=>{let k=oa(f,["install"],{cwd:i,stdio:"pipe",shell:!0});k.on("close",y=>{y===0?w():$(new Error(`${f} install failed with code ${y}`))}),k.on("error",$)}),e==="human"&&(o?.stop(),v("Dependencies installed"),o?.start())}if(o?.stop(),e==="json"){V({name:n,path:i,template:t.template,type:a,installed:t.install});return}if(e==="quiet"){console.log(i);return}console.log(),v(`Video project "${n}" created successfully!`),p(`Location: ${i}`),a==="tiktok"?p("Format: TikTok/Reels/Shorts (1080x1920 @ 30fps)"):a==="audiogram"?p("Format: Audiogram (1080x1080 @ 30fps)"):p("Format: YouTube (1920x1080 @ 30fps)"),console.log(),p("Next steps:"),p(` cd ${i}`),t.install||p(` ${Qo()?"bun":"npm"} install`),p(" bun run dev # Preview in Remotion Studio"),p(` ${u.commands[0]} video generate assets --scenes public/scenes.json`),p(` ${u.commands[0]} video generate plan`)}catch(r){o?.stop(),l(r instanceof Error?r.message:"Unknown error"),process.exit(S.GENERAL_ERROR)}}),xa=new se("plan").description("Generate video plan from context (via stdin or --context)").option("-c, --context <text>","Context for plan generation").option("-o, --output <path>","Save plan to file").option("-b, --branding <path>","Branding JSON file to include").action(async n=>{let t=Ze("Generating video plan...").start();try{let e=n.context;if(!e){let a=await Zo();a&&(e=a)}e||(t.stop(),l("Provide context via stdin or --context flag"),process.exit(S.INVALID_INPUT));let o;if(n.branding)try{o=await Rt(n.branding,"utf-8")}catch{t.stop(),l(`Could not read branding file: ${n.branding}`),process.exit(S.INVALID_INPUT)}let r=await Xn({context:e,branding:o});t.stop(),r.success||(l(r.error||"Failed to generate plan"),process.exit(S.GENERAL_ERROR));let i=r.data;n.output?(await ae(n.output,i),v(`Plan saved to ${n.output}`)):console.log(i)}catch(e){t.stop(),l(e instanceof Error?e.message:"Unknown error"),process.exit(S.GENERAL_ERROR)}}),wa=new se("generate").description("Generate parts of a video project").addCommand(va).addCommand(ya).addCommand(xa),Sa=new se("projects").description("List all video projects").option("-f, --format <format>","Output format: human, json","human").action(async n=>{let t=await dn();if(n.format==="json"){V({projectsDir:je(),projects:t.map(e=>({name:e,path:Ct(e)}))});return}if(t.length===0){p("No video projects found"),p(`Projects are stored in: ${je()}`);return}console.log(`
|
|
3745
|
-
Video Projects:`),console.log("-".repeat(50));for(let e of t)console.log(` ${e}`),console.log(` ${Ct(e)}`);console.log()}),Ca=new se("inject").description("Inject thumbnail into video (first frame + file icon)").requiredOption("-i, --video <path>","Input video file").requiredOption("-t, --thumbnail <path>","Thumbnail image").option("-o, --output <path>","Output video (default: video-with-thumb.mp4)").option("-f, --format <format>","Output format: human, json, quiet","human").action(async n=>{let t=n.format,e=t==="human"?Ze("Injecting thumbnail...").start():null;try{let o=Qe(n.video),r=Qe(n.thumbnail),i=n.output?Qe(n.output):o.replace(/\.mp4$/,"-with-thumb.mp4");await mn(o),await mn(r);let a=ia;if(a)try{await mn(a)}catch{a=null}if(!a)try{a=qe("which ffmpeg",{encoding:"utf8"}).trim()}catch{throw new Error("ffmpeg not found. Install ffmpeg or ffmpeg-static.")}let s=ra(a,["-i",o],{encoding:"utf8"});if(s.error)throw new Error(`Failed to probe video: ${s.error.message}`);let c=(s.stderr??"").match(/(\d{2,5})x(\d{2,5})/);if(!c)throw new Error("Could not detect video dimensions");let[,d,m]=c;e&&(e.text="Creating thumbnail frame...");let C="/tmp/cc-thumb-frame.mp4";qe(`${a} -y -loop 1 -i "${r}" -vf "scale=${d}:${m}:force_original_aspect_ratio=decrease,pad=${d}:${m}:(ow-iw)/2:(oh-ih)/2,setsar=1" -t 0.033 -r 30 -c:v libx264 -pix_fmt yuv420p "${C}"`,{stdio:"pipe"}),e&&(e.text="Concatenating with video...");let h="/tmp/cc-concat-video.mp4";if(qe(`${a} -y -i "${C}" -i "${o}" -filter_complex "[0:v][1:v]concat=n=2:v=1:a=0[v]" -map "[v]" -map 1:a -c:v libx264 -preset ultrafast -crf 18 -c:a copy "${h}"`,{stdio:"pipe"}),e&&(e.text="Adding file icon thumbnail..."),qe(`${a} -y -i "${h}" -i "${r}" -map 0 -map 1 -c copy -c:v:1 png -disposition:v:1 attached_pic "${i}"`,{stdio:"pipe"}),await un(C,{force:!0}),await un(h,{force:!0}),e?.stop(),t==="json"){V({success:!0,output:i});return}if(t==="quiet"){console.log(i);return}v(`Thumbnail injected: ${i}`)}catch(o){e?.stop(),l(o instanceof Error?o.message:"Unknown error"),process.exit(S.GENERAL_ERROR)}}),ka=new se("thumbnail").description("Thumbnail operations").addCommand(Ca),er=new se("video").description("Video generation commands").addCommand(wa).addCommand(ba).addCommand(Sa).addCommand(ka).addCommand(Xo);import{Command as Ta}from"commander";import $a from"ora";import{writeFile as Ia}from"fs/promises";var tr=new Ta("scrape").description("Extract content from a URL").argument("<url>","URL to scrape").option("-o, --output <path>","Save content to file").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(n,t)=>{let e=t.format,o=e==="human"?$a("Scraping URL...").start():null;try{let r=await zn(n);o?.stop(),r.success||(l(r.error||"Failed to scrape URL"),process.exit(S.GENERAL_ERROR));let i=r.data;if(t.output&&(await Ia(t.output,i.content,"utf-8"),e==="human"&&v(`Content saved to: ${t.output}`)),e==="json"){let{cost:a,...s}=i;V(s);return}if(e==="quiet"){console.log(i.content);return}i.title&&(console.log(),console.log(`Title: ${i.title}`)),i.metadata?.description&&console.log(`Description: ${i.metadata.description}`),console.log(`URL: ${i.url}`),console.log(`Tokens: ~${i.metadata?.tokenUsage?.toLocaleString()||"unknown"}`),i.warning&&I(i.warning),t.output||(console.log(),console.log("--- Content ---"),console.log(i.content)),i.cost&&i.cost>0&&p(`Cost: $${i.cost.toFixed(6)}`)}catch(r){o?.stop(),l(r instanceof Error?r.message:"Unknown error"),process.exit(S.GENERAL_ERROR)}});import{Command as gn}from"commander";import pn from"ora";import{writeFile as nr,mkdir as Ra}from"fs/promises";import{dirname as Aa}from"path";function fn(n,t){if(t==="json"){let{cost:e,...o}=n;V(o);return}if(t==="quiet"){n.audioUrl?console.log(n.audioUrl):n.requestId?console.log(n.requestId):console.log(n.id);return}p(`Request ID: ${n.requestId||n.id}`),p(`Status: ${n.status}`),n.duration&&p(`Duration: ${n.duration}s`),n.audioUrl&&v(`Audio URL: ${n.audioUrl}`),n.cost!==void 0&&p(`Cost: $${n.cost.toFixed(4)}`),n.error&&l(`Error: ${n.error}`)}async function Oa(n,t){if(await Ra(Aa(t),{recursive:!0}),n.startsWith("data:")){let r=n.match(/^data:[^;]+;base64,(.+)$/);if(!r)throw new Error("Invalid data URL format");let i=Buffer.from(r[1],"base64");await nr(t,i);return}let e=await fetch(n);if(!e.ok)throw new Error(`Failed to download: ${e.status}`);let o=await e.arrayBuffer();await nr(t,Buffer.from(o))}var Ea=new gn("generate").description("Generate sound effects from a text prompt").requiredOption("-t, --text <text>","Sound effect description (e.g., 'thunder rumbling, rain on window')").option("-d, --duration <seconds>","Duration in seconds (0.5-30)").option("-p, --provider <provider>","Provider (elevenlabs)","elevenlabs").option("--prompt-influence <value>","How closely to follow prompt (0-1)","0.3").option("-l, --loop","Generate seamless looping audio",!1).option("--format <format>","Audio format: mp3, wav, ogg","mp3").option("-o, --output <path>","Output file path").option("--no-wait","Do not wait for completion").option("-f, --output-format <format>","Output format: human, json, quiet","human").action(async n=>{let t;n.duration&&(t=parseFloat(n.duration),(isNaN(t)||t<.5||t>30)&&(l("Duration must be between 0.5 and 30 seconds"),process.exit(S.INVALID_INPUT)));let e;n.promptInfluence&&(e=parseFloat(n.promptInfluence),(isNaN(e)||e<0||e>1)&&(l("Prompt influence must be between 0 and 1"),process.exit(S.INVALID_INPUT)));let o=n.outputFormat,r=o==="human"?pn("Generating sound effects...").start():null;try{let i=await Kn({text:n.text,duration:t,options:{provider:n.provider,promptInfluence:e,loop:n.loop,format:n.format}});if(!n.wait){r?.stop(),fn(i,o);return}let a=i;if(i.status!=="completed"&&i.status!=="failed"){let s=i.requestId||i.id;r&&(r.text=`Processing (ID: ${s})...`),a=await de(()=>qt(s),60,2e3)}if(r?.stop(),a.status==="failed"&&(l(a.error||"Sound effects generation failed"),process.exit(S.GENERAL_ERROR)),fn(a,o),n.output&&a.audioUrl){let s=o==="human"?pn("Downloading...").start():null;try{await Oa(a.audioUrl,n.output),s?.stop(),o==="human"&&v(`Saved to: ${n.output}`)}catch(c){s?.stop(),I(`Failed to download: ${c instanceof Error?c.message:"Unknown error"}`)}}}catch(i){r?.stop(),l(i instanceof Error?i.message:"Unknown error"),process.exit(S.GENERAL_ERROR)}}),Pa=new gn("status").description("Check status of a sound effects generation request").argument("<id>","Request ID").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(n,t)=>{let e=t.format==="human"?pn("Checking status...").start():null;try{let o=await qt(n);e?.stop(),fn(o,t.format)}catch(o){e?.stop(),l(o instanceof Error?o.message:"Unknown error"),process.exit(S.GENERAL_ERROR)}}),or=new gn("sfx").description("Sound effects generation commands").addCommand(Ea).addCommand(Pa);var Da="0.1.
|
|
3745
|
+
Video Projects:`),console.log("-".repeat(50));for(let e of t)console.log(` ${e}`),console.log(` ${Ct(e)}`);console.log()}),Ca=new se("inject").description("Inject thumbnail into video (first frame + file icon)").requiredOption("-i, --video <path>","Input video file").requiredOption("-t, --thumbnail <path>","Thumbnail image").option("-o, --output <path>","Output video (default: video-with-thumb.mp4)").option("-f, --format <format>","Output format: human, json, quiet","human").action(async n=>{let t=n.format,e=t==="human"?Ze("Injecting thumbnail...").start():null;try{let o=Qe(n.video),r=Qe(n.thumbnail),i=n.output?Qe(n.output):o.replace(/\.mp4$/,"-with-thumb.mp4");await mn(o),await mn(r);let a=ia;if(a)try{await mn(a)}catch{a=null}if(!a)try{a=qe("which ffmpeg",{encoding:"utf8"}).trim()}catch{throw new Error("ffmpeg not found. Install ffmpeg or ffmpeg-static.")}let s=ra(a,["-i",o],{encoding:"utf8"});if(s.error)throw new Error(`Failed to probe video: ${s.error.message}`);let c=(s.stderr??"").match(/(\d{2,5})x(\d{2,5})/);if(!c)throw new Error("Could not detect video dimensions");let[,d,m]=c;e&&(e.text="Creating thumbnail frame...");let C="/tmp/cc-thumb-frame.mp4";qe(`${a} -y -loop 1 -i "${r}" -vf "scale=${d}:${m}:force_original_aspect_ratio=decrease,pad=${d}:${m}:(ow-iw)/2:(oh-ih)/2,setsar=1" -t 0.033 -r 30 -c:v libx264 -pix_fmt yuv420p "${C}"`,{stdio:"pipe"}),e&&(e.text="Concatenating with video...");let h="/tmp/cc-concat-video.mp4";if(qe(`${a} -y -i "${C}" -i "${o}" -filter_complex "[0:v][1:v]concat=n=2:v=1:a=0[v]" -map "[v]" -map 1:a -c:v libx264 -preset ultrafast -crf 18 -c:a copy "${h}"`,{stdio:"pipe"}),e&&(e.text="Adding file icon thumbnail..."),qe(`${a} -y -i "${h}" -i "${r}" -map 0 -map 1 -c copy -c:v:1 png -disposition:v:1 attached_pic "${i}"`,{stdio:"pipe"}),await un(C,{force:!0}),await un(h,{force:!0}),e?.stop(),t==="json"){V({success:!0,output:i});return}if(t==="quiet"){console.log(i);return}v(`Thumbnail injected: ${i}`)}catch(o){e?.stop(),l(o instanceof Error?o.message:"Unknown error"),process.exit(S.GENERAL_ERROR)}}),ka=new se("thumbnail").description("Thumbnail operations").addCommand(Ca),er=new se("video").description("Video generation commands").addCommand(wa).addCommand(ba).addCommand(Sa).addCommand(ka).addCommand(Xo);import{Command as Ta}from"commander";import $a from"ora";import{writeFile as Ia}from"fs/promises";var tr=new Ta("scrape").description("Extract content from a URL").argument("<url>","URL to scrape").option("-o, --output <path>","Save content to file").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(n,t)=>{let e=t.format,o=e==="human"?$a("Scraping URL...").start():null;try{let r=await zn(n);o?.stop(),r.success||(l(r.error||"Failed to scrape URL"),process.exit(S.GENERAL_ERROR));let i=r.data;if(t.output&&(await Ia(t.output,i.content,"utf-8"),e==="human"&&v(`Content saved to: ${t.output}`)),e==="json"){let{cost:a,...s}=i;V(s);return}if(e==="quiet"){console.log(i.content);return}i.title&&(console.log(),console.log(`Title: ${i.title}`)),i.metadata?.description&&console.log(`Description: ${i.metadata.description}`),console.log(`URL: ${i.url}`),console.log(`Tokens: ~${i.metadata?.tokenUsage?.toLocaleString()||"unknown"}`),i.warning&&I(i.warning),t.output||(console.log(),console.log("--- Content ---"),console.log(i.content)),i.cost&&i.cost>0&&p(`Cost: $${i.cost.toFixed(6)}`)}catch(r){o?.stop(),l(r instanceof Error?r.message:"Unknown error"),process.exit(S.GENERAL_ERROR)}});import{Command as gn}from"commander";import pn from"ora";import{writeFile as nr,mkdir as Ra}from"fs/promises";import{dirname as Aa}from"path";function fn(n,t){if(t==="json"){let{cost:e,...o}=n;V(o);return}if(t==="quiet"){n.audioUrl?console.log(n.audioUrl):n.requestId?console.log(n.requestId):console.log(n.id);return}p(`Request ID: ${n.requestId||n.id}`),p(`Status: ${n.status}`),n.duration&&p(`Duration: ${n.duration}s`),n.audioUrl&&v(`Audio URL: ${n.audioUrl}`),n.cost!==void 0&&p(`Cost: $${n.cost.toFixed(4)}`),n.error&&l(`Error: ${n.error}`)}async function Oa(n,t){if(await Ra(Aa(t),{recursive:!0}),n.startsWith("data:")){let r=n.match(/^data:[^;]+;base64,(.+)$/);if(!r)throw new Error("Invalid data URL format");let i=Buffer.from(r[1],"base64");await nr(t,i);return}let e=await fetch(n);if(!e.ok)throw new Error(`Failed to download: ${e.status}`);let o=await e.arrayBuffer();await nr(t,Buffer.from(o))}var Ea=new gn("generate").description("Generate sound effects from a text prompt").requiredOption("-t, --text <text>","Sound effect description (e.g., 'thunder rumbling, rain on window')").option("-d, --duration <seconds>","Duration in seconds (0.5-30)").option("-p, --provider <provider>","Provider (elevenlabs)","elevenlabs").option("--prompt-influence <value>","How closely to follow prompt (0-1)","0.3").option("-l, --loop","Generate seamless looping audio",!1).option("--format <format>","Audio format: mp3, wav, ogg","mp3").option("-o, --output <path>","Output file path").option("--no-wait","Do not wait for completion").option("-f, --output-format <format>","Output format: human, json, quiet","human").action(async n=>{let t;n.duration&&(t=parseFloat(n.duration),(isNaN(t)||t<.5||t>30)&&(l("Duration must be between 0.5 and 30 seconds"),process.exit(S.INVALID_INPUT)));let e;n.promptInfluence&&(e=parseFloat(n.promptInfluence),(isNaN(e)||e<0||e>1)&&(l("Prompt influence must be between 0 and 1"),process.exit(S.INVALID_INPUT)));let o=n.outputFormat,r=o==="human"?pn("Generating sound effects...").start():null;try{let i=await Kn({text:n.text,duration:t,options:{provider:n.provider,promptInfluence:e,loop:n.loop,format:n.format}});if(!n.wait){r?.stop(),fn(i,o);return}let a=i;if(i.status!=="completed"&&i.status!=="failed"){let s=i.requestId||i.id;r&&(r.text=`Processing (ID: ${s})...`),a=await de(()=>qt(s),60,2e3)}if(r?.stop(),a.status==="failed"&&(l(a.error||"Sound effects generation failed"),process.exit(S.GENERAL_ERROR)),fn(a,o),n.output&&a.audioUrl){let s=o==="human"?pn("Downloading...").start():null;try{await Oa(a.audioUrl,n.output),s?.stop(),o==="human"&&v(`Saved to: ${n.output}`)}catch(c){s?.stop(),I(`Failed to download: ${c instanceof Error?c.message:"Unknown error"}`)}}}catch(i){r?.stop(),l(i instanceof Error?i.message:"Unknown error"),process.exit(S.GENERAL_ERROR)}}),Pa=new gn("status").description("Check status of a sound effects generation request").argument("<id>","Request ID").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(n,t)=>{let e=t.format==="human"?pn("Checking status...").start():null;try{let o=await qt(n);e?.stop(),fn(o,t.format)}catch(o){e?.stop(),l(o instanceof Error?o.message:"Unknown error"),process.exit(S.GENERAL_ERROR)}}),or=new gn("sfx").description("Sound effects generation commands").addCommand(Ea).addCommand(Pa);var Da="0.1.39",N=new Fa,ce=u.commands[0];N.name(ce).description(u.description).version(Da,"-v, --version","Show version number").option("--debug","Enable debug logging").option("--no-color","Disable colored output").configureOutput({outputError:(n,t)=>{t(Q.red(n))}});N.addCommand(io);N.addCommand(ao);N.addCommand(co);N.addCommand(po);N.addCommand(fo);N.addCommand(go);N.addCommand(ho);N.addCommand(bo);N.addCommand(xo);N.addCommand(So);N.addCommand(To);N.addCommand($o);N.addCommand(Me);N.addCommand(Vo);N.addCommand(qo);N.addCommand(Bo);N.addCommand(Wo);N.addCommand(er);N.addCommand(tr);N.addCommand(or);var rr=ko();rr.commands.length>0&&N.addCommand(rr);N.on("command:*",n=>{console.error(Q.red(`Error: Unknown command '${n[0]}'`)),console.error(),console.error(`Run '${ce} --help' to see available commands.`),process.exit(1)});N.addHelpText("after",`
|
|
3746
3746
|
${Q.bold("Examples:")}
|
|
3747
3747
|
${Q.gray("# Log in (opens browser)")}
|
|
3748
3748
|
$ ${ce} login
|