@longzai-intelligence-music-video/cli 0.0.1
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.d.ts +1 -0
- package/dist/index.js +7 -0
- package/dist/rolldown-runtime-BqT_7tdF.js +1 -0
- package/package.json +40 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "./rolldown-runtime-BqT_7tdF.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import"./rolldown-runtime-BqT_7tdF.js";import{createCli as e,defineCommand as t}from"@longzai-intelligence-cli/core";import{clackInteraction as n,picocolorsColor as r}from"@longzai-intelligence-cli/standard";import*as i from"zod";import{CONFIG_FILENAME as a,buildRenderProps as o,failure as s,listProjects as c,loadProject as l,parseMidiTimeline as u,probeAudioDurationSec as d,resolveRuntimeEnv as f,runVerify as p,scaffoldConfigText as m,stageProjectMedia as h,success as g}from"@longzai-intelligence-music-video/core";import{existsSync as _,mkdirSync as v,writeFileSync as y}from"node:fs";import{dirname as b,join as x,resolve as S}from"node:path";import{discoverTemplates as C,listTemplates as w,loadTemplate as T,validateTemplateParams as E,writeRenderEntry as D}from"@longzai-intelligence-music-video/templates";import{bundle as O}from"@remotion/bundler";import{renderMedia as k,selectComposition as A}from"@remotion/renderer";import{fileURLToPath as j}from"node:url";function M(e,t={}){let n=x(e,a);return _(n)?s({message:`配置文件已存在:${n}(不覆盖;如需重建请先删除)`}):(y(n,m({dataRoot:t.dataRoot})),g({path:n}))}async function N(e){let t=await f(e);return t.ok?g({resolution:{channel:t.value.channel,...t.value.configPath===void 0?{}:{configPath:t.value.configPath},env:{dataRoot:t.value.env.dataRoot}}}):s({message:t.error.message})}function P(e){let t=x(e,a);return{configPath:t,configExists:_(t),envPath:x(e,`.env`),envExists:_(x(e,`.env`))}}async function F(e){let t=await f(e);if(!t.ok)return s({message:t.error.message});let n=c(t.value.env.dataRoot);return g({dataRoot:t.value.env.dataRoot,entries:n.map(e=>e.ok?{id:e.value.id,title:e.value.frontmatter.title,template:e.value.frontmatter.template,ok:!0}:{id:`(解析失败)`,title:`-`,template:`-`,ok:!1,error:e.error.message})})}async function I(e,t){let n=await f(t);if(!n.ok)return s({message:n.error.message});let r=await p(n.value.env.dataRoot,e);return r.ok?g({dataRoot:n.value.env.dataRoot,reports:r.value.reports.map(e=>({projectId:e.projectId,issues:e.issues}))}):s({message:r.error.message})}function L(){let e=j(new URL(`.`,import.meta.url)),t=S(e,`..`,`..`,`remotion`);return{appDir:t,entryPoint:x(t,`.generated`,`entry.tsx`),publicDir:x(t,`public`),stagingRoot:x(t,`public`,`.staging`)}}const R={gl:`angle`};function z(){let e=process.env.LZI_MUSIC_VIDEO_BROWSER_EXECUTABLE;return typeof e==`string`&&e.length>0?e:void 0}async function B(e,t){let n=await f(t);if(!n.ok)return s({message:n.error.message});let r=l(n.value.env.dataRoot,e);if(!r.ok)return s({message:r.error.message});let i=r.value,a=await p(n.value.env.dataRoot,e);if(!a.ok)return s({message:a.error.message});if(!a.value.ok){let e=a.value.reports.flatMap(e=>e.issues.filter(e=>e.severity===`error`)).map(e=>e.message);return s({message:`体检未通过(拒绝渲染):\n${e.map(e=>`- ${e}`).join(`
|
|
2
|
+
`)}`})}let c=a.value.reports.flatMap(e=>e.issues.filter(e=>e.severity===`warning`)),m=await T(n.value.env.dataRoot,i.frontmatter.template);if(!m.ok)return s({message:m.message});let _=E(m.template,i.frontmatter.templateParams);if(!_.ok)return s({message:_.message});if(i.midiFile===void 0)return s({message:`缺少 MIDI 乐谱(score/*.mid)`});let v=await u(i.midiFile.absPath,i.frontmatter.tracks);if(!v.ok)return s({message:v.error.message});let y=null;if(i.audioFile!==void 0){let e=await d(i.audioFile.absPath);if(!e.ok)return s({message:e.error.message});y=e.value}let b=L(),x=h({dataRoot:n.value.env.dataRoot,project:i,assetParams:m.template.assetParams,stagingRoot:b.stagingRoot});if(!x.ok)return s({message:x.error.message});let S=o({project:i,validatedTemplateParams:_.params,rawTimeline:v.value,audioDurationSec:y,staged:x.value});return g({project:i,props:S,staged:x.value,warnings:c})}async function V(e){let t=await B(e.projectId,e.configPath);if(!t.ok)return s(t.error);let n=L(),r=await f(e.configPath);if(!r.ok)return s({message:r.error.message});D({templates:C(r.value.env.dataRoot),outputPath:n.entryPoint});let i=await O({entryPoint:n.entryPoint}),a=z(),o=await A({serveUrl:i,id:t.value.project.frontmatter.template,inputProps:t.value.props,chromiumOptions:R,...a===void 0?{}:{browserExecutable:a}}),c=e.out??x(t.value.project.dir,`renders`,`${t.value.project.frontmatter.template}.mp4`);v(b(c),{recursive:!0});let l=t=>{e.onProgress?.(t.progress)};return await k({composition:o,serveUrl:i,codec:e.codec??`h264`,inputProps:t.value.props,outputLocation:c,scale:e.scale,chromiumOptions:R,...a===void 0?{}:{browserExecutable:a},onProgress:l}),g({outputLocation:c,durationInFrames:t.value.props.durationInFrames,warnings:t.value.warnings})}async function H(e,t){let n=await B(e,t);if(!n.ok)return s(n.error);let r=await f(t);if(!r.ok)return s({message:r.error.message});let i=L();D({templates:C(r.value.env.dataRoot),outputPath:i.entryPoint});let a=x(n.value.staged.stagingDir,`props.json`);y(a,`${JSON.stringify(n.value.props,null,2)}\n`);let o=await Bun.spawn({cmd:[`bunx`,`remotion`,`studio`,i.entryPoint,`--props`,a],cwd:i.appDir,stdout:`inherit`,stderr:`inherit`,stdin:`inherit`}).exited;return g({exitedCode:o,propsPath:a})}async function U(e){let t=await f(e);return t.ok?{ok:!0,value:{dataRoot:t.value.env.dataRoot,entries:await w(t.value.env.dataRoot)}}:{ok:!1,error:{message:t.error.message}}}const W=e({name:`lzi-music-video`,description:`模板化音乐视频合成工具(数据仓驱动:project 管理/render 出片/studio 预览;双仓架构裁定见仓 ADR 0001)`,interaction:n,color:r}),G=i.enum([`text`,`json`]).default(`text`);function K(e){process.stdout.write(`${JSON.stringify(e,null,2)}\n`)}const q={flags:`--format <kind>`,description:`输出格式 text|json`,defaultValue:`text`},J={flags:`--config <path>`,description:`配置文件路径(缺省自 cwd 向上查找 lzi-music-video-data.config.ts;未找到回落 .env)`};t(W,{name:`project list`,description:`列出数据仓内全部音乐项目(projects/<song-id>/ 扫描)`,options:[J,q],schema:i.object({config:i.string().optional(),format:G}),action:async e=>{let t=await F(e.config);if(e.format===`json`){K(t);return}if(!t.ok){process.stderr.write(`${t.error.message}\n`),process.exitCode=1;return}if(process.stdout.write(`数据根:${t.value.dataRoot}\n`),t.value.entries.length===0){process.stdout.write(`(空:无音乐项目)
|
|
3
|
+
`);return}for(let e of t.value.entries)process.stdout.write(e.ok?`${e.id}:${e.title}(${e.template})\n`:`[失败] ${e.error}\n`)}}),t(W,{name:`project verify`,description:`一致性体检:结构/媒体文件/模板参数/MIDI 通道覆盖/时长对齐(缺省全量项目)`,arguments:[{signature:`[projectId]`,description:`项目 id(缺省全量体检)`}],options:[J,q],schema:i.object({projectId:i.string().optional(),config:i.string().optional(),format:G}),action:async(e,t)=>{let n=await I(t[0],e.config);if(e.format===`json`){K(n);return}if(!n.ok){process.stderr.write(`${n.error.message}\n`),process.exitCode=1;return}process.stdout.write(`数据根:${n.value.dataRoot}\n`);for(let e of n.value.reports){let t=e.issues.filter(e=>e.severity===`error`).length;process.stdout.write(`${e.projectId}:${t===0?`通过`:`${t} 项错误`}\n`);for(let t of e.issues)process.stdout.write(` [${t.severity}] ${t.message}\n`)}}}),t(W,{name:`render`,description:`渲染出片(默认输出 <项目目录>/renders/<template>.mp4——产物内聚项目文件夹;首次渲染自动下载 Chrome Headless Shell)`,arguments:[{signature:`<projectId>`,description:`项目 id`}],options:[{flags:`--out <path>`,description:`输出路径覆盖(缺省落位项目内 renders/)`},{flags:`--scale <n>`,description:`分辨率缩放(缺省 1;0.5 = 半分辨率快速预览)`},{flags:`--codec <kind>`,description:`编码 h264|vp8|vp9(缺省 h264)`},J,q],schema:i.object({out:i.string().optional(),scale:i.coerce.number().min(.1).max(4).optional(),codec:i.enum([`h264`,`vp8`,`vp9`]).optional(),config:i.string().optional(),format:G}),action:async(e,t)=>{let n=t[0];if(n===void 0){process.stderr.write(`缺少项目 id:lzi-music-video render <projectId>
|
|
4
|
+
`),process.exitCode=1;return}let r=await V({projectId:n,configPath:e.config,out:e.out,scale:e.scale,codec:e.codec,onProgress:e=>{process.stdout.write(`\r渲染进度:${Math.round(e)}%`)}});if(process.stdout.write(`
|
|
5
|
+
`),e.format===`json`){K(r);return}if(!r.ok){process.stderr.write(`${r.error.message}\n`),process.exitCode=1;return}process.stdout.write(`已渲染:${r.value.outputLocation}(${r.value.durationInFrames} 帧)\n`);for(let e of r.value.warnings)process.stdout.write(` [warning] ${e.message}\n`)}}),t(W,{name:`studio`,description:`打开 Remotion Studio 预览项目(staging + props 与渲染同源,所见即所得)`,arguments:[{signature:`<projectId>`,description:`项目 id`}],options:[J],schema:i.object({config:i.string().optional()}),action:async(e,t)=>{let n=t[0];if(n===void 0){process.stderr.write(`缺少项目 id:lzi-music-video studio <projectId>
|
|
6
|
+
`),process.exitCode=1;return}let r=await H(n,e.config);if(!r.ok){process.stderr.write(`${r.error.message}\n`),process.exitCode=1;return}if(r.value.exitedCode!==0){process.stderr.write(`remotion studio 退出码:${r.value.exitedCode}\n`),process.exitCode=1;return}}}),t(W,{name:`template list`,description:`列出数据根内发现的全部模板(templates/<id>/ 装载;id = Composition id,project.md template 取值域)`,options:[J,q],schema:i.object({config:i.string().optional(),format:G}),action:async e=>{let t=await U(e.config);if(e.format===`json`){K(t);return}if(!t.ok){process.stderr.write(`${t.error.message}\n`),process.exitCode=1;return}if(process.stdout.write(`数据根:${t.value.dataRoot}\n`),t.value.entries.length===0){process.stdout.write(`(空:数据根无模板)
|
|
7
|
+
`);return}for(let e of t.value.entries)e.ok?process.stdout.write(`${e.entry.id}:${e.entry.label}\n ${e.entry.description}\n`):process.stdout.write(`[装载失败] ${e.id}:${e.message}\n`)}}),t(W,{name:`config init`,description:`生成 lzi-music-video-data.config.ts(数据根就地配置入口;已存在则提示,不覆盖)`,options:[{flags:`--data-root <path>`,description:`数据根(缺省 '.',即就地配置:在数据根目录执行时自指)`}],schema:i.object({dataRoot:i.string().optional()}),action:async e=>{let t=M(process.cwd(),{dataRoot:e.dataRoot});if(!t.ok){process.stderr.write(`${t.error.message}\n`),process.exitCode=1;return}process.stdout.write(`已生成:${t.value.path}\n`)}}),t(W,{name:`config show`,description:`展示生效配置(配置文件优先,回落 .env;zod 校验 + 绝对路径)`,options:[J,q],schema:i.object({config:i.string().optional(),format:G}),action:async e=>{let t=await N(e.config);if(e.format===`json`){K(t);return}if(!t.ok){process.stderr.write(`${t.error.message}\n`),process.exitCode=1;return}process.stdout.write(`通道:${t.value.resolution.channel}${t.value.resolution.configPath===void 0?``:`(${t.value.resolution.configPath})`}\ndataRoot=${t.value.resolution.env.dataRoot}\n`)}}),t(W,{name:`config path`,description:`打印配置文件与 .env 双通道位置`,schema:i.object({}),action:async()=>{let e=P(process.cwd());process.stdout.write(`${e.configPath}${e.configExists?``:`(不存在,可先 config init)`}\n${e.envPath}${e.envExists?``:`(不存在)`}\n`)}}),await W.parseAsync(process.argv);export{};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"node:module";export{};
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@longzai-intelligence-music-video/cli",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Longzai Intelligence Music Video CLI - 音乐视频合成命令行工具(project/render/studio/template/config 命令族)",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"bin": {
|
|
7
|
+
"lzi-music-video": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"type": "module",
|
|
13
|
+
"scripts": {
|
|
14
|
+
"dev": "bun run src/index.ts",
|
|
15
|
+
"build": "lzi-builder",
|
|
16
|
+
"build:prod": "NODE_ENV=production bun run build",
|
|
17
|
+
"start": "node dist/index.js",
|
|
18
|
+
"prepublishOnly": "bun run build:prod",
|
|
19
|
+
"typecheck": "lzi-tsgo typecheck tsconfig.json",
|
|
20
|
+
"lint": "oxlint && oxfmt --check",
|
|
21
|
+
"lint:fix": "oxlint --fix && oxfmt",
|
|
22
|
+
"test": "lzi-bun-cli test",
|
|
23
|
+
"test:watch": "lzi-bun-cli test --watch",
|
|
24
|
+
"clean": "lzi-dev-cli clean"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@longzai-intelligence-cli/core": "^0.0.10",
|
|
28
|
+
"@longzai-intelligence-cli/standard": "^0.0.10",
|
|
29
|
+
"@longzai-intelligence-music-video/core": "0.0.1",
|
|
30
|
+
"@longzai-intelligence-music-video/templates": "0.0.1",
|
|
31
|
+
"@remotion/bundler": "^4.0.0",
|
|
32
|
+
"@remotion/renderer": "^4.0.0",
|
|
33
|
+
"zod": "^4.4.3"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@longzai-intelligence-builder/esm": "^0.2.13",
|
|
37
|
+
"@longzai-intelligence-builder/utils": "^0.2.13",
|
|
38
|
+
"@types/bun": "^1.3.14"
|
|
39
|
+
}
|
|
40
|
+
}
|