quickmagic-cli 1.2.0 → 1.3.0
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/package.json +1 -1
- package/src/commands/elements.js +42 -0
- package/src/commands/generate.js +6 -0
- package/src/index.js +23 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "quickmagic-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Quick Magic CLI — generate AI images/videos, product photoshoots, virtual try-on, hook ad videos, subtitles and more via the Quick Magic REST API, with browser-based OAuth PKCE login (no API keys to manage). Ships with 8 Claude Agent Skills and an MCP server integration.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"quickmagic": "src/index.js",
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// commands/elements.js — thư viện element tái dùng: xem `@tag` để trỏ trong prompt video, và tạo mới.
|
|
2
|
+
// BE dịch `@tag` trong prompt thành "Image N (mô tả)" lúc gửi provider → giữ nhân vật/sản phẩm
|
|
3
|
+
// nhất quán qua nhiều cảnh. Nhớ đưa ảnh element vào `--image` kèm `--mode reference`.
|
|
4
|
+
const api = require('../api');
|
|
5
|
+
const { resolveMediaInput } = require('../media');
|
|
6
|
+
|
|
7
|
+
async function list(options) {
|
|
8
|
+
const limit = (options && options.limit) || 50;
|
|
9
|
+
const data = await api.call('GET', `/elements?limit=${encodeURIComponent(limit)}`);
|
|
10
|
+
const items = (data && data.elements) || [];
|
|
11
|
+
if (!items.length) {
|
|
12
|
+
console.log('Chưa có element nào. Tạo bằng: qm elements create --name "Áo dài đỏ" --image ./ao.jpg --desc "..."');
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const tag_w = Math.max(3, ...items.map((e) => `@${e.tag}`.length));
|
|
17
|
+
const name_w = Math.max(4, ...items.map((e) => (e.name || '').length));
|
|
18
|
+
console.log(`${'TAG'.padEnd(tag_w)} ${'TÊN'.padEnd(name_w)} MÔ TẢ`);
|
|
19
|
+
console.log('─'.repeat(tag_w + name_w + 30));
|
|
20
|
+
for (const e of items) {
|
|
21
|
+
const desc = (e.description || '').replace(/\s+/g, ' ');
|
|
22
|
+
const short = desc.length > 60 ? `${desc.slice(0, 57)}...` : desc;
|
|
23
|
+
console.log(`${`@${e.tag}`.padEnd(tag_w)} ${(e.name || '').padEnd(name_w)} ${short}`);
|
|
24
|
+
}
|
|
25
|
+
console.log(`\nDùng: qm generate video --prompt "cô gái mặc @${items[0].tag} đi dạo" --image <url-element> --mode reference --model seedance-2-0`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function create(options) {
|
|
29
|
+
// File local → tự upload presigned lên host Quick Magic (server chỉ nhận URL của mình).
|
|
30
|
+
const media_url = await resolveMediaInput(options.image);
|
|
31
|
+
const body = { name: options.name, media_url };
|
|
32
|
+
if (options.desc) body.description = options.desc;
|
|
33
|
+
if (options.category) body.category = options.category;
|
|
34
|
+
if (options.type) body.media_type = options.type;
|
|
35
|
+
|
|
36
|
+
const data = await api.call('POST', '/elements', { body });
|
|
37
|
+
console.log(`Đã tạo element: @${data.tag} (${data.name})`);
|
|
38
|
+
if (data.description) console.log(`Mô tả gửi kèm model: ${data.description}`);
|
|
39
|
+
console.log(`Dùng trong prompt: "@${data.tag}"`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = { list, create };
|
package/src/commands/generate.js
CHANGED
|
@@ -34,6 +34,12 @@ async function video(options) {
|
|
|
34
34
|
// --video-ref (v2v 260723): file local tự upload presigned → URL host QM; server probe duration
|
|
35
35
|
// + validate (chỉ seedance-2-0/-fast, max 15s, không mix ảnh, giá hạng with-video).
|
|
36
36
|
if (options.videoRef) body.video_ref_url = await resolveMediaInput(options.videoRef);
|
|
37
|
+
// [260726] 4 tham số nâng cao — server đã nhận sẵn, CLI chỉ chưa mở.
|
|
38
|
+
// --no-audio: commander set options.audio = false; mặc định undefined (giữ default của model).
|
|
39
|
+
if (Number.isInteger(options.seed)) body.seed = options.seed;
|
|
40
|
+
if (options.negative) body.negative_prompt = options.negative;
|
|
41
|
+
if (options.cameraFixed) body.camera_fixed = true;
|
|
42
|
+
if (options.audio === false) body.audio = false;
|
|
37
43
|
|
|
38
44
|
const data = await api.call('POST', '/videos', { body });
|
|
39
45
|
const job_id = data.job_id;
|
package/src/index.js
CHANGED
|
@@ -4,6 +4,7 @@ const { program } = require('commander');
|
|
|
4
4
|
|
|
5
5
|
const auth = require('./commands/auth');
|
|
6
6
|
const generate = require('./commands/generate');
|
|
7
|
+
const elements = require('./commands/elements');
|
|
7
8
|
const jobs = require('./commands/jobs');
|
|
8
9
|
const models = require('./commands/models');
|
|
9
10
|
const credits = require('./commands/credits');
|
|
@@ -51,10 +52,32 @@ gen_cmd
|
|
|
51
52
|
.option('--image <i...>', 'Ảnh đầu vào (URL hoặc file local, lặp nhiều lần — max theo model+mode, xem qm models)')
|
|
52
53
|
.option('--mode <m>', 'Chế độ ảnh: reference (nhiều ảnh tham chiếu) | frames (khung đầu/cuối, max 2). Seedance 2.x KHÔNG nhận ảnh người thật ở MỌI chế độ (xem qm models --type video)')
|
|
53
54
|
.option('--video-ref <v>', 'Video tham chiếu (URL hoặc file local, max 15s/100MB) — CHỈ model supports_video_ref (seedance-2-0/-fast). Giá = rate with-video × (giây output + giây video); không dùng chung với --image')
|
|
55
|
+
.option('--seed <n>', 'Seed cố định — cùng seed + cùng prompt cho kết quả LẶP LẠI được (giữ nhất quán khi render nhiều cảnh)', parseInt)
|
|
56
|
+
.option('--negative <p>', 'Mô tả thứ KHÔNG muốn xuất hiện (watermark, chữ, tay thừa…) — model nào không hỗ trợ thì bỏ qua')
|
|
57
|
+
.option('--camera-fixed', 'Khoá máy quay đứng yên (không pan/zoom) — hợp cảnh xoay sản phẩm')
|
|
58
|
+
.option('--no-audio', 'Tắt tiếng (không đổi giá)')
|
|
54
59
|
.option('--wait', 'Chờ tới khi job hoàn tất')
|
|
55
60
|
.option('--out <dir>', 'Thư mục lưu kết quả (dùng kèm --wait)')
|
|
56
61
|
.action(generate.video);
|
|
57
62
|
|
|
63
|
+
// ── elements ─────────────────────────────────────────────────────────────────
|
|
64
|
+
// [260726] Thư viện element tái dùng: `@tag` trong prompt → BE dịch thành "Image N (mô tả)".
|
|
65
|
+
const elements_cmd = program.command('elements').description('Thư viện element tái dùng (@tag trong prompt)');
|
|
66
|
+
elements_cmd
|
|
67
|
+
.command('list')
|
|
68
|
+
.description('Liệt kê element + tag để trỏ trong prompt')
|
|
69
|
+
.option('--limit <n>', 'Số lượng tối đa (mặc định 50)')
|
|
70
|
+
.action(elements.list);
|
|
71
|
+
elements_cmd
|
|
72
|
+
.command('create')
|
|
73
|
+
.description('Lưu 1 ảnh thành element tái dùng')
|
|
74
|
+
.requiredOption('--name <n>', 'Tên element, vd "Áo dài đỏ" (tag tự sinh từ tên)')
|
|
75
|
+
.requiredOption('--image <i>', 'Ảnh (URL hoặc file local — tự upload)')
|
|
76
|
+
.option('--desc <d>', 'Mô tả chi tiết — CHÍNH là thứ được chèn kèm ảnh vào prompt, viết càng rõ càng giữ được nhân vật')
|
|
77
|
+
.option('--category <c>', 'character | location | prop | product | style | other')
|
|
78
|
+
.option('--type <t>', 'image (mặc định) | video')
|
|
79
|
+
.action(elements.create);
|
|
80
|
+
|
|
58
81
|
// ── jobs ─────────────────────────────────────────────────────────────────────
|
|
59
82
|
const jobs_cmd = program.command('jobs').description('Theo dõi job');
|
|
60
83
|
jobs_cmd.command('get <id>').description('Xem chi tiết job (JSON)').action(jobs.get);
|