@conceptcraft/mindframes 0.1.4 → 0.1.5
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.js +840 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -293,6 +293,12 @@ function progressBar(current, total, width = 30, showPercentage = true) {
|
|
|
293
293
|
const bar = chalk.green("\u2588".repeat(filled)) + chalk.gray("\u2591".repeat(empty));
|
|
294
294
|
return showPercentage ? `[${bar}] ${percentage}%` : `[${bar}]`;
|
|
295
295
|
}
|
|
296
|
+
function formatJson(data) {
|
|
297
|
+
return JSON.stringify(data, null, 2);
|
|
298
|
+
}
|
|
299
|
+
function printJson(data) {
|
|
300
|
+
console.log(formatJson(data));
|
|
301
|
+
}
|
|
296
302
|
var init_output = __esm({
|
|
297
303
|
"src/lib/output.ts"() {
|
|
298
304
|
"use strict";
|
|
@@ -300,11 +306,19 @@ var init_output = __esm({
|
|
|
300
306
|
}
|
|
301
307
|
});
|
|
302
308
|
|
|
309
|
+
// src/types/media.ts
|
|
310
|
+
var init_media = __esm({
|
|
311
|
+
"src/types/media.ts"() {
|
|
312
|
+
"use strict";
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
|
|
303
316
|
// src/types/index.ts
|
|
304
317
|
var EXIT_CODES;
|
|
305
318
|
var init_types = __esm({
|
|
306
319
|
"src/types/index.ts"() {
|
|
307
320
|
"use strict";
|
|
321
|
+
init_media();
|
|
308
322
|
EXIT_CODES = {
|
|
309
323
|
SUCCESS: 0,
|
|
310
324
|
GENERAL_ERROR: 1,
|
|
@@ -889,6 +903,123 @@ async function validateGeneration(mode, slideCount, teamId) {
|
|
|
889
903
|
}
|
|
890
904
|
return limits;
|
|
891
905
|
}
|
|
906
|
+
async function generateSpeech(ttsRequest) {
|
|
907
|
+
const apiUrl = getApiUrl();
|
|
908
|
+
if (!hasAuth()) {
|
|
909
|
+
throw new ApiError(
|
|
910
|
+
"Not authenticated. Run 'cc login' or set CC_SLIDES_API_KEY environment variable.",
|
|
911
|
+
401,
|
|
912
|
+
2
|
|
913
|
+
);
|
|
914
|
+
}
|
|
915
|
+
const authHeaders = await getAuthHeaders();
|
|
916
|
+
let response;
|
|
917
|
+
try {
|
|
918
|
+
response = await fetch(`${apiUrl}/api/cli/tts`, {
|
|
919
|
+
method: "POST",
|
|
920
|
+
headers: {
|
|
921
|
+
"Content-Type": "application/json",
|
|
922
|
+
...authHeaders
|
|
923
|
+
},
|
|
924
|
+
body: JSON.stringify(ttsRequest)
|
|
925
|
+
});
|
|
926
|
+
} catch (error2) {
|
|
927
|
+
throw new ApiError(
|
|
928
|
+
`Network error: ${error2 instanceof Error ? error2.message : "Unknown error"}`,
|
|
929
|
+
0,
|
|
930
|
+
5
|
|
931
|
+
);
|
|
932
|
+
}
|
|
933
|
+
if (!response.ok) {
|
|
934
|
+
const errorText = await response.text().catch(() => "Unknown error");
|
|
935
|
+
let errorMessage;
|
|
936
|
+
try {
|
|
937
|
+
const errorJson = JSON.parse(errorText);
|
|
938
|
+
errorMessage = errorJson.error || errorJson.message || errorText;
|
|
939
|
+
} catch {
|
|
940
|
+
errorMessage = errorText;
|
|
941
|
+
}
|
|
942
|
+
throw new ApiError(errorMessage, response.status, response.status === 401 ? 2 : 1);
|
|
943
|
+
}
|
|
944
|
+
const audioData = Buffer.from(await response.arrayBuffer());
|
|
945
|
+
const duration = parseFloat(response.headers.get("X-Duration-Seconds") || "0");
|
|
946
|
+
const cost = parseFloat(response.headers.get("X-Cost-USD") || "0");
|
|
947
|
+
const provider = response.headers.get("X-Provider") || "unknown";
|
|
948
|
+
const format = response.headers.get("X-Audio-Format") || "mp3";
|
|
949
|
+
return {
|
|
950
|
+
audioData,
|
|
951
|
+
duration,
|
|
952
|
+
cost,
|
|
953
|
+
provider,
|
|
954
|
+
format
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
async function getVoices() {
|
|
958
|
+
return request("/api/cli/tts");
|
|
959
|
+
}
|
|
960
|
+
async function generateMusic(musicRequest) {
|
|
961
|
+
const response = await request("/api/cli/music", {
|
|
962
|
+
method: "POST",
|
|
963
|
+
body: musicRequest
|
|
964
|
+
});
|
|
965
|
+
if (!response.data) {
|
|
966
|
+
throw new ApiError(`Invalid API response: ${JSON.stringify(response)}`, 500, 1);
|
|
967
|
+
}
|
|
968
|
+
return {
|
|
969
|
+
requestId: response.data.id,
|
|
970
|
+
status: response.data.status,
|
|
971
|
+
audioUrl: response.data.audioUrl,
|
|
972
|
+
duration: response.data.duration,
|
|
973
|
+
cost: response.data.cost,
|
|
974
|
+
error: response.data.error
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
async function checkMusicStatus(requestId) {
|
|
978
|
+
const response = await request(
|
|
979
|
+
`/api/cli/music?requestId=${encodeURIComponent(requestId)}`
|
|
980
|
+
);
|
|
981
|
+
return {
|
|
982
|
+
requestId: response.data.id,
|
|
983
|
+
status: response.data.status,
|
|
984
|
+
audioUrl: response.data.audioUrl,
|
|
985
|
+
duration: response.data.duration,
|
|
986
|
+
cost: response.data.cost,
|
|
987
|
+
error: response.data.error
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
async function mixAudio(mixRequest) {
|
|
991
|
+
return request("/api/cli/mix", {
|
|
992
|
+
method: "POST",
|
|
993
|
+
body: mixRequest
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
async function checkMixStatus(requestId) {
|
|
997
|
+
return request(
|
|
998
|
+
`/api/cli/mix?requestId=${encodeURIComponent(requestId)}`
|
|
999
|
+
);
|
|
1000
|
+
}
|
|
1001
|
+
async function searchImages(searchRequest) {
|
|
1002
|
+
return request("/api/cli/images/search", {
|
|
1003
|
+
method: "POST",
|
|
1004
|
+
body: searchRequest
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
async function searchVideos(searchRequest) {
|
|
1008
|
+
return request("/api/cli/videos/search", {
|
|
1009
|
+
method: "POST",
|
|
1010
|
+
body: searchRequest
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
async function pollForCompletion(checkFn, maxAttempts = 60, intervalMs = 2e3) {
|
|
1014
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
1015
|
+
const result = await checkFn();
|
|
1016
|
+
if (result.status === "completed" || result.status === "failed") {
|
|
1017
|
+
return result;
|
|
1018
|
+
}
|
|
1019
|
+
await new Promise((resolve4) => setTimeout(resolve4, intervalMs));
|
|
1020
|
+
}
|
|
1021
|
+
throw new ApiError("Operation timed out", 408, 1);
|
|
1022
|
+
}
|
|
892
1023
|
var ApiError;
|
|
893
1024
|
var init_api = __esm({
|
|
894
1025
|
"src/lib/api.ts"() {
|
|
@@ -1054,6 +1185,20 @@ async function exchangeCodeForTokens(tokenEndpoint, code, codeVerifier, redirect
|
|
|
1054
1185
|
}
|
|
1055
1186
|
function startCallbackServer(port, expectedState) {
|
|
1056
1187
|
return new Promise((resolve4, reject) => {
|
|
1188
|
+
let timeoutId;
|
|
1189
|
+
let settled = false;
|
|
1190
|
+
const cleanup = () => {
|
|
1191
|
+
if (settled) return;
|
|
1192
|
+
settled = true;
|
|
1193
|
+
clearTimeout(timeoutId);
|
|
1194
|
+
process.off("SIGINT", onCancel);
|
|
1195
|
+
process.off("SIGTERM", onCancel);
|
|
1196
|
+
server.close();
|
|
1197
|
+
};
|
|
1198
|
+
const onCancel = () => {
|
|
1199
|
+
cleanup();
|
|
1200
|
+
reject(new Error("Login cancelled"));
|
|
1201
|
+
};
|
|
1057
1202
|
const server = http.createServer((req, res) => {
|
|
1058
1203
|
const url = new URL(req.url || "", `http://localhost:${port}`);
|
|
1059
1204
|
if (url.pathname !== "/callback") {
|
|
@@ -1078,7 +1223,7 @@ function startCallbackServer(port, expectedState) {
|
|
|
1078
1223
|
</body>
|
|
1079
1224
|
</html>
|
|
1080
1225
|
`);
|
|
1081
|
-
|
|
1226
|
+
cleanup();
|
|
1082
1227
|
reject(new Error(errorDescription || errorParam));
|
|
1083
1228
|
return;
|
|
1084
1229
|
}
|
|
@@ -1094,7 +1239,7 @@ function startCallbackServer(port, expectedState) {
|
|
|
1094
1239
|
</body>
|
|
1095
1240
|
</html>
|
|
1096
1241
|
`);
|
|
1097
|
-
|
|
1242
|
+
cleanup();
|
|
1098
1243
|
reject(new Error("Missing authorization code or state"));
|
|
1099
1244
|
return;
|
|
1100
1245
|
}
|
|
@@ -1110,7 +1255,7 @@ function startCallbackServer(port, expectedState) {
|
|
|
1110
1255
|
</body>
|
|
1111
1256
|
</html>
|
|
1112
1257
|
`);
|
|
1113
|
-
|
|
1258
|
+
cleanup();
|
|
1114
1259
|
reject(new Error("State mismatch"));
|
|
1115
1260
|
return;
|
|
1116
1261
|
}
|
|
@@ -1124,20 +1269,14 @@ function startCallbackServer(port, expectedState) {
|
|
|
1124
1269
|
</body>
|
|
1125
1270
|
</html>
|
|
1126
1271
|
`);
|
|
1127
|
-
|
|
1272
|
+
cleanup();
|
|
1128
1273
|
resolve4({ code, state });
|
|
1129
1274
|
});
|
|
1130
1275
|
server.listen(port);
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
process.once("SIGINT", cleanup);
|
|
1136
|
-
process.once("SIGTERM", cleanup);
|
|
1137
|
-
setTimeout(() => {
|
|
1138
|
-
process.off("SIGINT", cleanup);
|
|
1139
|
-
process.off("SIGTERM", cleanup);
|
|
1140
|
-
server.close();
|
|
1276
|
+
process.once("SIGINT", onCancel);
|
|
1277
|
+
process.once("SIGTERM", onCancel);
|
|
1278
|
+
timeoutId = setTimeout(() => {
|
|
1279
|
+
cleanup();
|
|
1141
1280
|
reject(new Error("Login timed out - no callback received within 5 minutes"));
|
|
1142
1281
|
}, 5 * 60 * 1e3);
|
|
1143
1282
|
});
|
|
@@ -1238,6 +1377,7 @@ var init_login = __esm({
|
|
|
1238
1377
|
}
|
|
1239
1378
|
try {
|
|
1240
1379
|
await runLoginFlow(options);
|
|
1380
|
+
process.exit(0);
|
|
1241
1381
|
} catch {
|
|
1242
1382
|
process.exit(1);
|
|
1243
1383
|
}
|
|
@@ -1246,7 +1386,7 @@ var init_login = __esm({
|
|
|
1246
1386
|
});
|
|
1247
1387
|
|
|
1248
1388
|
// src/index.ts
|
|
1249
|
-
import { Command as
|
|
1389
|
+
import { Command as Command20 } from "commander";
|
|
1250
1390
|
import chalk13 from "chalk";
|
|
1251
1391
|
|
|
1252
1392
|
// src/lib/brand.ts
|
|
@@ -3295,9 +3435,687 @@ function installSkill(skillPath, content, force) {
|
|
|
3295
3435
|
writeFileSync(skillFile, content, "utf-8");
|
|
3296
3436
|
}
|
|
3297
3437
|
|
|
3438
|
+
// src/commands/tts.ts
|
|
3439
|
+
init_api();
|
|
3440
|
+
init_output();
|
|
3441
|
+
init_types();
|
|
3442
|
+
import { Command as Command15 } from "commander";
|
|
3443
|
+
import ora8 from "ora";
|
|
3444
|
+
import { writeFile as writeFile2 } from "fs/promises";
|
|
3445
|
+
var generateCommand = new Command15("generate").description("Generate speech from text").requiredOption("-t, --text <text>", "Text to convert to speech").requiredOption("-o, --output <path>", "Output file path").option("-v, --voice <voice>", "Voice name or ID (e.g., Kore, Rachel, alloy)").option("-p, --provider <provider>", "Provider: gemini, elevenlabs, openai").option("-m, --model <model>", "Model (provider-specific)").option("-s, --speed <speed>", "Speech speed 0.25-4.0 (default: 1.0)").option("-f, --format <format>", "Output format: human, json, quiet", "human").action(async (options) => {
|
|
3446
|
+
const format = options.format;
|
|
3447
|
+
const spinner = format === "human" ? ora8("Generating speech...").start() : null;
|
|
3448
|
+
let speed;
|
|
3449
|
+
if (options.speed) {
|
|
3450
|
+
speed = parseFloat(options.speed);
|
|
3451
|
+
if (isNaN(speed) || speed < 0.25 || speed > 4) {
|
|
3452
|
+
spinner?.stop();
|
|
3453
|
+
error("Speed must be between 0.25 and 4.0");
|
|
3454
|
+
process.exit(EXIT_CODES.INVALID_INPUT);
|
|
3455
|
+
}
|
|
3456
|
+
}
|
|
3457
|
+
try {
|
|
3458
|
+
const result = await generateSpeech({
|
|
3459
|
+
text: options.text,
|
|
3460
|
+
options: {
|
|
3461
|
+
provider: options.provider,
|
|
3462
|
+
voice: options.voice,
|
|
3463
|
+
model: options.model,
|
|
3464
|
+
speed
|
|
3465
|
+
}
|
|
3466
|
+
});
|
|
3467
|
+
spinner?.stop();
|
|
3468
|
+
const outputPath = options.output.endsWith(`.${result.format}`) ? options.output : `${options.output}.${result.format}`;
|
|
3469
|
+
await writeFile2(outputPath, result.audioData);
|
|
3470
|
+
if (format === "json") {
|
|
3471
|
+
printJson({
|
|
3472
|
+
status: "completed",
|
|
3473
|
+
output: outputPath,
|
|
3474
|
+
duration: result.duration,
|
|
3475
|
+
cost: result.cost,
|
|
3476
|
+
provider: result.provider,
|
|
3477
|
+
format: result.format
|
|
3478
|
+
});
|
|
3479
|
+
return;
|
|
3480
|
+
}
|
|
3481
|
+
if (format === "quiet") {
|
|
3482
|
+
console.log(outputPath);
|
|
3483
|
+
return;
|
|
3484
|
+
}
|
|
3485
|
+
success(`Saved to: ${outputPath}`);
|
|
3486
|
+
info(`Duration: ${result.duration.toFixed(2)}s`);
|
|
3487
|
+
info(`Provider: ${result.provider}`);
|
|
3488
|
+
info(`Cost: $${result.cost.toFixed(6)}`);
|
|
3489
|
+
} catch (err) {
|
|
3490
|
+
spinner?.stop();
|
|
3491
|
+
error(err instanceof Error ? err.message : "Unknown error");
|
|
3492
|
+
process.exit(EXIT_CODES.GENERAL_ERROR);
|
|
3493
|
+
}
|
|
3494
|
+
});
|
|
3495
|
+
var voicesCommand = new Command15("voices").description("List available voices").option("-p, --provider <provider>", "Filter by provider: gemini, elevenlabs, openai").option("-f, --format <format>", "Output format: human, json", "human").action(async (options) => {
|
|
3496
|
+
const spinner = options.format === "human" ? ora8("Fetching voices...").start() : null;
|
|
3497
|
+
try {
|
|
3498
|
+
const result = await getVoices();
|
|
3499
|
+
spinner?.stop();
|
|
3500
|
+
if (options.format === "json") {
|
|
3501
|
+
if (options.provider) {
|
|
3502
|
+
const providerVoices = result.voices[options.provider];
|
|
3503
|
+
printJson(providerVoices || []);
|
|
3504
|
+
} else {
|
|
3505
|
+
printJson(result.voices);
|
|
3506
|
+
}
|
|
3507
|
+
return;
|
|
3508
|
+
}
|
|
3509
|
+
const providers = options.provider ? [options.provider] : ["gemini", "elevenlabs", "openai"];
|
|
3510
|
+
for (const provider of providers) {
|
|
3511
|
+
const voices = result.voices[provider];
|
|
3512
|
+
if (!voices || voices.length === 0) continue;
|
|
3513
|
+
console.log();
|
|
3514
|
+
console.log(`${provider.toUpperCase()} Voices:`);
|
|
3515
|
+
console.log("-".repeat(50));
|
|
3516
|
+
for (const voice of voices) {
|
|
3517
|
+
console.log(` ${voice.name} (${voice.id})`);
|
|
3518
|
+
console.log(` ${voice.description}`);
|
|
3519
|
+
}
|
|
3520
|
+
}
|
|
3521
|
+
} catch (err) {
|
|
3522
|
+
spinner?.stop();
|
|
3523
|
+
error(err instanceof Error ? err.message : "Unknown error");
|
|
3524
|
+
process.exit(EXIT_CODES.GENERAL_ERROR);
|
|
3525
|
+
}
|
|
3526
|
+
});
|
|
3527
|
+
var ttsCommand = new Command15("tts").description("Text-to-speech commands").addCommand(generateCommand).addCommand(voicesCommand);
|
|
3528
|
+
|
|
3529
|
+
// src/commands/music.ts
|
|
3530
|
+
init_api();
|
|
3531
|
+
init_output();
|
|
3532
|
+
init_types();
|
|
3533
|
+
import { Command as Command16 } from "commander";
|
|
3534
|
+
import ora9 from "ora";
|
|
3535
|
+
import { writeFile as writeFile3 } from "fs/promises";
|
|
3536
|
+
function outputResult(result, format) {
|
|
3537
|
+
if (format === "json") {
|
|
3538
|
+
printJson(result);
|
|
3539
|
+
return;
|
|
3540
|
+
}
|
|
3541
|
+
if (format === "quiet") {
|
|
3542
|
+
if (result.audioUrl) {
|
|
3543
|
+
console.log(result.audioUrl);
|
|
3544
|
+
} else {
|
|
3545
|
+
console.log(result.requestId);
|
|
3546
|
+
}
|
|
3547
|
+
return;
|
|
3548
|
+
}
|
|
3549
|
+
info(`Request ID: ${result.requestId}`);
|
|
3550
|
+
info(`Status: ${result.status}`);
|
|
3551
|
+
if (result.duration) {
|
|
3552
|
+
info(`Duration: ${result.duration}s`);
|
|
3553
|
+
}
|
|
3554
|
+
if (result.audioUrl) {
|
|
3555
|
+
success(`Audio URL: ${result.audioUrl}`);
|
|
3556
|
+
}
|
|
3557
|
+
if (result.cost !== void 0) {
|
|
3558
|
+
info(`Cost: $${result.cost.toFixed(4)}`);
|
|
3559
|
+
}
|
|
3560
|
+
if (result.error) {
|
|
3561
|
+
error(`Error: ${result.error}`);
|
|
3562
|
+
}
|
|
3563
|
+
}
|
|
3564
|
+
async function downloadFile(url, outputPath) {
|
|
3565
|
+
if (url.startsWith("data:")) {
|
|
3566
|
+
const matches = url.match(/^data:[^;]+;base64,(.+)$/);
|
|
3567
|
+
if (!matches) {
|
|
3568
|
+
throw new Error("Invalid data URL format");
|
|
3569
|
+
}
|
|
3570
|
+
const buffer2 = Buffer.from(matches[1], "base64");
|
|
3571
|
+
await writeFile3(outputPath, buffer2);
|
|
3572
|
+
return;
|
|
3573
|
+
}
|
|
3574
|
+
const response = await fetch(url);
|
|
3575
|
+
if (!response.ok) {
|
|
3576
|
+
throw new Error(`Failed to download: ${response.status}`);
|
|
3577
|
+
}
|
|
3578
|
+
const buffer = await response.arrayBuffer();
|
|
3579
|
+
await writeFile3(outputPath, Buffer.from(buffer));
|
|
3580
|
+
}
|
|
3581
|
+
var generateCommand2 = new Command16("generate").description("Generate music from a text prompt").requiredOption("-p, --prompt <text>", "Music description").option("-d, --duration <seconds>", "Duration in seconds (3-30)", "30").option("-s, --style <style>", "Style preset").option("--provider <provider>", "Provider (elevenlabs, suno)").option("-o, --output <path>", "Output file path").option("--no-wait", "Do not wait for completion").option("-f, --format <format>", "Output format: human, json, quiet", "human").action(async (options) => {
|
|
3582
|
+
const duration = parseInt(options.duration, 10);
|
|
3583
|
+
if (isNaN(duration) || duration < 3 || duration > 30) {
|
|
3584
|
+
error("Duration must be between 3 and 30 seconds");
|
|
3585
|
+
process.exit(EXIT_CODES.INVALID_INPUT);
|
|
3586
|
+
}
|
|
3587
|
+
const format = options.format;
|
|
3588
|
+
const spinner = format === "human" ? ora9("Generating music...").start() : null;
|
|
3589
|
+
try {
|
|
3590
|
+
const result = await generateMusic({
|
|
3591
|
+
prompt: options.prompt,
|
|
3592
|
+
duration,
|
|
3593
|
+
options: {
|
|
3594
|
+
provider: options.provider,
|
|
3595
|
+
style: options.style
|
|
3596
|
+
}
|
|
3597
|
+
});
|
|
3598
|
+
if (!options.wait) {
|
|
3599
|
+
spinner?.stop();
|
|
3600
|
+
outputResult(result, format);
|
|
3601
|
+
return;
|
|
3602
|
+
}
|
|
3603
|
+
let finalResult = result;
|
|
3604
|
+
if (result.status !== "completed" && result.status !== "failed") {
|
|
3605
|
+
if (spinner) spinner.text = `Processing (ID: ${result.requestId})...`;
|
|
3606
|
+
finalResult = await pollForCompletion(
|
|
3607
|
+
() => checkMusicStatus(result.requestId),
|
|
3608
|
+
60,
|
|
3609
|
+
2e3
|
|
3610
|
+
);
|
|
3611
|
+
}
|
|
3612
|
+
spinner?.stop();
|
|
3613
|
+
if (finalResult.status === "failed") {
|
|
3614
|
+
error(finalResult.error || "Music generation failed");
|
|
3615
|
+
process.exit(EXIT_CODES.GENERAL_ERROR);
|
|
3616
|
+
}
|
|
3617
|
+
outputResult(finalResult, format);
|
|
3618
|
+
if (options.output && finalResult.audioUrl) {
|
|
3619
|
+
const downloadSpinner = format === "human" ? ora9("Downloading...").start() : null;
|
|
3620
|
+
try {
|
|
3621
|
+
await downloadFile(finalResult.audioUrl, options.output);
|
|
3622
|
+
downloadSpinner?.stop();
|
|
3623
|
+
if (format === "human") {
|
|
3624
|
+
success(`Saved to: ${options.output}`);
|
|
3625
|
+
}
|
|
3626
|
+
} catch (err) {
|
|
3627
|
+
downloadSpinner?.stop();
|
|
3628
|
+
warn(`Failed to download: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
3629
|
+
}
|
|
3630
|
+
}
|
|
3631
|
+
} catch (err) {
|
|
3632
|
+
spinner?.stop();
|
|
3633
|
+
error(err instanceof Error ? err.message : "Unknown error");
|
|
3634
|
+
process.exit(EXIT_CODES.GENERAL_ERROR);
|
|
3635
|
+
}
|
|
3636
|
+
});
|
|
3637
|
+
var statusCommand = new Command16("status").description("Check status of a music generation request").argument("<id>", "Request ID").option("-f, --format <format>", "Output format: human, json, quiet", "human").action(async (id, options) => {
|
|
3638
|
+
const spinner = options.format === "human" ? ora9("Checking status...").start() : null;
|
|
3639
|
+
try {
|
|
3640
|
+
const result = await checkMusicStatus(id);
|
|
3641
|
+
spinner?.stop();
|
|
3642
|
+
outputResult(result, options.format);
|
|
3643
|
+
} catch (err) {
|
|
3644
|
+
spinner?.stop();
|
|
3645
|
+
error(err instanceof Error ? err.message : "Unknown error");
|
|
3646
|
+
process.exit(EXIT_CODES.GENERAL_ERROR);
|
|
3647
|
+
}
|
|
3648
|
+
});
|
|
3649
|
+
var musicCommand = new Command16("music").description("Music generation commands").addCommand(generateCommand2).addCommand(statusCommand);
|
|
3650
|
+
|
|
3651
|
+
// src/commands/mix.ts
|
|
3652
|
+
init_api();
|
|
3653
|
+
init_output();
|
|
3654
|
+
init_types();
|
|
3655
|
+
import { Command as Command17 } from "commander";
|
|
3656
|
+
import ora10 from "ora";
|
|
3657
|
+
import { writeFile as writeFile4 } from "fs/promises";
|
|
3658
|
+
function outputResult2(result, format) {
|
|
3659
|
+
if (format === "json") {
|
|
3660
|
+
printJson(result);
|
|
3661
|
+
return;
|
|
3662
|
+
}
|
|
3663
|
+
if (format === "quiet") {
|
|
3664
|
+
if (result.outputUrl) {
|
|
3665
|
+
console.log(result.outputUrl);
|
|
3666
|
+
} else {
|
|
3667
|
+
console.log(result.requestId);
|
|
3668
|
+
}
|
|
3669
|
+
return;
|
|
3670
|
+
}
|
|
3671
|
+
info(`Request ID: ${result.requestId}`);
|
|
3672
|
+
info(`Status: ${result.status}`);
|
|
3673
|
+
if (result.duration) {
|
|
3674
|
+
info(`Duration: ${result.duration}s`);
|
|
3675
|
+
}
|
|
3676
|
+
if (result.outputUrl) {
|
|
3677
|
+
success(`Output URL: ${result.outputUrl}`);
|
|
3678
|
+
}
|
|
3679
|
+
if (result.cost !== void 0) {
|
|
3680
|
+
info(`Cost: $${result.cost.toFixed(4)}`);
|
|
3681
|
+
}
|
|
3682
|
+
if (result.error) {
|
|
3683
|
+
error(`Error: ${result.error}`);
|
|
3684
|
+
}
|
|
3685
|
+
}
|
|
3686
|
+
async function downloadFile2(url, outputPath) {
|
|
3687
|
+
const response = await fetch(url);
|
|
3688
|
+
if (!response.ok) {
|
|
3689
|
+
throw new Error(`Failed to download: ${response.status}`);
|
|
3690
|
+
}
|
|
3691
|
+
const buffer = await response.arrayBuffer();
|
|
3692
|
+
await writeFile4(outputPath, Buffer.from(buffer));
|
|
3693
|
+
}
|
|
3694
|
+
var mixCommand = new Command17("create").description("Mix audio tracks into a video").requiredOption("--video <url>", "Input video file/URL").option("--music <url>", "Background music file/URL").option("--voice <url>", "Voiceover file/URL").option("--music-volume <percent>", "Music volume 0-100", "50").option("--voice-volume <percent>", "Voice volume 0-100", "100").option("-o, --output <path>", "Output file path").option("--no-wait", "Do not wait for completion").option("-f, --format <format>", "Output format: human, json, quiet", "human").action(async (options) => {
|
|
3695
|
+
if (!options.music && !options.voice) {
|
|
3696
|
+
error("At least one of --music or --voice must be provided");
|
|
3697
|
+
process.exit(EXIT_CODES.INVALID_INPUT);
|
|
3698
|
+
}
|
|
3699
|
+
const musicVolume = parseInt(options.musicVolume, 10) / 100;
|
|
3700
|
+
const voiceVolume = parseInt(options.voiceVolume, 10) / 100;
|
|
3701
|
+
if (isNaN(musicVolume) || musicVolume < 0 || musicVolume > 1) {
|
|
3702
|
+
error("Music volume must be between 0 and 100");
|
|
3703
|
+
process.exit(EXIT_CODES.INVALID_INPUT);
|
|
3704
|
+
}
|
|
3705
|
+
if (isNaN(voiceVolume) || voiceVolume < 0 || voiceVolume > 2) {
|
|
3706
|
+
error("Voice volume must be between 0 and 200");
|
|
3707
|
+
process.exit(EXIT_CODES.INVALID_INPUT);
|
|
3708
|
+
}
|
|
3709
|
+
const format = options.format;
|
|
3710
|
+
const spinner = format === "human" ? ora10("Mixing audio...").start() : null;
|
|
3711
|
+
const inputs = [{ url: options.video, role: "video" }];
|
|
3712
|
+
if (options.music) {
|
|
3713
|
+
inputs.push({ url: options.music, role: "background", volume: musicVolume * 5 });
|
|
3714
|
+
}
|
|
3715
|
+
if (options.voice) {
|
|
3716
|
+
inputs.push({ url: options.voice, role: "voice", volume: voiceVolume * 2 });
|
|
3717
|
+
}
|
|
3718
|
+
try {
|
|
3719
|
+
const result = await mixAudio({
|
|
3720
|
+
operation: "add-to-video",
|
|
3721
|
+
inputs,
|
|
3722
|
+
options: {
|
|
3723
|
+
musicVolume,
|
|
3724
|
+
voiceVolume
|
|
3725
|
+
}
|
|
3726
|
+
});
|
|
3727
|
+
if (!options.wait) {
|
|
3728
|
+
spinner?.stop();
|
|
3729
|
+
outputResult2(result, format);
|
|
3730
|
+
return;
|
|
3731
|
+
}
|
|
3732
|
+
if (spinner) spinner.text = `Processing (ID: ${result.requestId})...`;
|
|
3733
|
+
const finalResult = await pollForCompletion(
|
|
3734
|
+
() => checkMixStatus(result.requestId),
|
|
3735
|
+
120,
|
|
3736
|
+
3e3
|
|
3737
|
+
);
|
|
3738
|
+
spinner?.stop();
|
|
3739
|
+
if (finalResult.status === "failed") {
|
|
3740
|
+
error(finalResult.error || "Audio mixing failed");
|
|
3741
|
+
process.exit(EXIT_CODES.GENERAL_ERROR);
|
|
3742
|
+
}
|
|
3743
|
+
outputResult2(finalResult, format);
|
|
3744
|
+
if (options.output && finalResult.outputUrl) {
|
|
3745
|
+
const downloadSpinner = format === "human" ? ora10("Downloading...").start() : null;
|
|
3746
|
+
try {
|
|
3747
|
+
await downloadFile2(finalResult.outputUrl, options.output);
|
|
3748
|
+
downloadSpinner?.stop();
|
|
3749
|
+
if (format === "human") {
|
|
3750
|
+
success(`Saved to: ${options.output}`);
|
|
3751
|
+
}
|
|
3752
|
+
} catch (err) {
|
|
3753
|
+
downloadSpinner?.stop();
|
|
3754
|
+
warn(`Failed to download: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
3755
|
+
}
|
|
3756
|
+
}
|
|
3757
|
+
} catch (err) {
|
|
3758
|
+
spinner?.stop();
|
|
3759
|
+
error(err instanceof Error ? err.message : "Unknown error");
|
|
3760
|
+
process.exit(EXIT_CODES.GENERAL_ERROR);
|
|
3761
|
+
}
|
|
3762
|
+
});
|
|
3763
|
+
var statusCommand2 = new Command17("status").description("Check status of an audio mix request").argument("<id>", "Request ID").option("-f, --format <format>", "Output format: human, json, quiet", "human").action(async (id, options) => {
|
|
3764
|
+
const spinner = options.format === "human" ? ora10("Checking status...").start() : null;
|
|
3765
|
+
try {
|
|
3766
|
+
const result = await checkMixStatus(id);
|
|
3767
|
+
spinner?.stop();
|
|
3768
|
+
outputResult2(result, options.format);
|
|
3769
|
+
} catch (err) {
|
|
3770
|
+
spinner?.stop();
|
|
3771
|
+
error(err instanceof Error ? err.message : "Unknown error");
|
|
3772
|
+
process.exit(EXIT_CODES.GENERAL_ERROR);
|
|
3773
|
+
}
|
|
3774
|
+
});
|
|
3775
|
+
var mixAudioCommand = new Command17("mix").description("Audio mixing commands").addCommand(mixCommand).addCommand(statusCommand2);
|
|
3776
|
+
|
|
3777
|
+
// src/commands/image.ts
|
|
3778
|
+
init_api();
|
|
3779
|
+
init_output();
|
|
3780
|
+
init_types();
|
|
3781
|
+
import { Command as Command18 } from "commander";
|
|
3782
|
+
import ora11 from "ora";
|
|
3783
|
+
var searchCommand = new Command18("search").description("Search for images").requiredOption("-q, --query <query>", "Search query").option("-n, --max-results <number>", "Maximum number of results (default: 10)").option("-s, --size <size>", "Image size: small, medium, large, any", "large").option("--safe-search", "Enable safe search (default: true)", true).option("--no-safe-search", "Disable safe search").option("-f, --format <format>", "Output format: human, json, quiet", "human").action(async (options) => {
|
|
3784
|
+
const format = options.format;
|
|
3785
|
+
const spinner = format === "human" ? ora11("Searching for images...").start() : null;
|
|
3786
|
+
let maxResults;
|
|
3787
|
+
if (options.maxResults) {
|
|
3788
|
+
maxResults = parseInt(options.maxResults, 10);
|
|
3789
|
+
if (isNaN(maxResults) || maxResults < 1) {
|
|
3790
|
+
spinner?.stop();
|
|
3791
|
+
error("Max results must be a positive number");
|
|
3792
|
+
process.exit(EXIT_CODES.INVALID_INPUT);
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3795
|
+
try {
|
|
3796
|
+
const result = await searchImages({
|
|
3797
|
+
query: options.query,
|
|
3798
|
+
options: {
|
|
3799
|
+
maxResults: maxResults || 10,
|
|
3800
|
+
size: options.size,
|
|
3801
|
+
safeSearch: options.safeSearch
|
|
3802
|
+
}
|
|
3803
|
+
});
|
|
3804
|
+
spinner?.stop();
|
|
3805
|
+
if (!result.success) {
|
|
3806
|
+
error("Search failed");
|
|
3807
|
+
process.exit(EXIT_CODES.GENERAL_ERROR);
|
|
3808
|
+
}
|
|
3809
|
+
const allImages = result.data.results.flatMap(
|
|
3810
|
+
(providerResult) => providerResult.results.map((img) => ({
|
|
3811
|
+
...img,
|
|
3812
|
+
provider: providerResult.providerName
|
|
3813
|
+
}))
|
|
3814
|
+
);
|
|
3815
|
+
if (format === "json") {
|
|
3816
|
+
printJson({
|
|
3817
|
+
success: true,
|
|
3818
|
+
query: options.query,
|
|
3819
|
+
totalResults: allImages.length,
|
|
3820
|
+
totalCost: result.data.totalCost,
|
|
3821
|
+
images: allImages
|
|
3822
|
+
});
|
|
3823
|
+
return;
|
|
3824
|
+
}
|
|
3825
|
+
if (format === "quiet") {
|
|
3826
|
+
for (const img of allImages) {
|
|
3827
|
+
console.log(img.url);
|
|
3828
|
+
}
|
|
3829
|
+
return;
|
|
3830
|
+
}
|
|
3831
|
+
if (allImages.length === 0) {
|
|
3832
|
+
info("No images found");
|
|
3833
|
+
return;
|
|
3834
|
+
}
|
|
3835
|
+
success(`Found ${allImages.length} images for "${options.query}"`);
|
|
3836
|
+
console.log();
|
|
3837
|
+
for (let i = 0; i < allImages.length; i++) {
|
|
3838
|
+
const img = allImages[i];
|
|
3839
|
+
console.log(`[${i + 1}] ${img.title || "Untitled"}`);
|
|
3840
|
+
console.log(` URL: ${img.url}`);
|
|
3841
|
+
console.log(` Size: ${img.width}x${img.height}`);
|
|
3842
|
+
if (img.author) {
|
|
3843
|
+
console.log(` Author: ${img.author}`);
|
|
3844
|
+
}
|
|
3845
|
+
console.log(` Provider: ${img.provider}`);
|
|
3846
|
+
console.log();
|
|
3847
|
+
}
|
|
3848
|
+
info(`Total cost: $${result.data.totalCost.toFixed(4)}`);
|
|
3849
|
+
} catch (err) {
|
|
3850
|
+
spinner?.stop();
|
|
3851
|
+
error(err instanceof Error ? err.message : "Unknown error");
|
|
3852
|
+
process.exit(EXIT_CODES.GENERAL_ERROR);
|
|
3853
|
+
}
|
|
3854
|
+
});
|
|
3855
|
+
var imageCommand = new Command18("image").description("Image search commands").addCommand(searchCommand);
|
|
3856
|
+
|
|
3857
|
+
// src/commands/video.ts
|
|
3858
|
+
init_api();
|
|
3859
|
+
init_output();
|
|
3860
|
+
init_types();
|
|
3861
|
+
import { Command as Command19 } from "commander";
|
|
3862
|
+
import ora12 from "ora";
|
|
3863
|
+
import { mkdir, writeFile as writeFile5, readFile as readFile2 } from "fs/promises";
|
|
3864
|
+
import { join as join2 } from "path";
|
|
3865
|
+
async function downloadFile3(url, outputPath) {
|
|
3866
|
+
if (url.startsWith("data:")) {
|
|
3867
|
+
const matches = url.match(/^data:[^;]+;base64,(.+)$/);
|
|
3868
|
+
if (!matches) {
|
|
3869
|
+
throw new Error("Invalid data URL format");
|
|
3870
|
+
}
|
|
3871
|
+
const buffer2 = Buffer.from(matches[1], "base64");
|
|
3872
|
+
await writeFile5(outputPath, buffer2);
|
|
3873
|
+
return;
|
|
3874
|
+
}
|
|
3875
|
+
const response = await fetch(url);
|
|
3876
|
+
if (!response.ok) {
|
|
3877
|
+
throw new Error(`Failed to download: ${response.status}`);
|
|
3878
|
+
}
|
|
3879
|
+
const buffer = await response.arrayBuffer();
|
|
3880
|
+
await writeFile5(outputPath, Buffer.from(buffer));
|
|
3881
|
+
}
|
|
3882
|
+
function getExtension(url) {
|
|
3883
|
+
try {
|
|
3884
|
+
const urlObj = new URL(url);
|
|
3885
|
+
const pathname = urlObj.pathname;
|
|
3886
|
+
const ext = pathname.split(".").pop()?.toLowerCase();
|
|
3887
|
+
if (ext && ["jpg", "jpeg", "png", "gif", "webp"].includes(ext)) {
|
|
3888
|
+
return ext;
|
|
3889
|
+
}
|
|
3890
|
+
} catch {
|
|
3891
|
+
}
|
|
3892
|
+
return "jpg";
|
|
3893
|
+
}
|
|
3894
|
+
var createCommand2 = new Command19("create").description("Create video assets (voiceover, music, images)").option("-s, --script <text>", "Narration script text").option("--script-file <path>", "Path to script file").option("-t, --topic <text>", "Topic for image search (inferred from script if not provided)").option("-d, --duration <seconds>", "Target duration (auto-calculated from script if not set)").option("-v, --voice <name>", "TTS voice (Kore, Puck, Rachel, alloy)", "Kore").option("-m, --music-prompt <text>", "Music description (auto-generated if not provided)").option("-n, --num-images <number>", "Number of images to search/download", "5").option("-o, --output <dir>", "Output directory", "./public").option("-f, --format <format>", "Output format: human, json, quiet", "human").action(async (options) => {
|
|
3895
|
+
const format = options.format;
|
|
3896
|
+
const spinner = format === "human" ? ora12("Initializing...").start() : null;
|
|
3897
|
+
try {
|
|
3898
|
+
let script = options.script;
|
|
3899
|
+
if (options.scriptFile) {
|
|
3900
|
+
try {
|
|
3901
|
+
script = await readFile2(options.scriptFile, "utf-8");
|
|
3902
|
+
} catch (err) {
|
|
3903
|
+
spinner?.stop();
|
|
3904
|
+
error(`Failed to read script file: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
3905
|
+
process.exit(EXIT_CODES.INVALID_INPUT);
|
|
3906
|
+
}
|
|
3907
|
+
}
|
|
3908
|
+
if (!script || script.trim().length === 0) {
|
|
3909
|
+
spinner?.stop();
|
|
3910
|
+
error("Either --script or --script-file is required");
|
|
3911
|
+
process.exit(EXIT_CODES.INVALID_INPUT);
|
|
3912
|
+
}
|
|
3913
|
+
script = script.trim();
|
|
3914
|
+
const topic = options.topic || script.split(".")[0].slice(0, 50);
|
|
3915
|
+
const numImages = parseInt(options.numImages, 10);
|
|
3916
|
+
if (isNaN(numImages) || numImages < 1 || numImages > 20) {
|
|
3917
|
+
spinner?.stop();
|
|
3918
|
+
error("Number of images must be between 1 and 20");
|
|
3919
|
+
process.exit(EXIT_CODES.INVALID_INPUT);
|
|
3920
|
+
}
|
|
3921
|
+
const audioDir = join2(options.output, "audio");
|
|
3922
|
+
const imagesDir = join2(options.output, "images");
|
|
3923
|
+
if (spinner) spinner.text = "Creating directories...";
|
|
3924
|
+
await mkdir(audioDir, { recursive: true });
|
|
3925
|
+
await mkdir(imagesDir, { recursive: true });
|
|
3926
|
+
let totalCost = 0;
|
|
3927
|
+
if (spinner) spinner.text = "Generating voiceover...";
|
|
3928
|
+
const ttsResult = await generateSpeech({
|
|
3929
|
+
text: script,
|
|
3930
|
+
options: { voice: options.voice }
|
|
3931
|
+
});
|
|
3932
|
+
const voiceoverPath = join2(audioDir, `voiceover.${ttsResult.format}`);
|
|
3933
|
+
await writeFile5(voiceoverPath, ttsResult.audioData);
|
|
3934
|
+
totalCost += ttsResult.cost;
|
|
3935
|
+
const voiceoverInfo = {
|
|
3936
|
+
path: `audio/voiceover.${ttsResult.format}`,
|
|
3937
|
+
duration: ttsResult.duration,
|
|
3938
|
+
voice: options.voice,
|
|
3939
|
+
provider: ttsResult.provider,
|
|
3940
|
+
cost: ttsResult.cost
|
|
3941
|
+
};
|
|
3942
|
+
if (format === "human") {
|
|
3943
|
+
spinner?.stop();
|
|
3944
|
+
success(`Voiceover: ${voiceoverPath} (${ttsResult.duration.toFixed(1)}s)`);
|
|
3945
|
+
spinner?.start();
|
|
3946
|
+
}
|
|
3947
|
+
const musicDuration = Math.min(30, Math.ceil(ttsResult.duration) + 5);
|
|
3948
|
+
const musicPrompt = options.musicPrompt || "uplifting background music, positive energy";
|
|
3949
|
+
if (spinner) spinner.text = "Generating music...";
|
|
3950
|
+
let musicResult = await generateMusic({
|
|
3951
|
+
prompt: musicPrompt,
|
|
3952
|
+
duration: musicDuration
|
|
3953
|
+
});
|
|
3954
|
+
if (musicResult.status !== "completed" && musicResult.status !== "failed") {
|
|
3955
|
+
if (spinner) spinner.text = `Processing music (ID: ${musicResult.requestId})...`;
|
|
3956
|
+
musicResult = await pollForCompletion(
|
|
3957
|
+
() => checkMusicStatus(musicResult.requestId),
|
|
3958
|
+
60,
|
|
3959
|
+
2e3
|
|
3960
|
+
);
|
|
3961
|
+
}
|
|
3962
|
+
if (musicResult.status === "failed") {
|
|
3963
|
+
spinner?.stop();
|
|
3964
|
+
error(`Music generation failed: ${musicResult.error || "Unknown error"}`);
|
|
3965
|
+
process.exit(EXIT_CODES.GENERAL_ERROR);
|
|
3966
|
+
}
|
|
3967
|
+
const musicPath = join2(audioDir, "music.mp3");
|
|
3968
|
+
if (musicResult.audioUrl) {
|
|
3969
|
+
await downloadFile3(musicResult.audioUrl, musicPath);
|
|
3970
|
+
}
|
|
3971
|
+
totalCost += musicResult.cost || 0;
|
|
3972
|
+
const musicInfo = {
|
|
3973
|
+
path: "audio/music.mp3",
|
|
3974
|
+
duration: musicResult.duration || musicDuration,
|
|
3975
|
+
prompt: musicPrompt,
|
|
3976
|
+
cost: musicResult.cost || 0
|
|
3977
|
+
};
|
|
3978
|
+
if (format === "human") {
|
|
3979
|
+
spinner?.stop();
|
|
3980
|
+
success(`Music: ${musicPath} (${musicInfo.duration}s)`);
|
|
3981
|
+
spinner?.start();
|
|
3982
|
+
}
|
|
3983
|
+
if (spinner) spinner.text = "Searching for images...";
|
|
3984
|
+
const imageResults = await searchImages({
|
|
3985
|
+
query: topic,
|
|
3986
|
+
options: {
|
|
3987
|
+
maxResults: numImages,
|
|
3988
|
+
size: "large",
|
|
3989
|
+
safeSearch: true
|
|
3990
|
+
}
|
|
3991
|
+
});
|
|
3992
|
+
const allImages = imageResults.data.results.flatMap(
|
|
3993
|
+
(providerResult) => providerResult.results.map((img) => ({
|
|
3994
|
+
...img,
|
|
3995
|
+
provider: providerResult.providerName
|
|
3996
|
+
}))
|
|
3997
|
+
);
|
|
3998
|
+
totalCost += imageResults.data.totalCost;
|
|
3999
|
+
const downloadedImages = [];
|
|
4000
|
+
for (let i = 0; i < Math.min(allImages.length, numImages); i++) {
|
|
4001
|
+
const img = allImages[i];
|
|
4002
|
+
const ext = getExtension(img.url);
|
|
4003
|
+
const filename = `scene-${i + 1}.${ext}`;
|
|
4004
|
+
const imagePath = join2(imagesDir, filename);
|
|
4005
|
+
if (spinner) spinner.text = `Downloading image ${i + 1}/${Math.min(allImages.length, numImages)}...`;
|
|
4006
|
+
try {
|
|
4007
|
+
await downloadFile3(img.url, imagePath);
|
|
4008
|
+
downloadedImages.push({
|
|
4009
|
+
path: `images/${filename}`,
|
|
4010
|
+
url: img.url,
|
|
4011
|
+
width: img.width,
|
|
4012
|
+
height: img.height,
|
|
4013
|
+
query: topic
|
|
4014
|
+
});
|
|
4015
|
+
} catch (err) {
|
|
4016
|
+
if (format === "human") {
|
|
4017
|
+
spinner?.stop();
|
|
4018
|
+
warn(`Failed to download image ${i + 1}: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
4019
|
+
spinner?.start();
|
|
4020
|
+
}
|
|
4021
|
+
}
|
|
4022
|
+
}
|
|
4023
|
+
if (format === "human") {
|
|
4024
|
+
spinner?.stop();
|
|
4025
|
+
success(`Images: Downloaded ${downloadedImages.length} images to ${imagesDir}`);
|
|
4026
|
+
spinner?.start();
|
|
4027
|
+
}
|
|
4028
|
+
if (spinner) spinner.text = "Writing manifest...";
|
|
4029
|
+
const manifest = {
|
|
4030
|
+
topic,
|
|
4031
|
+
script,
|
|
4032
|
+
voiceover: voiceoverInfo,
|
|
4033
|
+
music: musicInfo,
|
|
4034
|
+
images: downloadedImages,
|
|
4035
|
+
totalCost,
|
|
4036
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4037
|
+
};
|
|
4038
|
+
const manifestPath = join2(options.output, "video-manifest.json");
|
|
4039
|
+
await writeFile5(manifestPath, JSON.stringify(manifest, null, 2));
|
|
4040
|
+
spinner?.stop();
|
|
4041
|
+
if (format === "json") {
|
|
4042
|
+
printJson(manifest);
|
|
4043
|
+
return;
|
|
4044
|
+
}
|
|
4045
|
+
if (format === "quiet") {
|
|
4046
|
+
console.log(manifestPath);
|
|
4047
|
+
return;
|
|
4048
|
+
}
|
|
4049
|
+
console.log();
|
|
4050
|
+
success("Video assets created successfully!");
|
|
4051
|
+
console.log();
|
|
4052
|
+
info(`Topic: ${topic}`);
|
|
4053
|
+
info(`Voiceover: ${voiceoverInfo.path} (${voiceoverInfo.duration.toFixed(1)}s, ${voiceoverInfo.voice})`);
|
|
4054
|
+
info(`Music: ${musicInfo.path} (${musicInfo.duration}s)`);
|
|
4055
|
+
info(`Images: ${downloadedImages.length} downloaded`);
|
|
4056
|
+
info(`Manifest: ${manifestPath}`);
|
|
4057
|
+
console.log();
|
|
4058
|
+
info(`Total cost: $${totalCost.toFixed(4)}`);
|
|
4059
|
+
console.log();
|
|
4060
|
+
info("Next steps:");
|
|
4061
|
+
info(" 1. Create Remotion project (see remotion-best-practices skill)");
|
|
4062
|
+
info(" 2. Use manifest data to configure video composition");
|
|
4063
|
+
info(" 3. Run: npm start (preview) / npm run render (build)");
|
|
4064
|
+
} catch (err) {
|
|
4065
|
+
spinner?.stop();
|
|
4066
|
+
error(err instanceof Error ? err.message : "Unknown error");
|
|
4067
|
+
process.exit(EXIT_CODES.GENERAL_ERROR);
|
|
4068
|
+
}
|
|
4069
|
+
});
|
|
4070
|
+
var searchCommand2 = new Command19("search").description("Search for stock videos").argument("<query>", "Search query").option("-n, --max-results <count>", "Maximum number of results", "10").option("-o, --orientation <type>", "Video orientation: landscape, portrait, square, any", "any").option("-l, --license <type>", "License type: free, premium, any", "any").option("-f, --format <format>", "Output format: human, json, quiet", "human").action(async (query, options) => {
|
|
4071
|
+
const { maxResults, orientation, license, format } = options;
|
|
4072
|
+
const spinner = format === "human" ? ora12("Searching for videos...").start() : null;
|
|
4073
|
+
try {
|
|
4074
|
+
const result = await searchVideos({
|
|
4075
|
+
query,
|
|
4076
|
+
options: {
|
|
4077
|
+
maxResults: parseInt(maxResults, 10),
|
|
4078
|
+
orientation,
|
|
4079
|
+
license
|
|
4080
|
+
}
|
|
4081
|
+
});
|
|
4082
|
+
spinner?.stop();
|
|
4083
|
+
const allVideos = result.data.results.flatMap((provider) => provider.results);
|
|
4084
|
+
if (format === "json") {
|
|
4085
|
+
printJson(result);
|
|
4086
|
+
return;
|
|
4087
|
+
}
|
|
4088
|
+
if (format === "quiet") {
|
|
4089
|
+
allVideos.forEach((video) => {
|
|
4090
|
+
console.log(video.previewUrl || video.thumbnailUrl);
|
|
4091
|
+
});
|
|
4092
|
+
return;
|
|
4093
|
+
}
|
|
4094
|
+
if (allVideos.length === 0) {
|
|
4095
|
+
info("No videos found");
|
|
4096
|
+
return;
|
|
4097
|
+
}
|
|
4098
|
+
success(`Found ${allVideos.length} videos for "${query}"`);
|
|
4099
|
+
console.log();
|
|
4100
|
+
allVideos.forEach((video, index) => {
|
|
4101
|
+
console.log(`[${index + 1}] ${video.title}`);
|
|
4102
|
+
console.log(` URL: ${video.previewUrl || video.thumbnailUrl}`);
|
|
4103
|
+
console.log(` Duration: ${video.duration}s | Size: ${video.width}x${video.height}`);
|
|
4104
|
+
console.log(` Provider: ${video.provider}`);
|
|
4105
|
+
console.log();
|
|
4106
|
+
});
|
|
4107
|
+
info(`Total cost: $${result.data.totalCost.toFixed(4)}`);
|
|
4108
|
+
} catch (err) {
|
|
4109
|
+
spinner?.stop();
|
|
4110
|
+
error(err instanceof Error ? err.message : "Unknown error");
|
|
4111
|
+
process.exit(EXIT_CODES.GENERAL_ERROR);
|
|
4112
|
+
}
|
|
4113
|
+
});
|
|
4114
|
+
var videoCommand = new Command19("video").description("Video asset generation commands").addCommand(createCommand2).addCommand(searchCommand2);
|
|
4115
|
+
|
|
3298
4116
|
// src/index.ts
|
|
3299
|
-
var VERSION = "0.1.
|
|
3300
|
-
var program = new
|
|
4117
|
+
var VERSION = "0.1.5";
|
|
4118
|
+
var program = new Command20();
|
|
3301
4119
|
var cmdName = brand.commands[0];
|
|
3302
4120
|
program.name(cmdName).description(brand.description).version(VERSION, "-v, --version", "Show version number").option("--debug", "Enable debug logging").option("--no-color", "Disable colored output").configureOutput({
|
|
3303
4121
|
outputError: (str, write) => {
|
|
@@ -3317,6 +4135,11 @@ program.addCommand(brandingCommand);
|
|
|
3317
4135
|
program.addCommand(ideasCommand);
|
|
3318
4136
|
program.addCommand(whoamiCommand);
|
|
3319
4137
|
program.addCommand(skillCommand);
|
|
4138
|
+
program.addCommand(ttsCommand);
|
|
4139
|
+
program.addCommand(musicCommand);
|
|
4140
|
+
program.addCommand(mixAudioCommand);
|
|
4141
|
+
program.addCommand(imageCommand);
|
|
4142
|
+
program.addCommand(videoCommand);
|
|
3320
4143
|
var deriveCommand = buildDeriveCommand();
|
|
3321
4144
|
if (deriveCommand.commands.length > 0) {
|
|
3322
4145
|
program.addCommand(deriveCommand);
|