pellicule 0.0.3 → 0.0.4

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/bin/cli.js CHANGED
@@ -56,6 +56,7 @@ ${c.bold('OPTIONS')}
56
56
  ${c.info('-w, --width')} <pixels> Video width ${c.dim('(default: from component or 1920)')}
57
57
  ${c.info('-h, --height')} <pixels> Video height ${c.dim('(default: from component or 1080)')}
58
58
  ${c.info('-r, --range')} <start:end> Frame range for partial render ${c.dim('(e.g., 100:200)')}
59
+ ${c.info('-a, --audio')} <file> Audio file to include ${c.dim('(mp3, wav, aac, etc.)')}
59
60
  ${c.info('--help')} Show this help message
60
61
  ${c.info('--version')} Show version number
61
62
 
@@ -123,6 +124,7 @@ async function main() {
123
124
  width: { type: 'string', short: 'w' },
124
125
  height: { type: 'string', short: 'h' },
125
126
  range: { type: 'string', short: 'r' },
127
+ audio: { type: 'string', short: 'a' },
126
128
  help: { type: 'boolean' },
127
129
  version: { type: 'boolean' }
128
130
  }
@@ -158,6 +160,17 @@ async function main() {
158
160
  // Extract config from component (if defineVideoConfig is used)
159
161
  const componentConfig = extractVideoConfig(inputPath)
160
162
 
163
+ // Resolve audio file path (CLI flag takes precedence over component config)
164
+ let audioPath = null
165
+ if (values.audio) {
166
+ audioPath = resolve(values.audio)
167
+ if (!existsSync(audioPath)) fail(`Audio file not found: ${values.audio}`)
168
+ } else if (componentConfig?.audio) {
169
+ // Resolve component audio path relative to the component file
170
+ audioPath = resolve(dirname(inputPath), componentConfig.audio)
171
+ if (!existsSync(audioPath)) fail(`Audio file not found: ${componentConfig.audio}`)
172
+ }
173
+
161
174
  // Build CLI flags object (only include explicitly provided values)
162
175
  const cliFlags = {}
163
176
  if (values.duration !== undefined) cliFlags.duration = parseInt(values.duration, 10)
@@ -215,6 +228,9 @@ async function main() {
215
228
  if (isPartialRender) {
216
229
  console.log(` ${c.bold('Range')} ${c.highlight(`frames ${startFrame}-${endFrame - 1}`)} ${c.dim(`(${framesToRender} frames, ${partialSeconds}s)`)}`)
217
230
  }
231
+ if (audioPath) {
232
+ console.log(` ${c.bold('Audio')} ${c.info(basename(audioPath))}`)
233
+ }
218
234
  console.log()
219
235
 
220
236
  const startTime = Date.now()
@@ -234,6 +250,7 @@ async function main() {
234
250
  width,
235
251
  height,
236
252
  output: outputPath,
253
+ audio: audioPath,
237
254
  silent: true,
238
255
  onProgress: ({ frame, total, fps: currentFps }) => {
239
256
  // Clear line and print progress (stays on same line)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pellicule",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Deterministic video rendering with Vue",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -8,6 +8,7 @@ import path from 'path'
8
8
  * @param {string} options.framesDir - Directory containing frame-XXXXX.png files
9
9
  * @param {string} options.output - Output video path (default: './output.mp4')
10
10
  * @param {number} options.fps - Frames per second (default: 30)
11
+ * @param {string|null} options.audio - Audio file path to include (default: null)
11
12
  * @param {boolean} options.silent - Suppress console output (default: false)
12
13
  * @returns {Promise<string>} Path to the output video
13
14
  */
@@ -16,6 +17,7 @@ export function encodeVideo(options) {
16
17
  framesDir,
17
18
  output = './output.mp4',
18
19
  fps = 30,
20
+ audio = null,
19
21
  silent = false
20
22
  } = options
21
23
 
@@ -26,9 +28,11 @@ export function encodeVideo(options) {
26
28
  '-y', // Overwrite output
27
29
  '-framerate', String(fps),
28
30
  '-i', framePattern,
31
+ ...(audio ? ['-i', audio] : []),
29
32
  '-c:v', 'libx264',
30
33
  '-pix_fmt', 'yuv420p', // Compatibility
31
34
  '-preset', 'fast',
35
+ ...(audio ? ['-c:a', 'aac'] : []),
32
36
  output
33
37
  ]
34
38
 
@@ -72,13 +76,14 @@ export function encodeVideo(options) {
72
76
  * Full render pipeline: Vue component → frames → MP4
73
77
  *
74
78
  * @param {object} options - Same as renderVideo, plus output path
79
+ * @param {string|null} options.audio - Audio file path to include (default: null)
75
80
  * @param {boolean} options.silent - Suppress console output (default: false)
76
81
  * @returns {Promise<string>} Path to the output video
77
82
  */
78
83
  export async function renderToMp4(options) {
79
84
  const { renderVideo } = await import('./render.js')
80
85
 
81
- const { output = './output.mp4', silent = false, ...renderOptions } = options
86
+ const { output = './output.mp4', audio = null, silent = false, ...renderOptions } = options
82
87
 
83
88
  // Step 1: Render frames (stored in .pellicule/frames)
84
89
  const { framesDir, cleanup } = await renderVideo({ ...renderOptions, silent })
@@ -89,6 +94,7 @@ export async function renderToMp4(options) {
89
94
  framesDir,
90
95
  output,
91
96
  fps: renderOptions.fps || 30,
97
+ audio,
92
98
  silent
93
99
  })
94
100