@9000ai/cli 0.5.5 → 0.6.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.
@@ -1,15 +1,54 @@
1
1
  import { request, printJson, pollUntilDone } from "../client.js";
2
2
  import { loadJsonFile } from "../utils/format.js";
3
+ /** Extract unique video_ids from monitor task output */
4
+ function extractVideoIdsFromMonitorOutput(output) {
5
+ const data = (output.data ?? output);
6
+ const creators = (data.creators ?? []);
7
+ const seen = new Set();
8
+ for (const creator of creators) {
9
+ const vids = (creator.videos ?? []);
10
+ for (const v of vids) {
11
+ const vid = v.video_id;
12
+ if (vid)
13
+ seen.add(vid);
14
+ }
15
+ }
16
+ return [...seen];
17
+ }
3
18
  export function registerTranscribeCommands(parent) {
4
19
  const cmd = parent.command("transcribe").description("Video / audio transcription");
5
20
  cmd
6
21
  .command("submit")
7
- .description("Submit batch video-to-text task")
8
- .requiredOption("--json-file <path>", "JSON file with video task details")
22
+ .description("Submit video-to-text task (by video IDs or JSON file)")
23
+ .option("--video-ids <ids>", "Comma-separated video IDs (backend resolves URLs from cache)")
24
+ .option("--json-file <path>", "JSON file with video task details (legacy, includes URLs)")
25
+ .option("--group-label <label>", "Batch group label for tracking")
26
+ .option("--webhook <url>", "Webhook URL for completion callback")
9
27
  .option("--wait", "Poll until task completes then print results")
10
28
  .option("--fields <list>", "Comma-separated fields to extract (used with --wait)")
11
29
  .option("--compact", "One JSON object per line (used with --wait)")
12
30
  .action(async (opts) => {
31
+ if (!opts.videoIds && !opts.jsonFile) {
32
+ console.error("Error: must provide --video-ids or --json-file");
33
+ process.exit(1);
34
+ }
35
+ // ── --video-ids: 新流程,后端自己查 URL + 去重 ──
36
+ if (opts.videoIds) {
37
+ const ids = opts.videoIds.split(",").map((s) => s.trim()).filter(Boolean);
38
+ const payload = { video_ids: ids };
39
+ if (opts.groupLabel)
40
+ payload.group_label = opts.groupLabel;
41
+ if (opts.webhook)
42
+ payload.webhook = opts.webhook;
43
+ const data = await request({
44
+ method: "POST",
45
+ path: "/api/v1/media/transcribe-by-video-ids",
46
+ payload,
47
+ });
48
+ printJson(data, { fields: opts.fields, compact: opts.compact });
49
+ return;
50
+ }
51
+ // ── --json-file: 兼容老流程 ──
13
52
  const payload = loadJsonFile(opts.jsonFile);
14
53
  const data = await request({
15
54
  method: "POST",
@@ -23,7 +62,6 @@ export function registerTranscribeCommands(parent) {
23
62
  printJson(data);
24
63
  return;
25
64
  }
26
- // --wait: poll then fetch results
27
65
  console.error(`Batch submitted → ${batchId}, waiting...`);
28
66
  await pollUntilDone(`/api/v1/tasks/batch/${batchId}`);
29
67
  const resultData = await request({
@@ -73,4 +111,71 @@ export function registerTranscribeCommands(parent) {
73
111
  clearTimeout(timer);
74
112
  }
75
113
  });
114
+ // ────────────────────────────────────────────
115
+ // from-monitor: 监控结果 → 提取 video_id → 后端处理
116
+ // ────────────────────────────────────────────
117
+ cmd
118
+ .command("from-monitor")
119
+ .description("Extract video IDs from monitor results, send to backend for dedup + transcribe")
120
+ .option("--task-id <id>", "Monitor task ID to extract videos from")
121
+ .option("--sec-user <ids>", "Comma-separated sec_user_id list (fetches videos, caches URLs)")
122
+ .option("--group-label <label>", "Batch group label for tracking")
123
+ .option("--count <n>", "Videos per user (with --sec-user)", "20")
124
+ .option("--webhook <url>", "Webhook URL for completion callback")
125
+ .action(async (opts) => {
126
+ if (!opts.taskId && !opts.secUser) {
127
+ console.error("Error: must provide --task-id or --sec-user");
128
+ process.exit(1);
129
+ }
130
+ // ── Step 1: Collect video_ids ──
131
+ console.error("Step 1: Collecting video IDs...");
132
+ const allVideoIds = new Set();
133
+ if (opts.taskId) {
134
+ const taskResp = await request({ method: "GET", path: `/api/v1/tasks/${opts.taskId}` });
135
+ const taskData = (taskResp.data ?? taskResp);
136
+ if (taskData.status !== "SUCCESS") {
137
+ console.error(`Task status: ${taskData.status ?? "unknown"} (need SUCCESS)`);
138
+ process.exit(1);
139
+ }
140
+ const output = (typeof taskData.output === "string" ? JSON.parse(taskData.output) : taskData.output);
141
+ for (const vid of extractVideoIdsFromMonitorOutput(output)) {
142
+ allVideoIds.add(vid);
143
+ }
144
+ }
145
+ if (opts.secUser) {
146
+ // fetch 会同时写入后端 video_url_cache,所以只需收集 video_id
147
+ const secUsers = opts.secUser.split(",").map((s) => s.trim()).filter(Boolean);
148
+ for (const secId of secUsers) {
149
+ const params = new URLSearchParams({ sec_user_id: secId, count: opts.count ?? "20", sort_type: "0" });
150
+ console.error(` Fetching ${secId}...`);
151
+ const resp = await request({ method: "GET", path: `/api/v1/douyin/monitor/user-posts?${params}` });
152
+ const data = (resp.data ?? resp);
153
+ const items = (data.items ?? data.videos ?? []);
154
+ for (const v of items) {
155
+ const vid = v.video_id;
156
+ if (vid)
157
+ allVideoIds.add(vid);
158
+ }
159
+ }
160
+ }
161
+ const videoIds = [...allVideoIds];
162
+ console.error(` Collected ${videoIds.length} unique video IDs`);
163
+ if (videoIds.length === 0) {
164
+ printJson({ total_videos: 0, already_transcribed: 0, newly_submitted: 0 });
165
+ return;
166
+ }
167
+ // ── Step 2: 调后端统一处理(去重 + 查缓存 + 转写) ──
168
+ console.error("Step 2: Sending to backend for dedup + transcribe...");
169
+ const payload = { video_ids: videoIds };
170
+ if (opts.groupLabel)
171
+ payload.group_label = opts.groupLabel;
172
+ if (opts.webhook)
173
+ payload.webhook = opts.webhook;
174
+ const data = await request({
175
+ method: "POST",
176
+ path: "/api/v1/media/transcribe-by-video-ids",
177
+ payload,
178
+ });
179
+ printJson(data);
180
+ });
76
181
  }
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ const program = new Command();
14
14
  program
15
15
  .name("9000ai")
16
16
  .description("9000AI Toolbox CLI — unified interface for 9000AI platform")
17
- .version("0.5.5");
17
+ .version("0.6.0");
18
18
  registerConfigCommands(program);
19
19
  registerAuthCommands(program);
20
20
  registerSearchCommands(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@9000ai/cli",
3
- "version": "0.5.5",
3
+ "version": "0.6.0",
4
4
  "description": "9000AI Toolbox CLI — unified command-line interface for 9000AI platform",
5
5
  "type": "module",
6
6
  "bin": {