diff --git a/.env.example b/.env.example index dcf63bc..b4e4cce 100644 --- a/.env.example +++ b/.env.example @@ -1,11 +1,20 @@ -# OpenAI Configuration +# Anthropic / Claude (pipeline brain: feature detection + script writing) +# When set, the pipeline runs the "smart" path; unset falls back to heuristics. +ANTHROPIC_API_KEY=your_anthropic_api_key_here +ANTHROPIC_MODEL=claude-opus-4-8 + +# OpenAI Configuration (legacy CLI path) OPENAI_API_KEY=your_openai_api_key_here OPENAI_MODEL=gpt-4-turbo-preview -# ElevenLabs Configuration +# ElevenLabs Configuration (voiceover synthesis) ELEVENLABS_API_KEY=your_elevenlabs_api_key_here ELEVENLABS_VOICE_ID=your_preferred_voice_id_here +# Motion graphics: set to 1 to use the Remotion backend (requires the +# optional @remotion/* deps + graphics/ project). Default uses the ffmpeg backend. +MKDEMO_REMOTION=0 + # Puppeteer Configuration PUPPETEER_HEADLESS=true PUPPETEER_VIEWPORT_WIDTH=1920 diff --git a/package.json b/package.json index 211e80d..44d40fd 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "start": "node apps/web/server.js", "cli": "node src/index.js", "setup": "node scripts/setup.js", + "pipeline": "node scripts/run-pipeline.js", "test": "NODE_OPTIONS='--loader=./node_modules/mocha/lib/nodejs/esm-utils.js' mocha test/**/*.test.js --recursive", "test:pipeline": "NODE_OPTIONS='--loader=./node_modules/mocha/lib/nodejs/esm-utils.js' mocha test/pipeline/**/*.test.js --recursive", "test:watch": "NODE_OPTIONS='--loader=./node_modules/mocha/lib/nodejs/esm-utils.js' mocha test/**/*.test.js --recursive --watch", diff --git a/scripts/run-pipeline.js b/scripts/run-pipeline.js new file mode 100644 index 0000000..9fcff13 --- /dev/null +++ b/scripts/run-pipeline.js @@ -0,0 +1,55 @@ +#!/usr/bin/env node +import 'dotenv/config'; +import { randomUUID } from 'node:crypto'; +import { runPipeline } from '../packages/core/index.js'; + +/** + * Local CLI runner for the makedemo pipeline brain — useful for testing the + * full crawl -> Claude features -> record -> script -> voiceover -> assemble + * flow without the web shell. The web app drives runPipeline(job, emit) the + * same way. + * + * node scripts/run-pipeline.js --url https://example.com \ + * --clips ./a.mp4,./b.mp4 --song ./suno.mp3 --max-features 5 + */ +function parseArgs(argv) { + const out = {}; + for (let i = 0; i < argv.length; i++) { + const a = argv[i]; + if (a.startsWith('--')) { + const key = a.slice(2); + const val = argv[i + 1] && !argv[i + 1].startsWith('--') ? argv[++i] : 'true'; + out[key] = val; + } + } + return out; +} + +const args = parseArgs(process.argv.slice(2)); +if (!args.url) { + console.error('Usage: node scripts/run-pipeline.js --url [--user --password

] [--clips a.mp4,b.mp4] [--song suno.mp3] [--max-features 5] [--voice ]'); + process.exit(1); +} + +const job = { + id: randomUUID().slice(0, 8), + url: args.url, + credentials: args.user && args.password ? { user: args.user, password: args.password } : null, + maxFeatures: args['max-features'] ? Number(args['max-features']) : 5, + voice: args.voice || null, + clips: args.clips ? args.clips.split(',').map((s) => s.trim()).filter(Boolean) : [], + song: args.song || null, +}; + +const emit = (type, data) => { + if (type === 'log') console.log(` ${data.msg}`); + else if (type === 'stage') console.log(`[${data.stage}] ${data.status}${data.step ? ` ${data.step}/${data.total}` : ''}`); + else if (type === 'done') console.log(`\nāœ… Done: output/${job.id}/${data.video}`); + else if (type === 'error') console.error(`āŒ ${data.message}`); +}; + +console.log(`Job ${job.id} → ${job.url}`); +runPipeline(job, emit).catch((err) => { + console.error('Pipeline failed:', err); + process.exit(1); +});