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 +17 -0
- package/package.json +1 -1
- package/src/renderer/encode.js +7 -1
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
package/src/renderer/encode.js
CHANGED
|
@@ -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
|
|