@editframe/cli 0.36.0-beta → 0.36.1-beta
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/VERSION.js +1 -1
- package/dist/VERSION.js.map +1 -1
- package/dist/commands/render.js +2 -2
- package/dist/commands/render.js.map +1 -1
- package/dist/commands/transcribe.js +37 -0
- package/dist/commands/transcribe.js.map +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/VERSION.js
CHANGED
package/dist/VERSION.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VERSION.js","names":[],"sources":["../src/VERSION.ts"],"sourcesContent":["export const VERSION = \"0.36.
|
|
1
|
+
{"version":3,"file":"VERSION.js","names":[],"sources":["../src/VERSION.ts"],"sourcesContent":["export const VERSION = \"0.36.1-beta\";\n"],"mappings":";AAAA,MAAa,UAAU"}
|
package/dist/commands/render.js
CHANGED
|
@@ -122,10 +122,10 @@ program.command("render [directory]").description("Render a video composition lo
|
|
|
122
122
|
throw error;
|
|
123
123
|
}
|
|
124
124
|
outputStream.end();
|
|
125
|
-
await new Promise((resolve, reject) => {
|
|
125
|
+
await new Promise((resolve$1, reject) => {
|
|
126
126
|
outputStream.on("finish", () => {
|
|
127
127
|
log(`Render complete: ${chunkCount} chunks, ${totalBytes} bytes written to ${outputPath}`);
|
|
128
|
-
resolve();
|
|
128
|
+
resolve$1();
|
|
129
129
|
});
|
|
130
130
|
outputStream.on("error", reject);
|
|
131
131
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.js","names":["renderData: Record<string, unknown> | undefined","renderUrl: string","viteServer: SpawnedViteServer | null","lastProgress: {\n currentFrame: number;\n totalFrames: number;\n renderedMs: number;\n totalDurationMs: number;\n elapsedMs: number;\n speedMultiplier: number;\n } | null","renderOptions: any"],"sources":["../../src/commands/render.ts"],"sourcesContent":["import { readFile, writeFile } from \"node:fs/promises\";\nimport { createWriteStream } from \"node:fs\";\nimport path from \"node:path\";\nimport { program } from \"commander\";\nimport debug from \"debug\";\nimport ora from \"ora\";\nimport type { Page } from \"playwright\";\nimport { launchBrowserAndWaitForSDK } from \"../utils/launchBrowserAndWaitForSDK.js\";\nimport { spawnViteServer, type SpawnedViteServer } from \"../utils/spawnViteServer.js\";\nimport { StreamTargetChunk } from \"mediabunny\";\nimport { withProfiling } from \"../utils/profileRender.js\";\n\nconst log = debug(\"ef:cli:render\");\n\n/**\n * Format milliseconds as MM:SS or HH:MM:SS\n */\nfunction formatTime(ms: number): string {\n const totalSeconds = Math.floor(ms / 1000);\n const hours = Math.floor(totalSeconds / 3600);\n const minutes = Math.floor((totalSeconds % 3600) / 60);\n const seconds = totalSeconds % 60;\n\n if (hours > 0) {\n return `${hours}:${minutes.toString().padStart(2, \"0\")}:${seconds.toString().padStart(2, \"0\")}`;\n }\n return `${minutes}:${seconds.toString().padStart(2, \"0\")}`;\n}\n\nprogram\n .command(\"render [directory]\")\n .description(\"Render a video composition locally\")\n .option(\"-o, --output <path>\", \"Output file path\", \"output.mp4\")\n .option(\"--url <url>\", \"URL to render (bypasses directory/server startup)\")\n .option(\"-d, --data <json>\", \"Custom render data (JSON string)\")\n .option(\"--data-file <path>\", \"Custom render data from JSON file\")\n .option(\"--fps <number>\", \"Frame rate\", \"30\")\n .option(\"--scale <number>\", \"Resolution scale (0-1)\", \"1\")\n .option(\"--include-audio\", \"Include audio track\", true)\n .option(\"--no-include-audio\", \"Exclude audio track\")\n .option(\"--from-ms <number>\", \"Start time in milliseconds\")\n .option(\"--to-ms <number>\", \"End time in milliseconds\")\n .option(\"--experimental-native-render\", \"Use experimental canvas capture API (faster)\")\n .option(\"--profile\", \"Enable CPU profiling\")\n .option(\"--profile-output <path>\", \"Profile output path\", \"./render-profile.cpuprofile\")\n .action(async (directory = \".\", options) => {\n // If running from the dev script (via tsx), ORIGINAL_CWD contains the user's actual directory\n const baseCwd = process.env.ORIGINAL_CWD || process.cwd();\n const outputPath = path.resolve(baseCwd, options.output);\n\n // Parse custom data if provided\n let renderData: Record<string, unknown> | undefined;\n if (options.dataFile) {\n const dataFileContent = await readFile(options.dataFile, \"utf-8\");\n renderData = JSON.parse(dataFileContent);\n log(\"Loaded render data from file:\", options.dataFile);\n } else if (options.data) {\n renderData = JSON.parse(options.data);\n log(\"Using render data from --data option\");\n }\n\n // Parse numeric options\n const fps = parseInt(options.fps, 10);\n const scale = parseFloat(options.scale);\n const fromMs = options.fromMs ? parseInt(options.fromMs, 10) : undefined;\n const toMs = options.toMs ? parseInt(options.toMs, 10) : undefined;\n\n // Single initialization spinner for all setup steps\n const initSpinner = ora(\"Initializing...\").start();\n \n let renderUrl: string;\n let viteServer: SpawnedViteServer | null = null;\n\n try {\n // Determine URL to render\n if (options.url) {\n // Use provided URL directly\n renderUrl = options.url;\n log(\"Using provided URL:\", renderUrl);\n } else {\n // Spawn Vite dev server as subprocess\n // This allows Vite to run with full config resolution (including Tailwind)\n // while we maintain Playwright control for rendering\n const srcDir = path.resolve(baseCwd, directory);\n viteServer = await spawnViteServer(srcDir);\n renderUrl = viteServer.url;\n log(\"Vite server spawned at:\", renderUrl);\n }\n\n // Launch browser and load SDK (all within initialization)\n await launchBrowserAndWaitForSDK(\n {\n url: renderUrl,\n headless: true,\n interactive: false,\n efInteractive: false,\n nativeRender: options.experimentalNativeRender === true,\n profile: options.profile === true,\n profileOutput: options.profileOutput,\n silent: true, // Suppress individual spinners since we show unified \"Initializing...\"\n },\n async (page) => {\n initSpinner.succeed(\"Ready\");\n \n // Now handle the render\n await withProfiling(\n page,\n {\n enabled: options.profile === true,\n outputPath: options.profileOutput,\n },\n async () => {\n // Open output file for streaming writes\n const outputStream = createWriteStream(outputPath);\n let chunkCount = 0;\n let totalBytes = 0;\n\n // Expose chunk handler - writes directly to file\n await page.exposeFunction(\"onRenderChunk\", (chunk: StreamTargetChunk) => {\n writeFile(outputPath, chunk.data, { flag: \"a\" });\n chunkCount++;\n totalBytes += chunk.data.length;\n log(`Received chunk ${chunkCount}: ${chunk.data.length} bytes (total: ${totalBytes} bytes)`);\n });\n\n // Set custom render data if provided\n if (renderData) {\n await page.evaluate((data) => {\n window.EF_RENDER_DATA = data;\n }, renderData);\n log(\"Set EF_RENDER_DATA:\", renderData);\n }\n\n // Wait for EF_RENDER API to be available\n await page.waitForFunction(\n () => typeof window.EF_RENDER !== \"undefined\",\n { timeout: 10_000 },\n );\n\n // Check if ready\n const isReady = await page.evaluate(() => window.EF_RENDER?.isReady());\n if (!isReady) {\n throw new Error(\"Render API is not ready. No ef-timegroup found.\");\n }\n\n // Create progress spinner\n const progressSpinner = ora(\"Rendering video...\").start();\n\n // Track last progress for completion message\n let lastProgress: {\n currentFrame: number;\n totalFrames: number;\n renderedMs: number;\n totalDurationMs: number;\n elapsedMs: number;\n speedMultiplier: number;\n } | null = null;\n\n // Expose progress callback\n await page.exposeFunction(\"onRenderProgress\", (progress: {\n progress: number;\n currentFrame: number;\n totalFrames: number;\n renderedMs: number;\n totalDurationMs: number;\n elapsedMs: number;\n estimatedRemainingMs: number;\n speedMultiplier: number;\n }) => {\n const percent = (progress.progress * 100).toFixed(1);\n const renderedTime = formatTime(progress.renderedMs);\n const totalTime = formatTime(progress.totalDurationMs);\n const remainingTime = formatTime(progress.estimatedRemainingMs);\n const speed = progress.speedMultiplier.toFixed(2);\n \n progressSpinner.text = `Rendering: ${progress.currentFrame}/${progress.totalFrames} frames (${percent}%) | ${renderedTime}/${totalTime} | ${remainingTime} remaining | ${speed}x speed`;\n \n // Store last progress for completion message\n lastProgress = {\n currentFrame: progress.currentFrame,\n totalFrames: progress.totalFrames,\n renderedMs: progress.renderedMs,\n totalDurationMs: progress.totalDurationMs,\n elapsedMs: progress.elapsedMs,\n speedMultiplier: progress.speedMultiplier,\n };\n });\n\n // Render with streaming\n try {\n const renderOptions: any = {\n fps,\n scale,\n includeAudio: options.includeAudio !== false,\n };\n\n if (fromMs !== undefined) {\n renderOptions.fromMs = fromMs;\n }\n if (toMs !== undefined) {\n renderOptions.toMs = toMs;\n }\n\n await page.evaluate(async (opts) => {\n await window.EF_RENDER!.renderStreaming(opts);\n }, renderOptions);\n\n // Build completion message with performance stats\n if (lastProgress) {\n const renderedTime = formatTime(lastProgress.renderedMs);\n const totalTime = formatTime(lastProgress.totalDurationMs);\n const elapsedTime = formatTime(lastProgress.elapsedMs);\n const speed = lastProgress.speedMultiplier.toFixed(2);\n progressSpinner.succeed(\n `Render complete: ${lastProgress.currentFrame}/${lastProgress.totalFrames} frames | ${renderedTime}/${totalTime} | ${elapsedTime} elapsed | ${speed}x speed`\n );\n } else {\n progressSpinner.succeed(\"Render complete\");\n }\n } catch (error) {\n progressSpinner.fail(\"Render failed\");\n throw error;\n }\n\n // Close the output stream\n outputStream.end();\n\n // Wait for stream to finish\n await new Promise<void>((resolve, reject) => {\n outputStream.on(\"finish\", () => {\n log(`Render complete: ${chunkCount} chunks, ${totalBytes} bytes written to ${outputPath}`);\n resolve();\n });\n outputStream.on(\"error\", reject);\n });\n },\n );\n },\n );\n } catch (error) {\n initSpinner.fail(\"Initialization failed\");\n throw error;\n }\n\n // Clean up spawned Vite process\n if (viteServer) {\n viteServer.kill();\n log(\"Vite server stopped\");\n }\n\n process.stderr.write(`\\nRender complete: ${outputPath}\\n`);\n });\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"render.js","names":["renderData: Record<string, unknown> | undefined","renderUrl: string","viteServer: SpawnedViteServer | null","lastProgress: {\n currentFrame: number;\n totalFrames: number;\n renderedMs: number;\n totalDurationMs: number;\n elapsedMs: number;\n speedMultiplier: number;\n } | null","renderOptions: any"],"sources":["../../src/commands/render.ts"],"sourcesContent":["import { readFile, writeFile } from \"node:fs/promises\";\nimport { createWriteStream } from \"node:fs\";\nimport path from \"node:path\";\nimport { program } from \"commander\";\nimport debug from \"debug\";\nimport ora from \"ora\";\nimport type { Page } from \"playwright\";\nimport { launchBrowserAndWaitForSDK } from \"../utils/launchBrowserAndWaitForSDK.js\";\nimport { spawnViteServer, type SpawnedViteServer } from \"../utils/spawnViteServer.js\";\nimport { StreamTargetChunk } from \"mediabunny\";\nimport { withProfiling } from \"../utils/profileRender.js\";\n\nconst log = debug(\"ef:cli:render\");\n\n/**\n * Format milliseconds as MM:SS or HH:MM:SS\n */\nfunction formatTime(ms: number): string {\n const totalSeconds = Math.floor(ms / 1000);\n const hours = Math.floor(totalSeconds / 3600);\n const minutes = Math.floor((totalSeconds % 3600) / 60);\n const seconds = totalSeconds % 60;\n\n if (hours > 0) {\n return `${hours}:${minutes.toString().padStart(2, \"0\")}:${seconds.toString().padStart(2, \"0\")}`;\n }\n return `${minutes}:${seconds.toString().padStart(2, \"0\")}`;\n}\n\nprogram\n .command(\"render [directory]\")\n .description(\"Render a video composition locally\")\n .option(\"-o, --output <path>\", \"Output file path\", \"output.mp4\")\n .option(\"--url <url>\", \"URL to render (bypasses directory/server startup)\")\n .option(\"-d, --data <json>\", \"Custom render data (JSON string)\")\n .option(\"--data-file <path>\", \"Custom render data from JSON file\")\n .option(\"--fps <number>\", \"Frame rate\", \"30\")\n .option(\"--scale <number>\", \"Resolution scale (0-1)\", \"1\")\n .option(\"--include-audio\", \"Include audio track\", true)\n .option(\"--no-include-audio\", \"Exclude audio track\")\n .option(\"--from-ms <number>\", \"Start time in milliseconds\")\n .option(\"--to-ms <number>\", \"End time in milliseconds\")\n .option(\"--experimental-native-render\", \"Use experimental canvas capture API (faster)\")\n .option(\"--profile\", \"Enable CPU profiling\")\n .option(\"--profile-output <path>\", \"Profile output path\", \"./render-profile.cpuprofile\")\n .action(async (directory = \".\", options) => {\n // If running from the dev script (via tsx), ORIGINAL_CWD contains the user's actual directory\n const baseCwd = process.env.ORIGINAL_CWD || process.cwd();\n const outputPath = path.resolve(baseCwd, options.output);\n\n // Parse custom data if provided\n let renderData: Record<string, unknown> | undefined;\n if (options.dataFile) {\n const dataFileContent = await readFile(options.dataFile, \"utf-8\");\n renderData = JSON.parse(dataFileContent);\n log(\"Loaded render data from file:\", options.dataFile);\n } else if (options.data) {\n renderData = JSON.parse(options.data);\n log(\"Using render data from --data option\");\n }\n\n // Parse numeric options\n const fps = parseInt(options.fps, 10);\n const scale = parseFloat(options.scale);\n const fromMs = options.fromMs ? parseInt(options.fromMs, 10) : undefined;\n const toMs = options.toMs ? parseInt(options.toMs, 10) : undefined;\n\n // Single initialization spinner for all setup steps\n const initSpinner = ora(\"Initializing...\").start();\n \n let renderUrl: string;\n let viteServer: SpawnedViteServer | null = null;\n\n try {\n // Determine URL to render\n if (options.url) {\n // Use provided URL directly\n renderUrl = options.url;\n log(\"Using provided URL:\", renderUrl);\n } else {\n // Spawn Vite dev server as subprocess\n // This allows Vite to run with full config resolution (including Tailwind)\n // while we maintain Playwright control for rendering\n const srcDir = path.resolve(baseCwd, directory);\n viteServer = await spawnViteServer(srcDir);\n renderUrl = viteServer.url;\n log(\"Vite server spawned at:\", renderUrl);\n }\n\n // Launch browser and load SDK (all within initialization)\n await launchBrowserAndWaitForSDK(\n {\n url: renderUrl,\n headless: true,\n interactive: false,\n efInteractive: false,\n nativeRender: options.experimentalNativeRender === true,\n profile: options.profile === true,\n profileOutput: options.profileOutput,\n silent: true, // Suppress individual spinners since we show unified \"Initializing...\"\n },\n async (page) => {\n initSpinner.succeed(\"Ready\");\n \n // Now handle the render\n await withProfiling(\n page,\n {\n enabled: options.profile === true,\n outputPath: options.profileOutput,\n },\n async () => {\n // Open output file for streaming writes\n const outputStream = createWriteStream(outputPath);\n let chunkCount = 0;\n let totalBytes = 0;\n\n // Expose chunk handler - writes directly to file\n await page.exposeFunction(\"onRenderChunk\", (chunk: StreamTargetChunk) => {\n writeFile(outputPath, chunk.data, { flag: \"a\" });\n chunkCount++;\n totalBytes += chunk.data.length;\n log(`Received chunk ${chunkCount}: ${chunk.data.length} bytes (total: ${totalBytes} bytes)`);\n });\n\n // Set custom render data if provided\n if (renderData) {\n await page.evaluate((data) => {\n window.EF_RENDER_DATA = data;\n }, renderData);\n log(\"Set EF_RENDER_DATA:\", renderData);\n }\n\n // Wait for EF_RENDER API to be available\n await page.waitForFunction(\n () => typeof window.EF_RENDER !== \"undefined\",\n { timeout: 10_000 },\n );\n\n // Check if ready\n const isReady = await page.evaluate(() => window.EF_RENDER?.isReady());\n if (!isReady) {\n throw new Error(\"Render API is not ready. No ef-timegroup found.\");\n }\n\n // Create progress spinner\n const progressSpinner = ora(\"Rendering video...\").start();\n\n // Track last progress for completion message\n let lastProgress: {\n currentFrame: number;\n totalFrames: number;\n renderedMs: number;\n totalDurationMs: number;\n elapsedMs: number;\n speedMultiplier: number;\n } | null = null;\n\n // Expose progress callback\n await page.exposeFunction(\"onRenderProgress\", (progress: {\n progress: number;\n currentFrame: number;\n totalFrames: number;\n renderedMs: number;\n totalDurationMs: number;\n elapsedMs: number;\n estimatedRemainingMs: number;\n speedMultiplier: number;\n }) => {\n const percent = (progress.progress * 100).toFixed(1);\n const renderedTime = formatTime(progress.renderedMs);\n const totalTime = formatTime(progress.totalDurationMs);\n const remainingTime = formatTime(progress.estimatedRemainingMs);\n const speed = progress.speedMultiplier.toFixed(2);\n \n progressSpinner.text = `Rendering: ${progress.currentFrame}/${progress.totalFrames} frames (${percent}%) | ${renderedTime}/${totalTime} | ${remainingTime} remaining | ${speed}x speed`;\n \n // Store last progress for completion message\n lastProgress = {\n currentFrame: progress.currentFrame,\n totalFrames: progress.totalFrames,\n renderedMs: progress.renderedMs,\n totalDurationMs: progress.totalDurationMs,\n elapsedMs: progress.elapsedMs,\n speedMultiplier: progress.speedMultiplier,\n };\n });\n\n // Render with streaming\n try {\n const renderOptions: any = {\n fps,\n scale,\n includeAudio: options.includeAudio !== false,\n };\n\n if (fromMs !== undefined) {\n renderOptions.fromMs = fromMs;\n }\n if (toMs !== undefined) {\n renderOptions.toMs = toMs;\n }\n\n await page.evaluate(async (opts) => {\n await window.EF_RENDER!.renderStreaming(opts);\n }, renderOptions);\n\n // Build completion message with performance stats\n if (lastProgress) {\n const renderedTime = formatTime(lastProgress.renderedMs);\n const totalTime = formatTime(lastProgress.totalDurationMs);\n const elapsedTime = formatTime(lastProgress.elapsedMs);\n const speed = lastProgress.speedMultiplier.toFixed(2);\n progressSpinner.succeed(\n `Render complete: ${lastProgress.currentFrame}/${lastProgress.totalFrames} frames | ${renderedTime}/${totalTime} | ${elapsedTime} elapsed | ${speed}x speed`\n );\n } else {\n progressSpinner.succeed(\"Render complete\");\n }\n } catch (error) {\n progressSpinner.fail(\"Render failed\");\n throw error;\n }\n\n // Close the output stream\n outputStream.end();\n\n // Wait for stream to finish\n await new Promise<void>((resolve, reject) => {\n outputStream.on(\"finish\", () => {\n log(`Render complete: ${chunkCount} chunks, ${totalBytes} bytes written to ${outputPath}`);\n resolve();\n });\n outputStream.on(\"error\", reject);\n });\n },\n );\n },\n );\n } catch (error) {\n initSpinner.fail(\"Initialization failed\");\n throw error;\n }\n\n // Clean up spawned Vite process\n if (viteServer) {\n viteServer.kill();\n log(\"Vite server stopped\");\n }\n\n process.stderr.write(`\\nRender complete: ${outputPath}\\n`);\n });\n"],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { program } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import ora from "ora";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
import { writeFile } from "node:fs/promises";
|
|
6
|
+
import { generateCaptionDataFromPath } from "@editframe/assets";
|
|
7
|
+
|
|
8
|
+
//#region src/commands/transcribe.ts
|
|
9
|
+
program.command("transcribe <input>").description("Generate captions from audio/video file using whisper_timestamped").option("-o, --output <file>", "Output JSON file", "captions.json").option("-l, --language <lang>", "Language code (e.g., en, es, fr)", "en").action(async (input, options) => {
|
|
10
|
+
const spinner = ora("Generating captions...").start();
|
|
11
|
+
try {
|
|
12
|
+
const absoluteInput = resolve(input);
|
|
13
|
+
const absoluteOutput = resolve(options.output);
|
|
14
|
+
spinner.text = `Transcribing ${input}...`;
|
|
15
|
+
const captionData = await generateCaptionDataFromPath(absoluteInput);
|
|
16
|
+
spinner.text = `Writing captions to ${options.output}...`;
|
|
17
|
+
await writeFile(absoluteOutput, captionData, "utf-8");
|
|
18
|
+
spinner.succeed(chalk.green(`✓ Captions generated successfully: ${options.output}`));
|
|
19
|
+
const parsed = JSON.parse(captionData);
|
|
20
|
+
console.log(chalk.dim(` ${parsed.segments.length} segments`));
|
|
21
|
+
console.log(chalk.dim(` ${parsed.word_segments.length} words`));
|
|
22
|
+
} catch (error) {
|
|
23
|
+
spinner.fail(chalk.red("Failed to generate captions"));
|
|
24
|
+
if (error.message.includes("whisper_timestamped")) {
|
|
25
|
+
console.error(chalk.red("\nwhisper_timestamped is not installed or not in PATH"));
|
|
26
|
+
console.error(chalk.yellow("\nInstall it with:"));
|
|
27
|
+
console.error(chalk.white(" pip3 install whisper-timestamped"));
|
|
28
|
+
console.error(chalk.dim("\nOr check installation instructions at:"));
|
|
29
|
+
console.error(chalk.dim(" https://github.com/linto-ai/whisper-timestamped#installation"));
|
|
30
|
+
} else console.error(chalk.red(`\n${error.message}`));
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
//#endregion
|
|
36
|
+
export { };
|
|
37
|
+
//# sourceMappingURL=transcribe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcribe.js","names":[],"sources":["../../src/commands/transcribe.ts"],"sourcesContent":["import { writeFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { program } from \"commander\";\nimport ora from \"ora\";\nimport chalk from \"chalk\";\nimport { generateCaptionDataFromPath } from \"@editframe/assets\";\n\nprogram\n .command(\"transcribe <input>\")\n .description(\"Generate captions from audio/video file using whisper_timestamped\")\n .option(\"-o, --output <file>\", \"Output JSON file\", \"captions.json\")\n .option(\"-l, --language <lang>\", \"Language code (e.g., en, es, fr)\", \"en\")\n .action(async (input: string, options: { output: string; language: string }) => {\n const spinner = ora(\"Generating captions...\").start();\n \n try {\n const absoluteInput = resolve(input);\n const absoluteOutput = resolve(options.output);\n \n spinner.text = `Transcribing ${input}...`;\n \n // Generate captions using the same function as the vite plugin\n const captionData = await generateCaptionDataFromPath(absoluteInput);\n \n spinner.text = `Writing captions to ${options.output}...`;\n await writeFile(absoluteOutput, captionData, \"utf-8\");\n \n spinner.succeed(\n chalk.green(`✓ Captions generated successfully: ${options.output}`)\n );\n \n // Parse to show stats\n const parsed = JSON.parse(captionData);\n console.log(chalk.dim(` ${parsed.segments.length} segments`));\n console.log(chalk.dim(` ${parsed.word_segments.length} words`));\n } catch (error) {\n spinner.fail(chalk.red(\"Failed to generate captions\"));\n \n if ((error as Error).message.includes(\"whisper_timestamped\")) {\n console.error(chalk.red(\"\\nwhisper_timestamped is not installed or not in PATH\"));\n console.error(chalk.yellow(\"\\nInstall it with:\"));\n console.error(chalk.white(\" pip3 install whisper-timestamped\"));\n console.error(chalk.dim(\"\\nOr check installation instructions at:\"));\n console.error(chalk.dim(\" https://github.com/linto-ai/whisper-timestamped#installation\"));\n } else {\n console.error(chalk.red(`\\n${(error as Error).message}`));\n }\n \n process.exit(1);\n }\n });\n"],"mappings":";;;;;;;;AAOA,QACG,QAAQ,qBAAqB,CAC7B,YAAY,oEAAoE,CAChF,OAAO,uBAAuB,oBAAoB,gBAAgB,CAClE,OAAO,yBAAyB,oCAAoC,KAAK,CACzE,OAAO,OAAO,OAAe,YAAkD;CAC9E,MAAM,UAAU,IAAI,yBAAyB,CAAC,OAAO;AAErD,KAAI;EACF,MAAM,gBAAgB,QAAQ,MAAM;EACpC,MAAM,iBAAiB,QAAQ,QAAQ,OAAO;AAE9C,UAAQ,OAAO,gBAAgB,MAAM;EAGrC,MAAM,cAAc,MAAM,4BAA4B,cAAc;AAEpE,UAAQ,OAAO,uBAAuB,QAAQ,OAAO;AACrD,QAAM,UAAU,gBAAgB,aAAa,QAAQ;AAErD,UAAQ,QACN,MAAM,MAAM,sCAAsC,QAAQ,SAAS,CACpE;EAGD,MAAM,SAAS,KAAK,MAAM,YAAY;AACtC,UAAQ,IAAI,MAAM,IAAI,KAAK,OAAO,SAAS,OAAO,WAAW,CAAC;AAC9D,UAAQ,IAAI,MAAM,IAAI,KAAK,OAAO,cAAc,OAAO,QAAQ,CAAC;UACzD,OAAO;AACd,UAAQ,KAAK,MAAM,IAAI,8BAA8B,CAAC;AAEtD,MAAK,MAAgB,QAAQ,SAAS,sBAAsB,EAAE;AAC5D,WAAQ,MAAM,MAAM,IAAI,wDAAwD,CAAC;AACjF,WAAQ,MAAM,MAAM,OAAO,qBAAqB,CAAC;AACjD,WAAQ,MAAM,MAAM,MAAM,qCAAqC,CAAC;AAChE,WAAQ,MAAM,MAAM,IAAI,2CAA2C,CAAC;AACpE,WAAQ,MAAM,MAAM,IAAI,iEAAiE,CAAC;QAE1F,SAAQ,MAAM,MAAM,IAAI,KAAM,MAAgB,UAAU,CAAC;AAG3D,UAAQ,KAAK,EAAE;;EAEjB"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport \"dotenv/config\";\nimport { Option, program } from \"commander\";\n\nimport { VERSION } from \"./VERSION.js\";\n\nprogram\n .name(\"editframe\")\n .addOption(new Option(\"-t, --token <token>\", \"API Token\").env(\"EF_TOKEN\"))\n .addOption(\n new Option(\"--ef-host <host>\", \"Editframe Host\")\n .env(\"EF_HOST\")\n .default(\"https://editframe.com\"),\n )\n .addOption(\n new Option(\"--ef-render-host <host>\", \"Editframe Render Host\")\n .env(\"EF_RENDER_HOST\")\n .default(\"https://editframe.com\"),\n )\n .version(VERSION);\n\nimport \"./commands/auth.js\";\nimport \"./commands/sync.js\";\nimport \"./commands/cloud-render.js\";\nimport \"./commands/render.js\";\nimport \"./commands/preview.js\";\nimport \"./commands/process.js\";\nimport \"./commands/process-file.js\";\nimport \"./commands/check.js\";\nimport \"./commands/webhook.js\";\n\nprogram.parse(process.argv);\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport \"dotenv/config\";\nimport { Option, program } from \"commander\";\n\nimport { VERSION } from \"./VERSION.js\";\n\nprogram\n .name(\"editframe\")\n .addOption(new Option(\"-t, --token <token>\", \"API Token\").env(\"EF_TOKEN\"))\n .addOption(\n new Option(\"--ef-host <host>\", \"Editframe Host\")\n .env(\"EF_HOST\")\n .default(\"https://editframe.com\"),\n )\n .addOption(\n new Option(\"--ef-render-host <host>\", \"Editframe Render Host\")\n .env(\"EF_RENDER_HOST\")\n .default(\"https://editframe.com\"),\n )\n .version(VERSION);\n\nimport \"./commands/auth.js\";\nimport \"./commands/sync.js\";\nimport \"./commands/cloud-render.js\";\nimport \"./commands/render.js\";\nimport \"./commands/preview.js\";\nimport \"./commands/process.js\";\nimport \"./commands/process-file.js\";\nimport \"./commands/check.js\";\nimport \"./commands/webhook.js\";\nimport \"./commands/transcribe.js\";\n\nprogram.parse(process.argv);\n"],"mappings":";;;;;;;;;;;;;;;;AAMA,QACG,KAAK,YAAY,CACjB,UAAU,IAAI,OAAO,uBAAuB,YAAY,CAAC,IAAI,WAAW,CAAC,CACzE,UACC,IAAI,OAAO,oBAAoB,iBAAiB,CAC7C,IAAI,UAAU,CACd,QAAQ,wBAAwB,CACpC,CACA,UACC,IAAI,OAAO,2BAA2B,wBAAwB,CAC3D,IAAI,iBAAiB,CACrB,QAAQ,wBAAwB,CACpC,CACA,QAAQ,QAAQ;AAanB,QAAQ,MAAM,QAAQ,KAAK"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@editframe/cli",
|
|
3
|
-
"version": "0.36.
|
|
3
|
+
"version": "0.36.1-beta",
|
|
4
4
|
"description": "Command line interface for EditFrame",
|
|
5
5
|
"bin": {
|
|
6
6
|
"editframe": "./dist/index.js"
|
|
@@ -22,10 +22,10 @@
|
|
|
22
22
|
"typescript": "^5.5.4"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@editframe/api": "0.36.
|
|
26
|
-
"@editframe/assets": "0.36.
|
|
27
|
-
"@editframe/elements": "0.36.
|
|
28
|
-
"@editframe/vite-plugin": "0.36.
|
|
25
|
+
"@editframe/api": "0.36.1-beta",
|
|
26
|
+
"@editframe/assets": "0.36.1-beta",
|
|
27
|
+
"@editframe/elements": "0.36.1-beta",
|
|
28
|
+
"@editframe/vite-plugin": "0.36.1-beta",
|
|
29
29
|
"@inquirer/prompts": "^5.3.8",
|
|
30
30
|
"chalk": "^5.3.0",
|
|
31
31
|
"commander": "^12.0.0",
|