vidjutsu 1.3.0 → 1.4.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/README.md +153 -68
- package/dist/cli/index.mjs +45 -226
- package/dist/client.d.ts +1 -3
- package/dist/client.js +1 -5
- package/dist/index.d.ts +0 -2
- package/dist/methods.d.ts +60 -66
- package/dist/methods.js +6 -11
- package/install.sh +1 -43
- package/package.json +4 -2
- package/dist/distribution.d.ts +0 -23
- package/dist/distribution.js +0 -56
- package/dist/jobs.d.ts +0 -14
- package/dist/jobs.js +0 -50
package/README.md
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
# vidjutsu
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Clone viral TikTok and Instagram videos onto your own characters. Typed SDK, CLI, and MCP server for the [VidJutsu](https://vidjutsu.ai) API, auto-generated from the [OpenAPI spec](https://raw.githubusercontent.com/tfcbot/vidjutsu-openapi/main/openapi/spec.json).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Point it at a source video and it does three things:
|
|
6
|
+
|
|
7
|
+
1. **Score it.** A clone check tells you whether the video will clone well before you spend anything on rendering.
|
|
8
|
+
2. **Swap in your character.** Create a reusable character once, then reuse it across every clone.
|
|
9
|
+
3. **Get a rendered clone.** Kling 3.0 Motion Control maps the source motion onto your character while preserving the source soundtrack.
|
|
10
|
+
|
|
11
|
+
Supporting endpoints cover discovery (scrape TikTok/Instagram), single-video download, and media processing (watch, extract, transcribe, overlay, compliance).
|
|
6
12
|
|
|
7
13
|
## Install
|
|
8
14
|
|
|
@@ -10,130 +16,209 @@ Watch, extract, transcribe, and overlay — fully typed, auto-generated from the
|
|
|
10
16
|
npm install vidjutsu
|
|
11
17
|
```
|
|
12
18
|
|
|
13
|
-
|
|
19
|
+
Or install the standalone CLI (no Node required):
|
|
14
20
|
|
|
15
21
|
```bash
|
|
16
22
|
curl -fsSL https://vidjutsu.ai/install.sh | bash
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Get an API key
|
|
26
|
+
|
|
27
|
+
```bash
|
|
17
28
|
vidjutsu subscribe --email you@example.com
|
|
18
29
|
```
|
|
19
30
|
|
|
20
|
-
|
|
31
|
+
The key saves to `~/.vidjutsu/config.json`. The SDK and CLI read it from there, so nothing else needs configuring.
|
|
32
|
+
|
|
33
|
+
## Three ways to use it
|
|
34
|
+
|
|
35
|
+
- **MCP** — connect an agent (Claude and others) and let it drive the clone workflow.
|
|
36
|
+
- **CLI** — `vidjutsu clone ...` from a terminal.
|
|
37
|
+
- **SDK** — typed methods in TypeScript.
|
|
38
|
+
|
|
39
|
+
### MCP
|
|
40
|
+
|
|
41
|
+
For **Claude Code**:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
claude mcp add --transport http vidjutsu https://api.vidjutsu.ai/mcp
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
For the **Claude desktop and web apps**, add a custom connector pointing at `https://api.vidjutsu.ai/mcp` and complete the OAuth prompt. There is no command to run.
|
|
48
|
+
|
|
49
|
+
### CLI
|
|
50
|
+
|
|
51
|
+
Characters are reusable. Create one, then clone any number of videos with it.
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# 1. Create a character once. This prints a char_... id.
|
|
55
|
+
vidjutsu clone character --prompt "A neutral, camera-ready presenter"
|
|
56
|
+
|
|
57
|
+
# 2. Check whether a source video will clone well (optional but recommended).
|
|
58
|
+
vidjutsu clone check "https://www.tiktok.com/@creator/video/123456789"
|
|
59
|
+
|
|
60
|
+
# 3. Clone the video onto your character.
|
|
61
|
+
vidjutsu clone run "https://www.tiktok.com/@creator/video/123456789" \
|
|
62
|
+
--character char_abc123
|
|
63
|
+
|
|
64
|
+
# List your characters any time.
|
|
65
|
+
vidjutsu clone character list
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`clone run` downloads the source video, runs the clone check, extracts frame 0, builds a character-swapped starting frame, submits a Kling 3.0 Motion Control generation, and waits for the final URL. It stops if the check verdict is weak unless you pass `--force`. Source clips are limited to 15 seconds.
|
|
21
69
|
|
|
22
|
-
|
|
70
|
+
### SDK
|
|
23
71
|
|
|
24
72
|
```ts
|
|
25
73
|
import { createClient } from "vidjutsu";
|
|
26
74
|
|
|
27
|
-
const vj = createClient();
|
|
75
|
+
const vj = createClient(); // reads ~/.vidjutsu/config.json or env vars
|
|
28
76
|
|
|
29
|
-
//
|
|
30
|
-
const { data:
|
|
31
|
-
|
|
32
|
-
prompt: "Is the hook strong enough for fitness TikTok?",
|
|
77
|
+
// 1. Create a reusable character (returns { id: "char_...", imageUrl, model }).
|
|
78
|
+
const { data: character } = await vj.createCharacter({
|
|
79
|
+
prompt: "A neutral, camera-ready presenter",
|
|
33
80
|
});
|
|
34
|
-
console.log(watch.response);
|
|
35
81
|
|
|
36
|
-
//
|
|
37
|
-
const { data:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
82
|
+
// 2. Stage the source video's file, then check it.
|
|
83
|
+
const { data: source } = await vj.downloadTikTokVideo({
|
|
84
|
+
url: "https://www.tiktok.com/@creator/video/123456789",
|
|
85
|
+
});
|
|
86
|
+
const { data: check } = await vj.cloneCheck({ videoUrl: source.url });
|
|
87
|
+
console.log(check.verdict, check.score);
|
|
88
|
+
|
|
89
|
+
// 3. Extract frame 0 and build a character-swapped starting frame.
|
|
90
|
+
const { data: extracted } = await vj.extractMedia({
|
|
91
|
+
mediaUrl: source.url,
|
|
92
|
+
frames: [0],
|
|
93
|
+
audio: false,
|
|
94
|
+
metadata: false,
|
|
41
95
|
});
|
|
42
|
-
|
|
96
|
+
const firstFrame = extracted.frames?.[0]?.url;
|
|
97
|
+
if (!firstFrame) throw new Error("No first frame returned");
|
|
43
98
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
99
|
+
const { data: starting } = await vj.cloneStartingImage({
|
|
100
|
+
firstFrame,
|
|
101
|
+
characterId: character.id,
|
|
102
|
+
prompt: "Match the framing and lighting of the source",
|
|
47
103
|
});
|
|
48
|
-
console.log(transcript.transcript, transcript.words);
|
|
49
104
|
|
|
50
|
-
//
|
|
51
|
-
const { data:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
105
|
+
// 4. Generate the clone video, then poll for the final URL.
|
|
106
|
+
const { data: task } = await vj.cloneVideo({
|
|
107
|
+
startingImageUrl: starting.imageUrl,
|
|
108
|
+
sourceVideoUrl: source.url,
|
|
109
|
+
model: "kling", // the only supported clone-video model
|
|
55
110
|
});
|
|
56
|
-
|
|
111
|
+
let result;
|
|
112
|
+
do {
|
|
113
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
114
|
+
({ data: result } = await vj.getCloneVideo(task.id));
|
|
115
|
+
} while (result.status === "processing");
|
|
116
|
+
if (result.status === "failed") throw new Error(result.error);
|
|
117
|
+
console.log(result.videoUrl);
|
|
57
118
|
```
|
|
58
119
|
|
|
120
|
+
Every method returns the `openapi-fetch` shape: `{ data, error, response }`.
|
|
121
|
+
|
|
59
122
|
## Authentication
|
|
60
123
|
|
|
61
124
|
The client resolves your API key in this order:
|
|
62
125
|
|
|
63
126
|
1. **Explicit** — `createClient({ apiKey: "vj_live_..." })`
|
|
64
127
|
2. **Environment variable** — `VIDJUTSU_API_KEY`
|
|
65
|
-
3. **Config file** — `~/.vidjutsu/config.json` (written by `vidjutsu auth`)
|
|
128
|
+
3. **Config file** — `~/.vidjutsu/config.json` (written by `vidjutsu subscribe` / `vidjutsu auth`)
|
|
66
129
|
|
|
67
130
|
```ts
|
|
68
|
-
|
|
69
|
-
const vj = createClient();
|
|
131
|
+
const vj = createClient(); // env or config file
|
|
132
|
+
const vj = createClient({ apiKey: "vj_live_..." }); // explicit
|
|
133
|
+
const vj = createClient({ baseUrl: "https://staging.api.vidjutsu.ai" }); // custom host
|
|
134
|
+
```
|
|
70
135
|
|
|
71
|
-
|
|
72
|
-
const vj = createClient({ apiKey: "vj_live_..." });
|
|
136
|
+
The default base URL is `https://api.vidjutsu.ai`.
|
|
73
137
|
|
|
74
|
-
|
|
75
|
-
const vj = createClient({ baseUrl: "https://staging.api.vidjutsu.ai" });
|
|
76
|
-
```
|
|
138
|
+
## Methods
|
|
77
139
|
|
|
78
|
-
|
|
140
|
+
### Clone
|
|
79
141
|
|
|
80
|
-
|
|
142
|
+
| Method | Description |
|
|
143
|
+
|--------|-------------|
|
|
144
|
+
| `cloneCheck(body)` | Score whether a source video will clone well |
|
|
145
|
+
| `createCharacter(body)` / `getCharacter(id)` / `listCharacters()` | Create and reuse persisted characters |
|
|
146
|
+
| `cloneStartingImage(body)` | Build a character-swapped starting frame (no overlays) |
|
|
147
|
+
| `cloneVideo(body)` / `getCloneVideo(id)` | Generate a clone video and poll the task |
|
|
148
|
+
|
|
149
|
+
### Discovery (scrape)
|
|
81
150
|
|
|
82
|
-
|
|
151
|
+
Metadata and discovery only. Scrape returns the provider's raw source URLs and never stages media.
|
|
83
152
|
|
|
84
|
-
| Method |
|
|
85
|
-
|
|
86
|
-
| `
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
| `
|
|
153
|
+
| Method | Description |
|
|
154
|
+
|--------|-------------|
|
|
155
|
+
| `scrapeTikTokTrending(body)` / `scrapeTikTokProfile(body)` / `scrapeTikTokProfileVideos(body)` | Find TikTok videos |
|
|
156
|
+
| `scrapeTikTokVideo(body)` / `scrapeTikTokVideoTranscript(body)` / `scrapeTikTokVideoComments(body)` | Single TikTok video, transcript, comments |
|
|
157
|
+
| `scrapeTikTokSearchUsers(body)` | Search TikTok users |
|
|
158
|
+
| `scrapeInstagramProfile(body)` / `scrapeInstagramUserPosts(body)` / `scrapeInstagramUserReels(body)` | Find Instagram content |
|
|
159
|
+
| `scrapeInstagramPost(body)` / `scrapeInstagramPostComments(body)` | Single Instagram post, comments |
|
|
160
|
+
|
|
161
|
+
### Download
|
|
162
|
+
|
|
163
|
+
Stage one video's file to the CDN, one call per video.
|
|
164
|
+
|
|
165
|
+
| Method | Description |
|
|
166
|
+
|--------|-------------|
|
|
167
|
+
| `downloadTikTokVideo(body)` / `downloadInstagramVideo(body)` | Download and stage a single video |
|
|
168
|
+
|
|
169
|
+
### Media processing (metered)
|
|
170
|
+
|
|
171
|
+
Each endpoint has its own daily rate limit (fixed window, resets 00:00 UTC). Hitting a limit returns HTTP 429 with a `retryAfter` hint.
|
|
172
|
+
|
|
173
|
+
| Method | Description |
|
|
174
|
+
|--------|-------------|
|
|
175
|
+
| `watchMedia(body)` | AI watches a video/image and answers your prompt |
|
|
176
|
+
| `extractMedia(body)` | Extract frames, audio, and metadata |
|
|
177
|
+
| `transcribeMedia(body)` | Speech-to-text with word-level timing |
|
|
178
|
+
| `createOverlay(body)` / `createDisclaimer(body)` | Burn text or a disclaimer onto video |
|
|
179
|
+
| `checkComplianceVideo(body)` / `checkCompliancePrompt(body)` | Ad-compliance checks |
|
|
91
180
|
|
|
92
|
-
### Resources
|
|
181
|
+
### Resources
|
|
93
182
|
|
|
94
183
|
| Method | Description |
|
|
95
184
|
|--------|-------------|
|
|
96
|
-
| `createAccount
|
|
97
|
-
| `createPost
|
|
98
|
-
| `createAsset
|
|
99
|
-
| `createReference
|
|
100
|
-
| `
|
|
185
|
+
| `createAccount` / `updateAccount` / `listOrGetAccounts` / `deleteAccount` | Accounts |
|
|
186
|
+
| `createPost` / `updatePost` / `listOrGetPosts` / `deletePost` | Posts |
|
|
187
|
+
| `createAsset` / `updateAsset` / `listOrGetAssets` / `deleteAsset` | Assets |
|
|
188
|
+
| `createReference` / `updateReference` / `listOrGetReferences` / `deleteReference` | References |
|
|
189
|
+
| `createEditorProject` / `updateEditorProject` / `listOrGetEditorProjects` / `deleteEditorProject` | Editor projects |
|
|
190
|
+
| `uploadFile(body)` / `uploadFromUrl(body)` | Upload media to the CDN |
|
|
101
191
|
|
|
102
|
-
###
|
|
192
|
+
### Account and billing
|
|
103
193
|
|
|
104
194
|
| Method | Description |
|
|
105
195
|
|--------|-------------|
|
|
106
|
-
| `getInfo()` | API info
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
109
|
-
| `getCheckoutStatus(query)` | Check checkout status |
|
|
110
|
-
| `createSubscription(body)` | Subscribe ($99/mo) |
|
|
111
|
-
| `rotateApiKey()` | Rotate API key (invalidates current key) |
|
|
112
|
-
| `recoverApiKey(body)` | Recover key via email |
|
|
196
|
+
| `getInfo()` / `getPricing()` / `getUsage()` | API info, pricing, remaining daily capacity |
|
|
197
|
+
| `createSubscription(body)` / `getCheckoutStatus(query)` | Subscribe and check checkout status |
|
|
198
|
+
| `rotateApiKey()` / `recoverApiKey(body)` | Rotate or recover your key |
|
|
113
199
|
|
|
114
200
|
## Raw client
|
|
115
201
|
|
|
116
|
-
|
|
202
|
+
For any endpoint without a convenience method, use the raw `openapi-fetch` client:
|
|
117
203
|
|
|
118
204
|
```ts
|
|
119
|
-
const { data
|
|
120
|
-
const { data
|
|
121
|
-
body: {
|
|
205
|
+
const { data } = await vj.api.GET("/v1/usage");
|
|
206
|
+
const { data } = await vj.api.POST("/v1/clones/check", {
|
|
207
|
+
body: { videoUrl: "https://..." },
|
|
122
208
|
});
|
|
123
209
|
```
|
|
124
210
|
|
|
125
|
-
## Pricing
|
|
211
|
+
## Pricing and billing
|
|
126
212
|
|
|
127
|
-
- **$99/month flat**
|
|
128
|
-
-
|
|
213
|
+
- **$99/month flat** for full API access, via Stripe, billed monthly, cancel anytime.
|
|
214
|
+
- No credits and no top-ups. Usage is governed by per-endpoint daily rate limits that reset at 00:00 UTC.
|
|
129
215
|
- Hitting a daily limit returns **HTTP 429** with a `retryAfter` hint. Check remaining capacity with `getUsage()` or `GET /v1/usage`.
|
|
130
|
-
- `GET /v1/pricing` is the
|
|
216
|
+
- `GET /v1/pricing` is the source of truth for current price and limits.
|
|
131
217
|
|
|
132
218
|
## Links
|
|
133
219
|
|
|
134
220
|
- [Documentation](https://docs.vidjutsu.ai)
|
|
135
221
|
- [API Reference](https://docs.vidjutsu.ai/api-reference/introduction)
|
|
136
|
-
- [CLI](https://github.com/tfcbot/vidjutsu-sdk) (included in this package)
|
|
137
222
|
- [OpenAPI Spec](https://raw.githubusercontent.com/tfcbot/vidjutsu-openapi/main/openapi/spec.json)
|
|
138
223
|
|
|
139
224
|
## License
|
package/dist/cli/index.mjs
CHANGED
|
@@ -2190,7 +2190,7 @@ var init_dist2 = __esm(() => {
|
|
|
2190
2190
|
});
|
|
2191
2191
|
|
|
2192
2192
|
// src/cli/version.ts
|
|
2193
|
-
var VERSION = "1.
|
|
2193
|
+
var VERSION = "1.4.0";
|
|
2194
2194
|
|
|
2195
2195
|
// src/cli/client.ts
|
|
2196
2196
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
@@ -2607,21 +2607,19 @@ var init_status = __esm(() => {
|
|
|
2607
2607
|
status_default = defineCommand({
|
|
2608
2608
|
meta: { name: "status", description: "Check resource status by ID" },
|
|
2609
2609
|
args: {
|
|
2610
|
-
id: { type: "positional", description: "Resource ID (
|
|
2610
|
+
id: { type: "positional", description: "Resource ID (acc_xxx, post_xxx, ref_xxx)", required: true }
|
|
2611
2611
|
},
|
|
2612
2612
|
async run({ args }) {
|
|
2613
2613
|
const id = args.id;
|
|
2614
2614
|
let path;
|
|
2615
|
-
if (id.startsWith("
|
|
2616
|
-
path = `/v1/distribution/jobs?id=${id}`;
|
|
2617
|
-
else if (id.startsWith("acc_"))
|
|
2615
|
+
if (id.startsWith("acc_"))
|
|
2618
2616
|
path = `/v1/accounts?id=${id}`;
|
|
2619
2617
|
else if (id.startsWith("post_"))
|
|
2620
2618
|
path = `/v1/posts?id=${id}`;
|
|
2621
2619
|
else if (id.startsWith("ref_"))
|
|
2622
2620
|
path = `/v1/references?id=${id}`;
|
|
2623
2621
|
else {
|
|
2624
|
-
console.error("Unknown ID prefix. Expected
|
|
2622
|
+
console.error("Unknown ID prefix. Expected acc_, post_, or ref_");
|
|
2625
2623
|
process.exit(1);
|
|
2626
2624
|
}
|
|
2627
2625
|
const result = await apiRequest("GET", path);
|
|
@@ -2630,148 +2628,6 @@ var init_status = __esm(() => {
|
|
|
2630
2628
|
});
|
|
2631
2629
|
});
|
|
2632
2630
|
|
|
2633
|
-
// src/cli/commands/balance.ts
|
|
2634
|
-
var exports_balance = {};
|
|
2635
|
-
__export(exports_balance, {
|
|
2636
|
-
default: () => balance_default
|
|
2637
|
-
});
|
|
2638
|
-
function fmtTs(ms) {
|
|
2639
|
-
const d2 = new Date(ms);
|
|
2640
|
-
const pad = (n2) => String(n2).padStart(2, "0");
|
|
2641
|
-
return `${d2.getUTCFullYear()}-${pad(d2.getUTCMonth() + 1)}-${pad(d2.getUTCDate())} ${pad(d2.getUTCHours())}:${pad(d2.getUTCMinutes())} UTC`;
|
|
2642
|
-
}
|
|
2643
|
-
function fmtDelta(n2) {
|
|
2644
|
-
const s2 = n2 >= 0 ? `+${n2}` : String(n2);
|
|
2645
|
-
return s2.padStart(6);
|
|
2646
|
-
}
|
|
2647
|
-
var balance_default;
|
|
2648
|
-
var init_balance = __esm(() => {
|
|
2649
|
-
init_dist2();
|
|
2650
|
-
init_client();
|
|
2651
|
-
balance_default = defineCommand({
|
|
2652
|
-
meta: {
|
|
2653
|
-
name: "balance",
|
|
2654
|
-
description: "Show credit balance and recent ledger entries"
|
|
2655
|
-
},
|
|
2656
|
-
args: {
|
|
2657
|
-
json: {
|
|
2658
|
-
type: "boolean",
|
|
2659
|
-
description: "Emit raw JSON instead of a table",
|
|
2660
|
-
default: false
|
|
2661
|
-
}
|
|
2662
|
-
},
|
|
2663
|
-
async run({ args }) {
|
|
2664
|
-
const res = await apiRequest("GET", "/v1/balance");
|
|
2665
|
-
if (args.json) {
|
|
2666
|
-
console.log(JSON.stringify(res, null, 2));
|
|
2667
|
-
return;
|
|
2668
|
-
}
|
|
2669
|
-
console.log(`Balance: ${res.balance} credits`);
|
|
2670
|
-
if (!res.ledger || res.ledger.length === 0) {
|
|
2671
|
-
console.log(`
|
|
2672
|
-
No ledger entries yet.`);
|
|
2673
|
-
return;
|
|
2674
|
-
}
|
|
2675
|
-
console.log(`
|
|
2676
|
-
Recent ledger (newest first):`);
|
|
2677
|
-
for (const e2 of res.ledger) {
|
|
2678
|
-
const detail = e2.operationId ?? e2.note ?? "";
|
|
2679
|
-
console.log(` ${fmtDelta(e2.delta)} ${e2.reason.padEnd(7)} ${fmtTs(e2.createdAt)} ${detail}`);
|
|
2680
|
-
}
|
|
2681
|
-
}
|
|
2682
|
-
});
|
|
2683
|
-
});
|
|
2684
|
-
|
|
2685
|
-
// src/cli/commands/admin.ts
|
|
2686
|
-
var exports_admin = {};
|
|
2687
|
-
__export(exports_admin, {
|
|
2688
|
-
default: () => admin_default
|
|
2689
|
-
});
|
|
2690
|
-
import { spawnSync } from "node:child_process";
|
|
2691
|
-
var grant, admin_default;
|
|
2692
|
-
var init_admin = __esm(() => {
|
|
2693
|
-
init_dist2();
|
|
2694
|
-
grant = defineCommand({
|
|
2695
|
-
meta: {
|
|
2696
|
-
name: "grant",
|
|
2697
|
-
description: "Grant credits to a client (looked up by email or clientId). Runs the Convex mutation directly via 'npx convex run'."
|
|
2698
|
-
},
|
|
2699
|
-
args: {
|
|
2700
|
-
email: {
|
|
2701
|
-
type: "string",
|
|
2702
|
-
description: "Email of the client to grant credits to"
|
|
2703
|
-
},
|
|
2704
|
-
client: {
|
|
2705
|
-
type: "string",
|
|
2706
|
-
description: "clientId of the client to grant credits to"
|
|
2707
|
-
},
|
|
2708
|
-
amount: {
|
|
2709
|
-
type: "string",
|
|
2710
|
-
description: "Number of credits to grant (positive integer)",
|
|
2711
|
-
required: true
|
|
2712
|
-
},
|
|
2713
|
-
note: {
|
|
2714
|
-
type: "string",
|
|
2715
|
-
description: "Optional admin note recorded on the ledger row"
|
|
2716
|
-
},
|
|
2717
|
-
repo: {
|
|
2718
|
-
type: "string",
|
|
2719
|
-
description: "Path to the vidjutsu Convex repo where 'npx convex run' executes (defaults to cwd)"
|
|
2720
|
-
}
|
|
2721
|
-
},
|
|
2722
|
-
async run({ args }) {
|
|
2723
|
-
if (!args.email && !args.client) {
|
|
2724
|
-
console.error("Error: provide --email <e> or --client <clientId>");
|
|
2725
|
-
process.exit(1);
|
|
2726
|
-
}
|
|
2727
|
-
const amount = Number(args.amount);
|
|
2728
|
-
if (!Number.isInteger(amount) || amount <= 0) {
|
|
2729
|
-
console.error("Error: --amount must be a positive integer");
|
|
2730
|
-
process.exit(1);
|
|
2731
|
-
}
|
|
2732
|
-
const payload = { amount };
|
|
2733
|
-
if (args.email)
|
|
2734
|
-
payload.email = args.email;
|
|
2735
|
-
if (args.client)
|
|
2736
|
-
payload.clientId = args.client;
|
|
2737
|
-
if (args.note)
|
|
2738
|
-
payload.note = args.note;
|
|
2739
|
-
const cwd = args.repo ?? process.cwd();
|
|
2740
|
-
const cmdArgs = [
|
|
2741
|
-
"convex",
|
|
2742
|
-
"run",
|
|
2743
|
-
"services/machineClients:grantCreditsByLookup",
|
|
2744
|
-
JSON.stringify(payload)
|
|
2745
|
-
];
|
|
2746
|
-
const result = spawnSync("npx", cmdArgs, {
|
|
2747
|
-
cwd,
|
|
2748
|
-
encoding: "utf-8",
|
|
2749
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
2750
|
-
});
|
|
2751
|
-
if (result.error) {
|
|
2752
|
-
console.error("Error invoking npx convex:", result.error.message);
|
|
2753
|
-
process.exit(1);
|
|
2754
|
-
}
|
|
2755
|
-
if (result.stdout)
|
|
2756
|
-
process.stdout.write(result.stdout);
|
|
2757
|
-
if (result.stderr)
|
|
2758
|
-
process.stderr.write(result.stderr);
|
|
2759
|
-
if (result.status !== 0) {
|
|
2760
|
-
process.exit(result.status ?? 1);
|
|
2761
|
-
}
|
|
2762
|
-
}
|
|
2763
|
-
});
|
|
2764
|
-
admin_default = defineCommand({
|
|
2765
|
-
meta: {
|
|
2766
|
-
name: "admin",
|
|
2767
|
-
description: "Admin operations (credit grants, etc.)"
|
|
2768
|
-
},
|
|
2769
|
-
subCommands: {
|
|
2770
|
-
grant: () => grant
|
|
2771
|
-
}
|
|
2772
|
-
});
|
|
2773
|
-
});
|
|
2774
|
-
|
|
2775
2631
|
// src/cli/commands/version.ts
|
|
2776
2632
|
var exports_version = {};
|
|
2777
2633
|
__export(exports_version, {
|
|
@@ -2866,58 +2722,6 @@ var init_update = __esm(() => {
|
|
|
2866
2722
|
});
|
|
2867
2723
|
});
|
|
2868
2724
|
|
|
2869
|
-
// src/cli/commands/jobs.ts
|
|
2870
|
-
var exports_jobs = {};
|
|
2871
|
-
__export(exports_jobs, {
|
|
2872
|
-
default: () => jobs_default
|
|
2873
|
-
});
|
|
2874
|
-
async function getJob(jobId) {
|
|
2875
|
-
return await apiRequest("GET", `/v1/jobs?id=${encodeURIComponent(jobId)}`);
|
|
2876
|
-
}
|
|
2877
|
-
var jobs_default;
|
|
2878
|
-
var init_jobs = __esm(() => {
|
|
2879
|
-
init_dist2();
|
|
2880
|
-
init_client();
|
|
2881
|
-
jobs_default = defineCommand({
|
|
2882
|
-
meta: { name: "jobs", description: "Inspect durable VidJutsu media jobs" },
|
|
2883
|
-
subCommands: {
|
|
2884
|
-
get: defineCommand({
|
|
2885
|
-
meta: { name: "get", description: "Get a job" },
|
|
2886
|
-
args: {
|
|
2887
|
-
id: { type: "positional", description: "Job ID", required: true }
|
|
2888
|
-
},
|
|
2889
|
-
async run({ args }) {
|
|
2890
|
-
console.log(JSON.stringify(await getJob(args.id), null, 2));
|
|
2891
|
-
}
|
|
2892
|
-
}),
|
|
2893
|
-
wait: defineCommand({
|
|
2894
|
-
meta: { name: "wait", description: "Wait for a job to complete" },
|
|
2895
|
-
args: {
|
|
2896
|
-
id: { type: "positional", description: "Job ID", required: true },
|
|
2897
|
-
timeout: { type: "string", description: "Timeout in seconds", default: "600" },
|
|
2898
|
-
interval: { type: "string", description: "Poll interval in milliseconds", default: "1000" }
|
|
2899
|
-
},
|
|
2900
|
-
async run({ args }) {
|
|
2901
|
-
const deadline = Date.now() + Number(args.timeout) * 1000;
|
|
2902
|
-
while (true) {
|
|
2903
|
-
const job = await getJob(args.id);
|
|
2904
|
-
if (job?.status === "completed") {
|
|
2905
|
-
console.log(JSON.stringify(job, null, 2));
|
|
2906
|
-
return;
|
|
2907
|
-
}
|
|
2908
|
-
if (job?.status === "failed" || job?.status === "cancelled") {
|
|
2909
|
-
throw new Error(job.error?.message ?? `Job ${job.status}`);
|
|
2910
|
-
}
|
|
2911
|
-
if (Date.now() >= deadline)
|
|
2912
|
-
throw new Error(`Timed out waiting for ${args.id}`);
|
|
2913
|
-
await new Promise((resolve) => setTimeout(resolve, Number(args.interval)));
|
|
2914
|
-
}
|
|
2915
|
-
}
|
|
2916
|
-
})
|
|
2917
|
-
}
|
|
2918
|
-
});
|
|
2919
|
-
});
|
|
2920
|
-
|
|
2921
2725
|
// src/cli/commands/clone.ts
|
|
2922
2726
|
var exports_clone = {};
|
|
2923
2727
|
__export(exports_clone, {
|
|
@@ -2934,8 +2738,11 @@ function validateModel(model) {
|
|
|
2934
2738
|
function isCharacterId(value) {
|
|
2935
2739
|
return value.startsWith("char_");
|
|
2936
2740
|
}
|
|
2937
|
-
function
|
|
2938
|
-
|
|
2741
|
+
function requireCharacterId(value) {
|
|
2742
|
+
if (!isCharacterId(value)) {
|
|
2743
|
+
throw new Error("--character must be a stored character id beginning with char_");
|
|
2744
|
+
}
|
|
2745
|
+
return value;
|
|
2939
2746
|
}
|
|
2940
2747
|
async function pollCloneVideo(id, { intervalMs = 3000, timeoutMs = 600000 } = {}) {
|
|
2941
2748
|
const deadline = Date.now() + timeoutMs;
|
|
@@ -2954,17 +2761,17 @@ var VALID_MODELS, clone_default;
|
|
|
2954
2761
|
var init_clone = __esm(() => {
|
|
2955
2762
|
init_dist2();
|
|
2956
2763
|
init_client();
|
|
2957
|
-
VALID_MODELS = ["kling"
|
|
2764
|
+
VALID_MODELS = ["kling"];
|
|
2958
2765
|
clone_default = defineCommand({
|
|
2959
2766
|
meta: {
|
|
2960
2767
|
name: "clone",
|
|
2961
|
-
description: "Clone a source video's motion onto a new character. About 5 minutes end to end for the full chain. " + "Kling
|
|
2768
|
+
description: "Clone a source video's motion onto a new character. About 5 minutes end to end for the full chain. " + "Kling 3.0 Motion Control is the supported model. Source clips are capped at 15 seconds. " + "Generation steps share the subscription's daily clone limit."
|
|
2962
2769
|
},
|
|
2963
2770
|
subCommands: {
|
|
2964
2771
|
check: defineCommand({
|
|
2965
2772
|
meta: {
|
|
2966
2773
|
name: "check",
|
|
2967
|
-
description: "Evaluate whether a source video can be cloned reliably.
|
|
2774
|
+
description: "Evaluate whether a source video can be cloned reliably. Uses the daily clone limit. " + "Exits nonzero when the verdict is weak."
|
|
2968
2775
|
},
|
|
2969
2776
|
args: {
|
|
2970
2777
|
videoUrl: { type: "positional", description: "Public HTTPS URL of the source video", required: true },
|
|
@@ -2989,7 +2796,7 @@ var init_clone = __esm(() => {
|
|
|
2989
2796
|
character: defineCommand({
|
|
2990
2797
|
meta: {
|
|
2991
2798
|
name: "character",
|
|
2992
|
-
description: "Create a reusable, persisted character. Create it once, then reuse the printed id for every " + "clone (see `vidjutsu clone starting-image --character <id>` and `clone run --character <id>`). " + "
|
|
2799
|
+
description: "Create a reusable, persisted character. Create it once, then reuse the printed id for every " + "clone (see `vidjutsu clone starting-image --character <id>` and `clone run --character <id>`). " + "Uses the daily clone limit."
|
|
2993
2800
|
},
|
|
2994
2801
|
subCommands: {
|
|
2995
2802
|
list: defineCommand({
|
|
@@ -3034,22 +2841,26 @@ var init_clone = __esm(() => {
|
|
|
3034
2841
|
"starting-image": defineCommand({
|
|
3035
2842
|
meta: {
|
|
3036
2843
|
name: "starting-image",
|
|
3037
|
-
description: "Create a character-swapped starting frame
|
|
2844
|
+
description: "Create a character-swapped starting frame from an extracted first frame and a stored character id. Uses the daily clone limit."
|
|
3038
2845
|
},
|
|
3039
2846
|
args: {
|
|
3040
2847
|
character: {
|
|
3041
2848
|
type: "string",
|
|
3042
|
-
description: "Stored character id (char_...) from `clone character
|
|
2849
|
+
description: "Stored character id (char_...) from `clone character`",
|
|
2850
|
+
required: true
|
|
2851
|
+
},
|
|
2852
|
+
"first-frame": {
|
|
2853
|
+
type: "string",
|
|
2854
|
+
description: "Public HTTPS URL of the source video's extracted first-frame image",
|
|
3043
2855
|
required: true
|
|
3044
2856
|
},
|
|
3045
|
-
prompt: { type: "string", description: "Instructions for composing the starting frame", required: true }
|
|
3046
|
-
source: { type: "string", description: "Optional public HTTPS source video URL to ground the composition" }
|
|
2857
|
+
prompt: { type: "string", description: "Instructions for composing the starting frame", required: true }
|
|
3047
2858
|
},
|
|
3048
2859
|
async run({ args }) {
|
|
3049
2860
|
const result = await apiRequest("POST", "/v1/clones/starting-image", {
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
2861
|
+
firstFrame: args["first-frame"],
|
|
2862
|
+
characterId: requireCharacterId(args.character),
|
|
2863
|
+
prompt: args.prompt
|
|
3053
2864
|
});
|
|
3054
2865
|
console.log(result.imageUrl);
|
|
3055
2866
|
}
|
|
@@ -3057,13 +2868,13 @@ var init_clone = __esm(() => {
|
|
|
3057
2868
|
video: defineCommand({
|
|
3058
2869
|
meta: {
|
|
3059
2870
|
name: "video",
|
|
3060
|
-
description: "Clone source motion onto a starting image
|
|
2871
|
+
description: "Clone source motion onto a starting image with Kling 3.0 Motion Control. Uses the daily clone limit. " + "Returns a task id (HTTP 202); use --wait to poll to completion."
|
|
3061
2872
|
},
|
|
3062
2873
|
args: {
|
|
3063
2874
|
"starting-image": { type: "string", description: "Public HTTPS URL of the starting frame", required: true },
|
|
3064
2875
|
source: { type: "string", description: "Public HTTPS URL of the source video whose motion is cloned", required: true },
|
|
3065
|
-
model: { type: "string", description: "kling (
|
|
3066
|
-
prompt: { type: "string", description: "Optional override for the default
|
|
2876
|
+
model: { type: "string", description: "kling (the only supported model)" },
|
|
2877
|
+
prompt: { type: "string", description: "Optional override for the default motion-control prompt" },
|
|
3067
2878
|
wait: { type: "boolean", description: "Poll GET status until completed or failed, then print videoUrl" }
|
|
3068
2879
|
},
|
|
3069
2880
|
async run({ args }) {
|
|
@@ -3097,7 +2908,7 @@ var init_clone = __esm(() => {
|
|
|
3097
2908
|
run: defineCommand({
|
|
3098
2909
|
meta: {
|
|
3099
2910
|
name: "run",
|
|
3100
|
-
description: "Run the full clone chain: download the TikTok video, check it, generate a starting image from a " + "reusable stored character, generate the clone video, and wait for it to finish. About 5 minutes end " + "to end. Characters are reusable: create one once with `vidjutsu clone character --prompt ...`, then " + "pass its id here with --character on every run — this command never generates a new random character. " + "--character is required. Kling
|
|
2911
|
+
description: "Run the full clone chain: download the TikTok video, check it, generate a starting image from a " + "reusable stored character, generate the clone video, and wait for it to finish. About 5 minutes end " + "to end. Characters are reusable: create one once with `vidjutsu clone character --prompt ...`, then " + "pass its id here with --character on every run — this command never generates a new random character. " + "--character is required. Kling 3.0 Motion Control is the only model; source clips " + "are capped at 15 seconds. Generation steps share the daily clone limit. Stops before generation when the " + "check verdict is weak, unless --force is given. The command extracts frame 0 and passes it directly " + "to the character edit step."
|
|
3101
2912
|
},
|
|
3102
2913
|
args: {
|
|
3103
2914
|
tiktokUrl: { type: "positional", description: "TikTok video URL to clone", required: true },
|
|
@@ -3106,16 +2917,17 @@ var init_clone = __esm(() => {
|
|
|
3106
2917
|
description: "Required. Stored character id (char_...) from `vidjutsu clone character --prompt ...`. " + "Characters are reusable across runs — create one once, then pass its id here every time."
|
|
3107
2918
|
},
|
|
3108
2919
|
"starting-prompt": { type: "string", description: "Prompt for the starting frame composition" },
|
|
3109
|
-
model: { type: "string", description: "kling (
|
|
2920
|
+
model: { type: "string", description: "kling (the only supported model)" },
|
|
3110
2921
|
force: { type: "boolean", description: "Proceed even if the clone check verdict is weak" }
|
|
3111
2922
|
},
|
|
3112
2923
|
async run({ args }) {
|
|
3113
|
-
const model = validateModel(args.model);
|
|
2924
|
+
const model = validateModel(args.model) ?? "kling";
|
|
3114
2925
|
if (!args.character) {
|
|
3115
2926
|
console.log("Missing --character. Create a reusable character first with: " + 'vidjutsu clone character --prompt "A neutral, camera-ready presenter"');
|
|
3116
2927
|
console.log("Then pass its id here: vidjutsu clone run <url> --character char_...");
|
|
3117
2928
|
process.exit(1);
|
|
3118
2929
|
}
|
|
2930
|
+
const characterId = requireCharacterId(args.character);
|
|
3119
2931
|
console.log(`Downloading ${args.tiktokUrl}...`);
|
|
3120
2932
|
const download = await apiRequest("POST", "/v1/videos/download/tiktok", {
|
|
3121
2933
|
url: args.tiktokUrl
|
|
@@ -3132,11 +2944,21 @@ var init_clone = __esm(() => {
|
|
|
3132
2944
|
console.log("Stopping: the source clip is unlikely to clone well. Pass --force to continue anyway.");
|
|
3133
2945
|
process.exit(1);
|
|
3134
2946
|
}
|
|
2947
|
+
console.log("Extracting first frame...");
|
|
2948
|
+
const extracted = await apiRequest("POST", "/v1/extract", {
|
|
2949
|
+
mediaUrl: download.url,
|
|
2950
|
+
frames: [0],
|
|
2951
|
+
audio: false,
|
|
2952
|
+
metadata: false
|
|
2953
|
+
});
|
|
2954
|
+
const firstFrame = extracted.frames?.find((frame) => frame.index === 0)?.url ?? extracted.frames?.[0]?.url;
|
|
2955
|
+
if (!firstFrame)
|
|
2956
|
+
throw new Error("The source video did not return an extracted first frame");
|
|
3135
2957
|
console.log("Generating starting image...");
|
|
3136
2958
|
const startingImage = await apiRequest("POST", "/v1/clones/starting-image", {
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
2959
|
+
firstFrame,
|
|
2960
|
+
characterId,
|
|
2961
|
+
prompt: args["starting-prompt"] ?? "Match the framing and pose of the source video's opening frame"
|
|
3140
2962
|
});
|
|
3141
2963
|
console.log("Generating clone video...");
|
|
3142
2964
|
const accepted = await apiRequest("POST", "/v1/clones/video", {
|
|
@@ -3924,7 +3746,7 @@ var main = defineCommand({
|
|
|
3924
3746
|
meta: {
|
|
3925
3747
|
name: "vidjutsu",
|
|
3926
3748
|
version: VERSION,
|
|
3927
|
-
description: "
|
|
3749
|
+
description: "Clone short-form videos and process media with the VidJutsu API."
|
|
3928
3750
|
},
|
|
3929
3751
|
subCommands: {
|
|
3930
3752
|
auth: () => Promise.resolve().then(() => (init_auth(), exports_auth)).then((m2) => m2.default),
|
|
@@ -3932,11 +3754,8 @@ var main = defineCommand({
|
|
|
3932
3754
|
upload: () => Promise.resolve().then(() => (init_upload(), exports_upload)).then((m2) => m2.default),
|
|
3933
3755
|
subscribe: () => Promise.resolve().then(() => (init_subscribe(), exports_subscribe)).then((m2) => m2.default),
|
|
3934
3756
|
status: () => Promise.resolve().then(() => (init_status(), exports_status)).then((m2) => m2.default),
|
|
3935
|
-
balance: () => Promise.resolve().then(() => (init_balance(), exports_balance)).then((m2) => m2.default),
|
|
3936
|
-
admin: () => Promise.resolve().then(() => (init_admin(), exports_admin)).then((m2) => m2.default),
|
|
3937
3757
|
version: () => Promise.resolve().then(() => (init_version(), exports_version)).then((m2) => m2.default),
|
|
3938
3758
|
update: () => Promise.resolve().then(() => (init_update(), exports_update)).then((m2) => m2.default),
|
|
3939
|
-
jobs: () => Promise.resolve().then(() => (init_jobs(), exports_jobs)).then((m2) => m2.default),
|
|
3940
3759
|
clone: () => Promise.resolve().then(() => (init_clone(), exports_clone)).then((m2) => m2.default),
|
|
3941
3760
|
watch: () => Promise.resolve().then(() => (init_watch(), exports_watch)).then((m2) => m2.default),
|
|
3942
3761
|
extract: () => Promise.resolve().then(() => (init_extract(), exports_extract)).then((m2) => m2.default),
|
package/dist/client.d.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import createFetchClient from "openapi-fetch";
|
|
2
2
|
import type { paths } from "./schema.js";
|
|
3
3
|
import { type VidJutsuMethods } from "./methods.js";
|
|
4
|
-
import { type DistributionNamespaces } from "./distribution.js";
|
|
5
|
-
import { type JobsNamespaces } from "./jobs.js";
|
|
6
4
|
export interface VidJutsuConfig {
|
|
7
5
|
/** API key. Falls back to VIDJUTSU_API_KEY env var, then ~/.vidjutsu/config.json. */
|
|
8
6
|
apiKey?: string;
|
|
@@ -12,7 +10,7 @@ export interface VidJutsuConfig {
|
|
|
12
10
|
/** The raw openapi-fetch client with typed GET/POST/PUT/DELETE */
|
|
13
11
|
type FetchClient = ReturnType<typeof createFetchClient<paths>>;
|
|
14
12
|
/** Combined client: typed convenience methods + raw openapi-fetch escape hatch */
|
|
15
|
-
export type VidJutsuClient = VidJutsuMethods &
|
|
13
|
+
export type VidJutsuClient = VidJutsuMethods & {
|
|
16
14
|
api: FetchClient;
|
|
17
15
|
};
|
|
18
16
|
/**
|
package/dist/client.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import createFetchClient from "openapi-fetch";
|
|
2
2
|
import { bindMethods } from "./methods.js";
|
|
3
|
-
import { bindDistribution } from "./distribution.js";
|
|
4
|
-
import { bindJobs } from "./jobs.js";
|
|
5
3
|
import { readFileSync, existsSync } from "fs";
|
|
6
4
|
import { join } from "path";
|
|
7
5
|
import { homedir } from "os";
|
|
@@ -52,7 +50,5 @@ export function createClient(config = {}) {
|
|
|
52
50
|
},
|
|
53
51
|
});
|
|
54
52
|
const methods = bindMethods(fetchClient);
|
|
55
|
-
|
|
56
|
-
const jobs = bindJobs(fetchClient);
|
|
57
|
-
return { ...methods, ...distribution, ...jobs, api: fetchClient };
|
|
53
|
+
return { ...methods, api: fetchClient };
|
|
58
54
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
export { createClient } from "./client.js";
|
|
2
2
|
export type { VidJutsuConfig, VidJutsuClient } from "./client.js";
|
|
3
3
|
export type { VidJutsuMethods } from "./methods.js";
|
|
4
|
-
export type { DistributionNamespaces, MutationOptions, WaitOptions } from "./distribution.js";
|
|
5
|
-
export type { JobsNamespaces } from "./jobs.js";
|
|
6
4
|
export type { paths, components } from "./schema.js";
|
package/dist/methods.d.ts
CHANGED
|
@@ -14,143 +14,137 @@ type QueryParams<P extends keyof paths, M extends keyof paths[P]> = paths[P][M]
|
|
|
14
14
|
};
|
|
15
15
|
} ? Q : never;
|
|
16
16
|
export interface VidJutsuMethods {
|
|
17
|
-
/** Create account
|
|
17
|
+
/** Create account Bearer API key required. */
|
|
18
18
|
createAccount(body: ReqBody<"/v1/accounts", "post">): ReturnType<Client["POST"]>;
|
|
19
|
-
/** Update account
|
|
19
|
+
/** Update account Bearer API key required. */
|
|
20
20
|
updateAccount(body: ReqBody<"/v1/accounts", "put">, query?: QueryParams<"/v1/accounts", "put">): ReturnType<Client["PUT"]>;
|
|
21
|
-
/** List accounts or get by ID
|
|
21
|
+
/** List accounts or get by ID Bearer API key required. */
|
|
22
22
|
listOrGetAccounts(query?: QueryParams<"/v1/accounts", "get">): ReturnType<Client["GET"]>;
|
|
23
|
-
/** Delete account
|
|
23
|
+
/** Delete account Bearer API key required. */
|
|
24
24
|
deleteAccount(query?: QueryParams<"/v1/accounts", "delete">): ReturnType<Client["DELETE"]>;
|
|
25
25
|
/** Recover API key Public endpoint. */
|
|
26
26
|
recoverApiKey(body: ReqBody<"/v1/api_keys/recover", "post">): ReturnType<Client["POST"]>;
|
|
27
|
-
/** Rotate API key
|
|
27
|
+
/** Rotate API key Bearer API key required. */
|
|
28
28
|
rotateApiKey(): ReturnType<Client["POST"]>;
|
|
29
|
-
/** Create asset from existing URL
|
|
29
|
+
/** Create asset from existing URL Bearer API key required. */
|
|
30
30
|
createAsset(body: ReqBody<"/v1/assets", "post">): ReturnType<Client["POST"]>;
|
|
31
|
-
/** Update asset metadata
|
|
31
|
+
/** Update asset metadata Bearer API key required. */
|
|
32
32
|
updateAsset(body: ReqBody<"/v1/assets", "put">, query?: QueryParams<"/v1/assets", "put">): ReturnType<Client["PUT"]>;
|
|
33
|
-
/** List assets or get by ID
|
|
33
|
+
/** List assets or get by ID Bearer API key required. */
|
|
34
34
|
listOrGetAssets(query?: QueryParams<"/v1/assets", "get">): ReturnType<Client["GET"]>;
|
|
35
|
-
/** Delete asset (soft)
|
|
35
|
+
/** Delete asset (soft) Bearer API key required. */
|
|
36
36
|
deleteAsset(query?: QueryParams<"/v1/assets", "delete">): ReturnType<Client["DELETE"]>;
|
|
37
|
-
/** Agent registration (auth.md) */
|
|
37
|
+
/** Agent registration (auth.md) Public endpoint. */
|
|
38
38
|
signupAgent(body: ReqBody<"/v1/auth/agent", "post">): ReturnType<Client["POST"]>;
|
|
39
|
-
/** Initiate claim flow */
|
|
39
|
+
/** Initiate claim flow Public endpoint. */
|
|
40
40
|
agentClaim(body: ReqBody<"/v1/auth/agent/claim", "post">): ReturnType<Client["POST"]>;
|
|
41
|
-
/** Complete claim flow */
|
|
41
|
+
/** Complete claim flow Public endpoint. */
|
|
42
42
|
agentClaimComplete(body: ReqBody<"/v1/auth/agent/claim/complete", "post">): ReturnType<Client["POST"]>;
|
|
43
|
-
/** Revoke credential */
|
|
43
|
+
/** Revoke credential Public endpoint. */
|
|
44
44
|
agentRevoke(): ReturnType<Client["POST"]>;
|
|
45
45
|
/** Confirm email verification code Public endpoint. */
|
|
46
46
|
confirmVerification(body: ReqBody<"/v1/auth/verify/confirm", "post">): ReturnType<Client["POST"]>;
|
|
47
47
|
/** Request email verification code Public endpoint. */
|
|
48
48
|
requestVerification(body: ReqBody<"/v1/auth/verify/request", "post">): ReturnType<Client["POST"]>;
|
|
49
|
-
/** Create a reusable, persisted character image */
|
|
49
|
+
/** Create a reusable, persisted character image Active subscription and daily quota required. */
|
|
50
50
|
createCharacter(body: ReqBody<"/v1/characters", "post">): ReturnType<Client["POST"]>;
|
|
51
|
-
/** List your persisted characters */
|
|
51
|
+
/** List your persisted characters Bearer API key required. */
|
|
52
52
|
listCharacters(): ReturnType<Client["GET"]>;
|
|
53
|
-
/** Fetch a persisted character by id */
|
|
53
|
+
/** Fetch a persisted character by id Bearer API key required. */
|
|
54
54
|
getCharacter(id: string): ReturnType<Client["GET"]>;
|
|
55
|
-
/** Check spec
|
|
55
|
+
/** Check spec Active subscription and daily quota required. */
|
|
56
56
|
checkSpec(body: ReqBody<"/v1/check", "post">): ReturnType<Client["POST"]>;
|
|
57
|
-
/** Get check rules
|
|
57
|
+
/** Get check rules Bearer API key required. */
|
|
58
58
|
getCheckRules(): ReturnType<Client["GET"]>;
|
|
59
|
-
/** Update check rules
|
|
59
|
+
/** Update check rules Bearer API key required. */
|
|
60
60
|
updateCheckRules(body: ReqBody<"/v1/check/rules", "put">): ReturnType<Client["PUT"]>;
|
|
61
|
-
/** Evaluate whether a source video can be cloned reliably */
|
|
61
|
+
/** Evaluate whether a source video can be cloned reliably Active subscription and daily quota required. */
|
|
62
62
|
cloneCheck(body: ReqBody<"/v1/clones/check", "post">): ReturnType<Client["POST"]>;
|
|
63
|
-
/** Create a character-swapped starting frame */
|
|
63
|
+
/** Create a character-swapped starting frame Active subscription and daily quota required. */
|
|
64
64
|
cloneStartingImage(body: ReqBody<"/v1/clones/starting-image", "post">): ReturnType<Client["POST"]>;
|
|
65
|
-
/** Clone source motion with Kling
|
|
65
|
+
/** Clone source motion with Kling 3.0 Motion Control Active subscription and daily quota required. */
|
|
66
66
|
cloneVideo(body: ReqBody<"/v1/clones/video", "post">): ReturnType<Client["POST"]>;
|
|
67
|
-
/** Poll a clone video task */
|
|
67
|
+
/** Poll a clone video task Bearer API key required. */
|
|
68
68
|
getCloneVideo(id: string): ReturnType<Client["GET"]>;
|
|
69
|
-
/** Check prompt compliance */
|
|
69
|
+
/** Check prompt compliance Active subscription and daily quota required. */
|
|
70
70
|
checkCompliancePrompt(body: ReqBody<"/v1/compliance/prompt", "post">): ReturnType<Client["POST"]>;
|
|
71
|
-
/** Check video compliance */
|
|
71
|
+
/** Check video compliance Active subscription and daily quota required. */
|
|
72
72
|
checkComplianceVideo(body: ReqBody<"/v1/compliance/video", "post">): ReturnType<Client["POST"]>;
|
|
73
73
|
/** Check checkout status Public endpoint. */
|
|
74
74
|
getCheckoutStatus(query?: QueryParams<"/v1/credits/status", "get">): ReturnType<Client["GET"]>;
|
|
75
|
-
/** Burn fine-print disclaimer onto video */
|
|
75
|
+
/** Burn fine-print disclaimer onto video Active subscription and daily quota required. */
|
|
76
76
|
createDisclaimer(body: ReqBody<"/v1/disclaimer", "post">): ReturnType<Client["POST"]>;
|
|
77
|
-
/**
|
|
78
|
-
getDistributionJob(query?: QueryParams<"/v1/distribution/jobs", "get">): ReturnType<Client["GET"]>;
|
|
79
|
-
/** Extract from media Auth required. 5 credits. */
|
|
77
|
+
/** Extract from media Active subscription and daily quota required. */
|
|
80
78
|
extractMedia(body: ReqBody<"/v1/extract", "post">): ReturnType<Client["POST"]>;
|
|
81
79
|
/** API info Public endpoint. */
|
|
82
80
|
getInfo(): ReturnType<Client["GET"]>;
|
|
83
|
-
/**
|
|
84
|
-
getJob(query?: QueryParams<"/v1/jobs", "get">): ReturnType<Client["GET"]>;
|
|
85
|
-
/** Burn text overlay onto video Auth required. 5 credits. */
|
|
81
|
+
/** Burn text overlay onto video Active subscription and daily quota required. */
|
|
86
82
|
createOverlay(body: ReqBody<"/v1/overlay", "post">): ReturnType<Client["POST"]>;
|
|
87
|
-
/** Create post
|
|
83
|
+
/** Create post Bearer API key required. */
|
|
88
84
|
createPost(body: ReqBody<"/v1/posts", "post">): ReturnType<Client["POST"]>;
|
|
89
|
-
/** Update post
|
|
85
|
+
/** Update post Bearer API key required. */
|
|
90
86
|
updatePost(body: ReqBody<"/v1/posts", "put">, query?: QueryParams<"/v1/posts", "put">): ReturnType<Client["PUT"]>;
|
|
91
|
-
/** List posts or get by ID
|
|
87
|
+
/** List posts or get by ID Bearer API key required. */
|
|
92
88
|
listOrGetPosts(query?: QueryParams<"/v1/posts", "get">): ReturnType<Client["GET"]>;
|
|
93
|
-
/** Delete post
|
|
89
|
+
/** Delete post Bearer API key required. */
|
|
94
90
|
deletePost(query?: QueryParams<"/v1/posts", "delete">): ReturnType<Client["DELETE"]>;
|
|
95
91
|
/** Get pricing Public endpoint. */
|
|
96
92
|
getPricing(): ReturnType<Client["GET"]>;
|
|
97
|
-
/** Create editor project */
|
|
93
|
+
/** Create editor project Bearer API key required. */
|
|
98
94
|
createEditorProject(body: ReqBody<"/v1/projects", "post">): ReturnType<Client["POST"]>;
|
|
99
|
-
/** Update editor project */
|
|
95
|
+
/** Update editor project Bearer API key required. */
|
|
100
96
|
updateEditorProject(body: ReqBody<"/v1/projects", "put">, query?: QueryParams<"/v1/projects", "put">): ReturnType<Client["PUT"]>;
|
|
101
|
-
/** List projects or get by ID */
|
|
97
|
+
/** List projects or get by ID Bearer API key required. */
|
|
102
98
|
listOrGetEditorProjects(query?: QueryParams<"/v1/projects", "get">): ReturnType<Client["GET"]>;
|
|
103
|
-
/** Archive editor project (soft) */
|
|
99
|
+
/** Archive editor project (soft) Bearer API key required. */
|
|
104
100
|
deleteEditorProject(query?: QueryParams<"/v1/projects", "delete">): ReturnType<Client["DELETE"]>;
|
|
105
|
-
/** Create reference
|
|
101
|
+
/** Create reference Bearer API key required. */
|
|
106
102
|
createReference(body: ReqBody<"/v1/references", "post">): ReturnType<Client["POST"]>;
|
|
107
|
-
/** Update reference
|
|
103
|
+
/** Update reference Bearer API key required. */
|
|
108
104
|
updateReference(body: ReqBody<"/v1/references", "put">, query?: QueryParams<"/v1/references", "put">): ReturnType<Client["PUT"]>;
|
|
109
|
-
/** List references or get by ID
|
|
105
|
+
/** List references or get by ID Bearer API key required. */
|
|
110
106
|
listOrGetReferences(query?: QueryParams<"/v1/references", "get">): ReturnType<Client["GET"]>;
|
|
111
|
-
/** Delete reference
|
|
107
|
+
/** Delete reference Bearer API key required. */
|
|
112
108
|
deleteReference(query?: QueryParams<"/v1/references", "delete">): ReturnType<Client["DELETE"]>;
|
|
113
|
-
/** Scrape Instagram post or reel
|
|
109
|
+
/** Scrape Instagram post or reel Active subscription and daily quota required. */
|
|
114
110
|
scrapeInstagramPost(body: ReqBody<"/v1/scrape/instagram/post", "post">): ReturnType<Client["POST"]>;
|
|
115
|
-
/** Scrape Instagram post comments
|
|
111
|
+
/** Scrape Instagram post comments Active subscription and daily quota required. */
|
|
116
112
|
scrapeInstagramPostComments(body: ReqBody<"/v1/scrape/instagram/post/comments", "post">): ReturnType<Client["POST"]>;
|
|
117
|
-
/** Scrape Instagram profile
|
|
113
|
+
/** Scrape Instagram profile Active subscription and daily quota required. */
|
|
118
114
|
scrapeInstagramProfile(body: ReqBody<"/v1/scrape/instagram/profile", "post">): ReturnType<Client["POST"]>;
|
|
119
|
-
/** Scrape Instagram user posts
|
|
115
|
+
/** Scrape Instagram user posts Active subscription and daily quota required. */
|
|
120
116
|
scrapeInstagramUserPosts(body: ReqBody<"/v1/scrape/instagram/user/posts", "post">): ReturnType<Client["POST"]>;
|
|
121
|
-
/** Scrape Instagram user reels
|
|
117
|
+
/** Scrape Instagram user reels Active subscription and daily quota required. */
|
|
122
118
|
scrapeInstagramUserReels(body: ReqBody<"/v1/scrape/instagram/user/reels", "post">): ReturnType<Client["POST"]>;
|
|
123
|
-
/** Scrape TikTok profile
|
|
119
|
+
/** Scrape TikTok profile Active subscription and daily quota required. */
|
|
124
120
|
scrapeTikTokProfile(body: ReqBody<"/v1/scrape/tiktok/profile", "post">): ReturnType<Client["POST"]>;
|
|
125
|
-
/** Scrape TikTok profile videos
|
|
121
|
+
/** Scrape TikTok profile videos Active subscription and daily quota required. */
|
|
126
122
|
scrapeTikTokProfileVideos(body: ReqBody<"/v1/scrape/tiktok/profile/videos", "post">): ReturnType<Client["POST"]>;
|
|
127
|
-
/** Scrape TikTok user search
|
|
123
|
+
/** Scrape TikTok user search Active subscription and daily quota required. */
|
|
128
124
|
scrapeTikTokSearchUsers(body: ReqBody<"/v1/scrape/tiktok/search/users", "post">): ReturnType<Client["POST"]>;
|
|
129
|
-
/** Scrape TikTok trending feed
|
|
125
|
+
/** Scrape TikTok trending feed Active subscription and daily quota required. */
|
|
130
126
|
scrapeTikTokTrending(body: ReqBody<"/v1/scrape/tiktok/trending", "post">): ReturnType<Client["POST"]>;
|
|
131
|
-
/** Scrape TikTok video
|
|
127
|
+
/** Scrape TikTok video Active subscription and daily quota required. */
|
|
132
128
|
scrapeTikTokVideo(body: ReqBody<"/v1/scrape/tiktok/video", "post">): ReturnType<Client["POST"]>;
|
|
133
|
-
/** Scrape TikTok video comments
|
|
129
|
+
/** Scrape TikTok video comments Active subscription and daily quota required. */
|
|
134
130
|
scrapeTikTokVideoComments(body: ReqBody<"/v1/scrape/tiktok/video/comments", "post">): ReturnType<Client["POST"]>;
|
|
135
|
-
/** Scrape TikTok video transcript
|
|
131
|
+
/** Scrape TikTok video transcript Active subscription and daily quota required. */
|
|
136
132
|
scrapeTikTokVideoTranscript(body: ReqBody<"/v1/scrape/tiktok/video/transcript", "post">): ReturnType<Client["POST"]>;
|
|
137
133
|
/** Create subscription Public endpoint. */
|
|
138
134
|
createSubscription(body: ReqBody<"/v1/subscribe", "post">): ReturnType<Client["POST"]>;
|
|
139
|
-
/** Transcribe media
|
|
135
|
+
/** Transcribe media Active subscription and daily quota required. */
|
|
140
136
|
transcribeMedia(body: ReqBody<"/v1/transcribe", "post">): ReturnType<Client["POST"]>;
|
|
141
|
-
/** Upload file
|
|
142
|
-
uploadFile(): ReturnType<Client["POST"]>;
|
|
143
|
-
/** Upload from URL
|
|
137
|
+
/** Upload file Bearer API key required. */
|
|
138
|
+
uploadFile(body: Blob | ArrayBuffer | ReadableStream): ReturnType<Client["POST"]>;
|
|
139
|
+
/** Upload from URL Bearer API key required. */
|
|
144
140
|
uploadFromUrl(body: ReqBody<"/v1/upload/url", "post">): ReturnType<Client["POST"]>;
|
|
145
|
-
/** Get daily usage */
|
|
141
|
+
/** Get daily usage Bearer API key required. */
|
|
146
142
|
getUsage(): ReturnType<Client["GET"]>;
|
|
147
|
-
/**
|
|
148
|
-
addVideo(body: ReqBody<"/v1/videos/add", "post">): ReturnType<Client["POST"]>;
|
|
149
|
-
/** Import an Instagram video into VidJutsu Auth required. 1 credits. */
|
|
143
|
+
/** Import an Instagram video into VidJutsu Active subscription and daily quota required. */
|
|
150
144
|
downloadInstagramVideo(body: ReqBody<"/v1/videos/download/instagram", "post">): ReturnType<Client["POST"]>;
|
|
151
|
-
/** Import a TikTok video into VidJutsu
|
|
145
|
+
/** Import a TikTok video into VidJutsu Active subscription and daily quota required. */
|
|
152
146
|
downloadTikTokVideo(body: ReqBody<"/v1/videos/download/tiktok", "post">): ReturnType<Client["POST"]>;
|
|
153
|
-
/** Watch media
|
|
147
|
+
/** Watch media Active subscription and daily quota required. */
|
|
154
148
|
watchMedia(body: ReqBody<"/v1/watch", "post">): ReturnType<Client["POST"]>;
|
|
155
149
|
}
|
|
156
150
|
export declare function bindMethods(client: Client): VidJutsuMethods;
|
package/dist/methods.js
CHANGED
|
@@ -90,18 +90,12 @@ export function bindMethods(client) {
|
|
|
90
90
|
createDisclaimer(body) {
|
|
91
91
|
return client.POST("/v1/disclaimer", { body });
|
|
92
92
|
},
|
|
93
|
-
getDistributionJob(query) {
|
|
94
|
-
return client.GET("/v1/distribution/jobs", { params: { query } });
|
|
95
|
-
},
|
|
96
93
|
extractMedia(body) {
|
|
97
94
|
return client.POST("/v1/extract", { body });
|
|
98
95
|
},
|
|
99
96
|
getInfo() {
|
|
100
97
|
return client.GET("/v1/info", {});
|
|
101
98
|
},
|
|
102
|
-
getJob(query) {
|
|
103
|
-
return client.GET("/v1/jobs", { params: { query } });
|
|
104
|
-
},
|
|
105
99
|
createOverlay(body) {
|
|
106
100
|
return client.POST("/v1/overlay", { body });
|
|
107
101
|
},
|
|
@@ -186,8 +180,12 @@ export function bindMethods(client) {
|
|
|
186
180
|
transcribeMedia(body) {
|
|
187
181
|
return client.POST("/v1/transcribe", { body });
|
|
188
182
|
},
|
|
189
|
-
uploadFile() {
|
|
190
|
-
return client.POST("/v1/upload", {
|
|
183
|
+
uploadFile(body) {
|
|
184
|
+
return client.POST("/v1/upload", {
|
|
185
|
+
body: body,
|
|
186
|
+
bodySerializer: (b) => b,
|
|
187
|
+
headers: { "Content-Type": "application/octet-stream" },
|
|
188
|
+
});
|
|
191
189
|
},
|
|
192
190
|
uploadFromUrl(body) {
|
|
193
191
|
return client.POST("/v1/upload/url", { body });
|
|
@@ -195,9 +193,6 @@ export function bindMethods(client) {
|
|
|
195
193
|
getUsage() {
|
|
196
194
|
return client.GET("/v1/usage", {});
|
|
197
195
|
},
|
|
198
|
-
addVideo(body) {
|
|
199
|
-
return client.POST("/v1/videos/add", { body });
|
|
200
|
-
},
|
|
201
196
|
downloadInstagramVideo(body) {
|
|
202
197
|
return client.POST("/v1/videos/download/instagram", { body });
|
|
203
198
|
},
|
package/install.sh
CHANGED
|
@@ -43,45 +43,6 @@ detect_platform() {
|
|
|
43
43
|
info "Detected platform: ${BOLD}${PLATFORM}${RESET}"
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
# --- Prerequisites ---
|
|
47
|
-
|
|
48
|
-
ensure_node() {
|
|
49
|
-
if command -v node >/dev/null 2>&1; then
|
|
50
|
-
success "Node.js $(node --version) found"
|
|
51
|
-
return
|
|
52
|
-
fi
|
|
53
|
-
|
|
54
|
-
error "Node.js is required. Install it from https://nodejs.org"
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
ensure_bun() {
|
|
58
|
-
if command -v bun >/dev/null 2>&1; then
|
|
59
|
-
success "Bun $(bun --version) found"
|
|
60
|
-
return
|
|
61
|
-
fi
|
|
62
|
-
|
|
63
|
-
info "Installing Bun..."
|
|
64
|
-
curl -fsSL https://bun.sh/install | bash
|
|
65
|
-
export BUN_INSTALL="$HOME/.bun"
|
|
66
|
-
export PATH="$BUN_INSTALL/bin:$PATH"
|
|
67
|
-
success "Bun installed: $(bun --version)"
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
ensure_typescript() {
|
|
71
|
-
if command -v tsc >/dev/null 2>&1; then
|
|
72
|
-
success "TypeScript $(tsc --version) found"
|
|
73
|
-
return
|
|
74
|
-
fi
|
|
75
|
-
|
|
76
|
-
info "Installing TypeScript globally..."
|
|
77
|
-
if command -v bun >/dev/null 2>&1; then
|
|
78
|
-
bun add -g typescript
|
|
79
|
-
elif command -v npm >/dev/null 2>&1; then
|
|
80
|
-
npm install -g typescript
|
|
81
|
-
fi
|
|
82
|
-
success "TypeScript installed"
|
|
83
|
-
}
|
|
84
|
-
|
|
85
46
|
# --- Version Detection ---
|
|
86
47
|
|
|
87
48
|
get_version() {
|
|
@@ -180,9 +141,6 @@ main() {
|
|
|
180
141
|
printf "\n${BOLD} VidJutsu CLI Installer${RESET}\n\n"
|
|
181
142
|
|
|
182
143
|
detect_platform
|
|
183
|
-
ensure_node
|
|
184
|
-
ensure_bun
|
|
185
|
-
ensure_typescript
|
|
186
144
|
get_version
|
|
187
145
|
install_binary
|
|
188
146
|
verify
|
|
@@ -190,7 +148,7 @@ main() {
|
|
|
190
148
|
printf "\n${GREEN}${BOLD} ✓ VidJutsu CLI v${VERSION} installed successfully${RESET}\n"
|
|
191
149
|
printf "\n Get started:\n"
|
|
192
150
|
printf " ${CYAN}vidjutsu auth${RESET} — authenticate your account\n"
|
|
193
|
-
printf " ${CYAN}vidjutsu
|
|
151
|
+
printf " ${CYAN}vidjutsu clone --help${RESET} — clone a staged short video\n"
|
|
194
152
|
printf " ${CYAN}vidjutsu --help${RESET} — see all commands\n\n"
|
|
195
153
|
}
|
|
196
154
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vidjutsu",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "VidJutsu — video intelligence API. SDK + CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
],
|
|
21
21
|
"scripts": {
|
|
22
22
|
"generate": "bun scripts/generate-schema.ts && bun scripts/generate-methods.ts",
|
|
23
|
+
"test": "bun test",
|
|
24
|
+
"check:release": "bun scripts/check-release.ts",
|
|
23
25
|
"build": "bun run build:sdk && bun run build:cli",
|
|
24
26
|
"build:sdk": "tsc",
|
|
25
27
|
"build:cli": "bun build src/cli/index.ts --outfile dist/cli/index.mjs --target node",
|
|
@@ -29,7 +31,7 @@
|
|
|
29
31
|
"build:binary:linux-x64": "bun build src/cli/index.ts --compile --target=bun-linux-x64 --outfile dist/vidjutsu-linux-x64",
|
|
30
32
|
"build:binary:linux-arm64": "bun build src/cli/index.ts --compile --target=bun-linux-arm64 --outfile dist/vidjutsu-linux-arm64",
|
|
31
33
|
"dev": "bun run src/cli/index.ts",
|
|
32
|
-
"prepublishOnly": "bun run generate && bun run build"
|
|
34
|
+
"prepublishOnly": "bun run generate && bun run check:release && bun test && bun run build"
|
|
33
35
|
},
|
|
34
36
|
"dependencies": {
|
|
35
37
|
"citty": "^0.1",
|
package/dist/distribution.d.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type createFetchClient from "openapi-fetch";
|
|
2
|
-
import type { components, paths } from "./schema.js";
|
|
3
|
-
type FetchClient = ReturnType<typeof createFetchClient<paths>>;
|
|
4
|
-
type Job = components["schemas"]["DistributionJob"];
|
|
5
|
-
type AddVideoRequest = components["schemas"]["AddVideoRequest"];
|
|
6
|
-
export interface MutationOptions {
|
|
7
|
-
idempotencyKey?: string;
|
|
8
|
-
}
|
|
9
|
-
export interface WaitOptions {
|
|
10
|
-
timeoutMs?: number;
|
|
11
|
-
pollIntervalMs?: number;
|
|
12
|
-
}
|
|
13
|
-
export interface DistributionNamespaces {
|
|
14
|
-
videos: {
|
|
15
|
-
add(body: AddVideoRequest, options?: MutationOptions): Promise<Job>;
|
|
16
|
-
};
|
|
17
|
-
jobs: {
|
|
18
|
-
get(jobId: string): Promise<Job>;
|
|
19
|
-
waitForResult(jobId: string, options?: WaitOptions): Promise<Job>;
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
export declare function bindDistribution(client: FetchClient): DistributionNamespaces;
|
|
23
|
-
export {};
|
package/dist/distribution.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
export function bindDistribution(client) {
|
|
2
|
-
const jobs = {
|
|
3
|
-
async get(jobId) {
|
|
4
|
-
const result = await client.GET("/v1/distribution/jobs", {
|
|
5
|
-
params: { query: { id: jobId } },
|
|
6
|
-
});
|
|
7
|
-
return requireData(result, "get distribution job");
|
|
8
|
-
},
|
|
9
|
-
async waitForResult(jobId, options = {}) {
|
|
10
|
-
const timeoutMs = options.timeoutMs ?? 10 * 60_000;
|
|
11
|
-
const pollIntervalMs = options.pollIntervalMs ?? 1_000;
|
|
12
|
-
const deadline = Date.now() + timeoutMs;
|
|
13
|
-
while (true) {
|
|
14
|
-
const job = await jobs.get(jobId);
|
|
15
|
-
if (job.status === "completed")
|
|
16
|
-
return job;
|
|
17
|
-
if (job.status === "failed" || job.status === "cancelled") {
|
|
18
|
-
throw new Error(job.error?.message ?? `Distribution job ${job.status}`);
|
|
19
|
-
}
|
|
20
|
-
if (Date.now() >= deadline)
|
|
21
|
-
throw new Error(`Timed out waiting for distribution job ${jobId}`);
|
|
22
|
-
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
};
|
|
26
|
-
return {
|
|
27
|
-
videos: {
|
|
28
|
-
async add(body, options) {
|
|
29
|
-
const result = await client.POST("/v1/videos/add", {
|
|
30
|
-
body,
|
|
31
|
-
params: { header: idempotencyHeader(options) },
|
|
32
|
-
});
|
|
33
|
-
return requireData(result, "add video");
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
jobs,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
function idempotencyHeader(options) {
|
|
40
|
-
return options?.idempotencyKey ? { "Idempotency-Key": options.idempotencyKey } : {};
|
|
41
|
-
}
|
|
42
|
-
function requireData(result, operation) {
|
|
43
|
-
if (result.data !== undefined)
|
|
44
|
-
return result.data;
|
|
45
|
-
const message = extractErrorMessage(result.error) ?? `Vidjutsu failed to ${operation} (HTTP ${result.response.status})`;
|
|
46
|
-
throw new Error(message);
|
|
47
|
-
}
|
|
48
|
-
function extractErrorMessage(error) {
|
|
49
|
-
if (!error || typeof error !== "object")
|
|
50
|
-
return undefined;
|
|
51
|
-
if ("message" in error && typeof error.message === "string")
|
|
52
|
-
return error.message;
|
|
53
|
-
if ("error" in error && typeof error.error === "string")
|
|
54
|
-
return error.error;
|
|
55
|
-
return undefined;
|
|
56
|
-
}
|
package/dist/jobs.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type createFetchClient from "openapi-fetch";
|
|
2
|
-
import type { components, paths } from "./schema.js";
|
|
3
|
-
import type { WaitOptions } from "./distribution.js";
|
|
4
|
-
type FetchClient = ReturnType<typeof createFetchClient<paths>>;
|
|
5
|
-
type MediaJob = components["schemas"]["MediaJob"];
|
|
6
|
-
export interface JobsNamespaces {
|
|
7
|
-
mediaJobs: {
|
|
8
|
-
get(jobId: string): Promise<MediaJob>;
|
|
9
|
-
awaitJob(jobId: string, options?: WaitOptions): Promise<MediaJob>;
|
|
10
|
-
};
|
|
11
|
-
awaitJob(jobId: string, options?: WaitOptions): Promise<MediaJob>;
|
|
12
|
-
}
|
|
13
|
-
export declare function bindJobs(client: FetchClient): JobsNamespaces;
|
|
14
|
-
export {};
|
package/dist/jobs.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
export function bindJobs(client) {
|
|
2
|
-
const mediaJobs = {
|
|
3
|
-
async get(jobId) {
|
|
4
|
-
const result = await client.GET("/v1/jobs", {
|
|
5
|
-
params: { query: { id: jobId } },
|
|
6
|
-
});
|
|
7
|
-
return requireData(result, "get media job");
|
|
8
|
-
},
|
|
9
|
-
async awaitJob(jobId, options = {}) {
|
|
10
|
-
const timeoutMs = options.timeoutMs ?? 10 * 60_000;
|
|
11
|
-
const pollIntervalMs = options.pollIntervalMs ?? 1_000;
|
|
12
|
-
if (timeoutMs <= 0 || pollIntervalMs <= 0) {
|
|
13
|
-
throw new Error("timeoutMs and pollIntervalMs must be positive");
|
|
14
|
-
}
|
|
15
|
-
const deadline = Date.now() + timeoutMs;
|
|
16
|
-
while (true) {
|
|
17
|
-
const job = await mediaJobs.get(jobId);
|
|
18
|
-
if (job.status === "completed")
|
|
19
|
-
return job;
|
|
20
|
-
if (job.status === "failed" || job.status === "cancelled") {
|
|
21
|
-
throw new Error(job.error?.message ?? `Media job ${job.status}`);
|
|
22
|
-
}
|
|
23
|
-
if (Date.now() >= deadline) {
|
|
24
|
-
throw new Error(`Timed out waiting for media job ${jobId}`);
|
|
25
|
-
}
|
|
26
|
-
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
return {
|
|
31
|
-
mediaJobs,
|
|
32
|
-
awaitJob: mediaJobs.awaitJob,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
function requireData(result, operation) {
|
|
36
|
-
if (result.data !== undefined)
|
|
37
|
-
return result.data;
|
|
38
|
-
const message = extractErrorMessage(result.error) ??
|
|
39
|
-
`VidJutsu failed to ${operation} (HTTP ${result.response.status})`;
|
|
40
|
-
throw new Error(message);
|
|
41
|
-
}
|
|
42
|
-
function extractErrorMessage(error) {
|
|
43
|
-
if (!error || typeof error !== "object")
|
|
44
|
-
return undefined;
|
|
45
|
-
if ("message" in error && typeof error.message === "string")
|
|
46
|
-
return error.message;
|
|
47
|
-
if ("error" in error && typeof error.error === "string")
|
|
48
|
-
return error.error;
|
|
49
|
-
return undefined;
|
|
50
|
-
}
|