@rohitaryal/whisk-api 3.2.0 → 4.0.1
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 +141 -25
- package/dist/Cli.js +57 -38
- package/dist/Constants.d.ts +5 -1
- package/dist/Constants.js +7 -1
- package/dist/Media.js +7 -5
- package/dist/Project.d.ts +31 -4
- package/dist/Project.js +95 -3
- package/dist/Types.d.ts +13 -1
- package/dist/Utils.d.ts +6 -5
- package/dist/Utils.js +28 -10
- package/dist/Whisk.d.ts +11 -1
- package/dist/Whisk.js +36 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,11 +16,12 @@ bun i -g @rohitaryal/whisk-api
|
|
|
16
16
|
## Features
|
|
17
17
|
|
|
18
18
|
1. Text to Image using `IMAGEN_3_5`
|
|
19
|
-
2. Image to Video (Animation) using `
|
|
20
|
-
3. Image Refinement (Editing/Inpainting
|
|
21
|
-
4. Image to Text
|
|
19
|
+
2. Image to Video (Animation) using `VEO_3_1`
|
|
20
|
+
3. Image Refinement (Editing/Inpainting)
|
|
21
|
+
4. Image to Text (Caption Generation)
|
|
22
22
|
5. Project & Media Management
|
|
23
|
-
6.
|
|
23
|
+
6. Upload & Reference Images
|
|
24
|
+
7. Command line support
|
|
24
25
|
|
|
25
26
|
## Usage
|
|
26
27
|
|
|
@@ -62,6 +63,12 @@ Make sure you have:
|
|
|
62
63
|
> - PowerShell: `"$env:COOKIE"`
|
|
63
64
|
> - Command Prompt: `"%COOKIE%"`
|
|
64
65
|
|
|
66
|
+
- Creating a new project
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
whisk project --name "My Project" --cookie "$COOKIE"
|
|
70
|
+
```
|
|
71
|
+
|
|
65
72
|
- Generating image with prompt
|
|
66
73
|
|
|
67
74
|
```bash
|
|
@@ -95,55 +102,164 @@ Make sure you have:
|
|
|
95
102
|
whisk caption --file /path/to/img.webp --count 3 --cookie "$COOKIE"
|
|
96
103
|
```
|
|
97
104
|
|
|
105
|
+
- Fetching an existing media by ID
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
whisk fetch "__MEDIA__ID__HERE__" --cookie "$COOKIE"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
- Uploading an image as reference
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
whisk upload /path/to/image.jpg --category SUBJECT --project "__PROJECT__ID__" --cookie "$COOKIE"
|
|
115
|
+
```
|
|
116
|
+
|
|
98
117
|
- Deleting media from the cloud
|
|
99
118
|
|
|
100
119
|
```bash
|
|
101
120
|
whisk delete "__MEDIA__ID__HERE__" --cookie "$COOKIE"
|
|
102
121
|
```
|
|
103
122
|
|
|
104
|
-
Full
|
|
123
|
+
Full CLI commands help:
|
|
105
124
|
|
|
106
125
|
```text
|
|
107
|
-
whisk
|
|
126
|
+
whisk <cmd> [args]
|
|
127
|
+
|
|
128
|
+
Commands:
|
|
129
|
+
whisk project Generate a new project
|
|
130
|
+
whisk generate Generate new images using a temporary project
|
|
131
|
+
whisk animate <mediaId> Animate an existing landscape image into a video
|
|
132
|
+
whisk refine <mediaId> Edit/Refine an existing image
|
|
133
|
+
whisk caption Generate captions for a local image file
|
|
134
|
+
whisk fetch <mediaId> Download an existing generated media by ID
|
|
135
|
+
whisk upload <file> Upload an image to be used as reference later
|
|
136
|
+
whisk delete <mediaId> Delete a generated media from the cloud
|
|
108
137
|
|
|
109
138
|
Options:
|
|
110
|
-
|
|
111
|
-
-h, --help
|
|
112
|
-
|
|
113
|
-
-m, --model Image generation model (Default: IMAGEN_3_5)
|
|
114
|
-
-a, --aspect Aspect ratio (SQUARE, PORTRAIT, LANDSCAPE)
|
|
115
|
-
-s, --seed Seed value (0 for random)
|
|
116
|
-
-d, --dir Output directory
|
|
117
|
-
-c, --cookie Google account cookie
|
|
139
|
+
-c, --cookie Google account cookie [required]
|
|
140
|
+
-h, --help Show help
|
|
141
|
+
--version Show version number
|
|
118
142
|
```
|
|
119
143
|
|
|
120
|
-
|
|
144
|
+
<details>
|
|
145
|
+
<summary>Project Command</summary>
|
|
146
|
+
|
|
147
|
+
```text
|
|
148
|
+
whisk project
|
|
149
|
+
|
|
150
|
+
Options:
|
|
151
|
+
--name Project name [default: "Whisk-CLI project"]
|
|
152
|
+
-c, --cookie Google account cookie [required]
|
|
153
|
+
```
|
|
154
|
+
</details>
|
|
155
|
+
|
|
156
|
+
<details>
|
|
157
|
+
<summary>Generate Command</summary>
|
|
158
|
+
|
|
159
|
+
```text
|
|
160
|
+
whisk generate
|
|
161
|
+
|
|
162
|
+
Options:
|
|
163
|
+
-p, --prompt Description of the image [required]
|
|
164
|
+
-m, --model Image generation model [default: "IMAGEN_3_5"]
|
|
165
|
+
-a, --aspect Aspect ratio (SQUARE, PORTRAIT, LANDSCAPE) [default: "LANDSCAPE"]
|
|
166
|
+
-s, --seed Seed value (0 for random) [default: 0]
|
|
167
|
+
-d, --dir Output directory [default: "./output"]
|
|
168
|
+
-c, --cookie Google account cookie [required]
|
|
169
|
+
```
|
|
170
|
+
</details>
|
|
171
|
+
|
|
172
|
+
<details>
|
|
173
|
+
<summary>Animate Command</summary>
|
|
121
174
|
|
|
122
175
|
```text
|
|
123
176
|
whisk animate <mediaId>
|
|
124
177
|
|
|
125
178
|
Positionals:
|
|
126
|
-
mediaId The ID of the image to animate
|
|
179
|
+
mediaId The ID of the image to animate [required]
|
|
180
|
+
|
|
181
|
+
Options:
|
|
182
|
+
-s, --script Prompt/Script for the video animation [required]
|
|
183
|
+
-m, --model Video generation model [default: "VEO_3_1"]
|
|
184
|
+
-d, --dir Output directory [default: "./output"]
|
|
185
|
+
-c, --cookie Google account cookie [required]
|
|
186
|
+
```
|
|
187
|
+
</details>
|
|
188
|
+
|
|
189
|
+
<details>
|
|
190
|
+
<summary>Refine Command</summary>
|
|
191
|
+
|
|
192
|
+
```text
|
|
193
|
+
whisk refine <mediaId>
|
|
194
|
+
|
|
195
|
+
Positionals:
|
|
196
|
+
mediaId The ID of the image to refine [required]
|
|
197
|
+
|
|
198
|
+
Options:
|
|
199
|
+
-p, --prompt Instruction for editing (e.g., 'Make it snowy') [required]
|
|
200
|
+
-d, --dir Output directory [default: "./output"]
|
|
201
|
+
-c, --cookie Google account cookie [required]
|
|
202
|
+
```
|
|
203
|
+
</details>
|
|
204
|
+
|
|
205
|
+
<details>
|
|
206
|
+
<summary>Caption Command</summary>
|
|
207
|
+
|
|
208
|
+
```text
|
|
209
|
+
whisk caption
|
|
127
210
|
|
|
128
211
|
Options:
|
|
129
|
-
-
|
|
130
|
-
-
|
|
131
|
-
-
|
|
132
|
-
-c, --cookie Google account cookie
|
|
212
|
+
-f, --file Path to local image file [required]
|
|
213
|
+
-n, --count Number of captions [default: 1]
|
|
214
|
+
-c, --cookie Google account cookie [required]
|
|
133
215
|
```
|
|
216
|
+
</details>
|
|
134
217
|
|
|
135
|
-
|
|
218
|
+
<details>
|
|
219
|
+
<summary>Fetch Command</summary>
|
|
136
220
|
|
|
137
221
|
```text
|
|
138
222
|
whisk fetch <mediaId>
|
|
139
223
|
|
|
140
224
|
Positionals:
|
|
141
|
-
mediaId Unique ID of generated media
|
|
225
|
+
mediaId Unique ID of generated media [required]
|
|
142
226
|
|
|
143
227
|
Options:
|
|
144
|
-
-d, --dir Output directory
|
|
145
|
-
-c, --cookie Google account cookie
|
|
228
|
+
-d, --dir Output directory [default: "./output"]
|
|
229
|
+
-c, --cookie Google account cookie [required]
|
|
146
230
|
```
|
|
231
|
+
</details>
|
|
232
|
+
|
|
233
|
+
<details>
|
|
234
|
+
<summary>Upload Command</summary>
|
|
235
|
+
|
|
236
|
+
```text
|
|
237
|
+
whisk upload <file>
|
|
238
|
+
|
|
239
|
+
Positionals:
|
|
240
|
+
file Path to local image file [required]
|
|
241
|
+
|
|
242
|
+
Options:
|
|
243
|
+
--category, -ca Category of reference [required]
|
|
244
|
+
Choices: SUBJECT, SCENE, STYLE
|
|
245
|
+
--project, -pr Project/Workflow ID [required]
|
|
246
|
+
-c, --cookie Google account cookie [required]
|
|
247
|
+
```
|
|
248
|
+
</details>
|
|
249
|
+
|
|
250
|
+
<details>
|
|
251
|
+
<summary>Delete Command</summary>
|
|
252
|
+
|
|
253
|
+
```text
|
|
254
|
+
whisk delete <mediaId>
|
|
255
|
+
|
|
256
|
+
Positionals:
|
|
257
|
+
mediaId Unique ID of generated media to delete [required]
|
|
258
|
+
|
|
259
|
+
Options:
|
|
260
|
+
-c, --cookie Google account cookie [required]
|
|
261
|
+
```
|
|
262
|
+
</details>
|
|
147
263
|
|
|
148
264
|
</details>
|
|
149
265
|
|
|
@@ -186,7 +302,7 @@ Options:
|
|
|
186
302
|
const refinedImage = await baseImage.refine("Make it raining neon rain");
|
|
187
303
|
|
|
188
304
|
// 3. Animate (Video)
|
|
189
|
-
const video = await refinedImage.animate("Camera flies through the streets", "
|
|
305
|
+
const video = await refinedImage.animate("Camera flies through the streets", "VEO_3_1_I2V_12STEP");
|
|
190
306
|
|
|
191
307
|
video.save("./videos");
|
|
192
308
|
```
|
package/dist/Cli.js
CHANGED
|
@@ -3,11 +3,33 @@ import yargs from "yargs";
|
|
|
3
3
|
import { hideBin } from 'yargs/helpers';
|
|
4
4
|
import { Whisk } from "./Whisk.js";
|
|
5
5
|
import { imageToBase64 } from "./Utils.js";
|
|
6
|
-
import { ImageAspectRatio, ImageGenerationModel, VideoGenerationModel } from "./Constants.js";
|
|
6
|
+
import { ImageAspectRatio, ImageGenerationModel, MediaCategory, VideoGenerationModel } from "./Constants.js";
|
|
7
7
|
const y = yargs(hideBin(process.argv));
|
|
8
8
|
await y
|
|
9
9
|
.scriptName("whisk")
|
|
10
10
|
.usage('$0 <cmd> [args]')
|
|
11
|
+
.option("cookie", {
|
|
12
|
+
alias: "c",
|
|
13
|
+
describe: "Google account cookie",
|
|
14
|
+
type: "string",
|
|
15
|
+
demandOption: true,
|
|
16
|
+
})
|
|
17
|
+
.command("project", "Generate a new project", (yargs) => {
|
|
18
|
+
return yargs
|
|
19
|
+
.option("name", {
|
|
20
|
+
describe: "Project name",
|
|
21
|
+
demandOption: false,
|
|
22
|
+
type: "string",
|
|
23
|
+
default: "Whisk-CLI project"
|
|
24
|
+
});
|
|
25
|
+
}, async (argv) => {
|
|
26
|
+
const whisk = new Whisk(argv.cookie);
|
|
27
|
+
await whisk.account.refresh();
|
|
28
|
+
console.log(whisk.account.toString());
|
|
29
|
+
console.log("[*] Creating new project...");
|
|
30
|
+
const project = await whisk.newProject(argv.name);
|
|
31
|
+
console.log("[+] Project ID:", project.projectId);
|
|
32
|
+
})
|
|
11
33
|
.command("generate", "Generate new images using a temporary project", (yargs) => {
|
|
12
34
|
return yargs
|
|
13
35
|
.option("prompt", {
|
|
@@ -41,12 +63,6 @@ await y
|
|
|
41
63
|
describe: "Output directory",
|
|
42
64
|
type: "string",
|
|
43
65
|
default: "./output",
|
|
44
|
-
})
|
|
45
|
-
.option("cookie", {
|
|
46
|
-
alias: "c",
|
|
47
|
-
describe: "Google account cookie",
|
|
48
|
-
type: "string",
|
|
49
|
-
demandOption: true,
|
|
50
66
|
});
|
|
51
67
|
}, async (argv) => {
|
|
52
68
|
const whisk = new Whisk(argv.cookie);
|
|
@@ -91,7 +107,7 @@ await y
|
|
|
91
107
|
alias: "m",
|
|
92
108
|
describe: "Video generation model",
|
|
93
109
|
type: "string",
|
|
94
|
-
default: "
|
|
110
|
+
default: "VEO_3_1",
|
|
95
111
|
choices: Object.keys(VideoGenerationModel)
|
|
96
112
|
})
|
|
97
113
|
.option("dir", {
|
|
@@ -99,12 +115,6 @@ await y
|
|
|
99
115
|
describe: "Output directory",
|
|
100
116
|
type: "string",
|
|
101
117
|
default: "./output",
|
|
102
|
-
})
|
|
103
|
-
.option("cookie", {
|
|
104
|
-
alias: "c",
|
|
105
|
-
describe: "Google account cookie",
|
|
106
|
-
type: "string",
|
|
107
|
-
demandOption: true,
|
|
108
118
|
});
|
|
109
119
|
}, async (argv) => {
|
|
110
120
|
const whisk = new Whisk(argv.cookie);
|
|
@@ -141,12 +151,6 @@ await y
|
|
|
141
151
|
describe: "Output directory",
|
|
142
152
|
type: "string",
|
|
143
153
|
default: "./output",
|
|
144
|
-
})
|
|
145
|
-
.option("cookie", {
|
|
146
|
-
alias: "c",
|
|
147
|
-
describe: "Google account cookie",
|
|
148
|
-
type: "string",
|
|
149
|
-
demandOption: true,
|
|
150
154
|
});
|
|
151
155
|
}, async (argv) => {
|
|
152
156
|
const whisk = new Whisk(argv.cookie);
|
|
@@ -178,12 +182,6 @@ await y
|
|
|
178
182
|
describe: "Number of captions",
|
|
179
183
|
type: "number",
|
|
180
184
|
default: 1
|
|
181
|
-
})
|
|
182
|
-
.option("cookie", {
|
|
183
|
-
alias: "c",
|
|
184
|
-
describe: "Google account cookie",
|
|
185
|
-
type: "string",
|
|
186
|
-
demandOption: true,
|
|
187
185
|
});
|
|
188
186
|
}, async (argv) => {
|
|
189
187
|
const whisk = new Whisk(argv.cookie);
|
|
@@ -216,12 +214,6 @@ await y
|
|
|
216
214
|
describe: "Output directory",
|
|
217
215
|
type: "string",
|
|
218
216
|
default: "./output",
|
|
219
|
-
})
|
|
220
|
-
.option("cookie", {
|
|
221
|
-
alias: "c",
|
|
222
|
-
describe: "Google account cookie",
|
|
223
|
-
type: "string",
|
|
224
|
-
demandOption: true,
|
|
225
217
|
});
|
|
226
218
|
}, async (argv) => {
|
|
227
219
|
const whisk = new Whisk(argv.cookie);
|
|
@@ -237,16 +229,43 @@ await y
|
|
|
237
229
|
console.error("[!] Fetch failed:", error);
|
|
238
230
|
}
|
|
239
231
|
})
|
|
240
|
-
.command("
|
|
232
|
+
.command("upload <file>", "Upload an image to be used as reference later. References can't be used currently", (yargs) => {
|
|
241
233
|
return yargs
|
|
242
|
-
.positional("
|
|
243
|
-
describe: "
|
|
234
|
+
.positional("file", {
|
|
235
|
+
describe: "Path to local image file",
|
|
236
|
+
type: "string",
|
|
237
|
+
demandOption: true
|
|
238
|
+
})
|
|
239
|
+
.option("category", {
|
|
240
|
+
alias: "ca",
|
|
241
|
+
describe: "Category of reference",
|
|
244
242
|
type: "string",
|
|
245
243
|
demandOption: true,
|
|
244
|
+
choices: Object.keys(MediaCategory)
|
|
246
245
|
})
|
|
247
|
-
.option("
|
|
248
|
-
alias: "
|
|
249
|
-
describe: "
|
|
246
|
+
.option("project", {
|
|
247
|
+
alias: "pr",
|
|
248
|
+
describe: "Project/Workflow ID",
|
|
249
|
+
type: "string",
|
|
250
|
+
demandOption: true
|
|
251
|
+
});
|
|
252
|
+
}, async (argv) => {
|
|
253
|
+
const whisk = new Whisk(argv.cookie);
|
|
254
|
+
await whisk.account.refresh();
|
|
255
|
+
console.log(whisk.account.toString());
|
|
256
|
+
console.log("[*] Generating caption for image...");
|
|
257
|
+
const base64 = await imageToBase64(argv.file);
|
|
258
|
+
// Split at the comma to get the raw base64 string
|
|
259
|
+
const rawBase64 = base64.split(",")[1];
|
|
260
|
+
const captionedImage = await Whisk.generateCaption(rawBase64, whisk.account);
|
|
261
|
+
console.log("[*] Uploading image...");
|
|
262
|
+
let mediaId = await Whisk.uploadImage(rawBase64, captionedImage[0], MediaCategory[argv.category], argv.project, whisk.account);
|
|
263
|
+
console.log("[+] ID:", mediaId);
|
|
264
|
+
})
|
|
265
|
+
.command("delete <mediaId>", "Delete a generated media from the cloud", (yargs) => {
|
|
266
|
+
return yargs
|
|
267
|
+
.positional("mediaId", {
|
|
268
|
+
describe: "Unique ID of generated media to delete",
|
|
250
269
|
type: "string",
|
|
251
270
|
demandOption: true,
|
|
252
271
|
});
|
package/dist/Constants.d.ts
CHANGED
|
@@ -33,5 +33,9 @@ export declare const ImageRefinementModel: Readonly<{
|
|
|
33
33
|
}>;
|
|
34
34
|
export declare const VideoGenerationModel: Readonly<{
|
|
35
35
|
readonly VEO_3_1: "VEO_3_1_I2V_12STEP";
|
|
36
|
-
|
|
36
|
+
}>;
|
|
37
|
+
export declare const MediaCategory: Readonly<{
|
|
38
|
+
readonly SUBJECT: "MEDIA_CATEGORY_SUBJECT";
|
|
39
|
+
readonly SCENE: "MEDIA_CATEGORY_SCENE";
|
|
40
|
+
readonly STYLE: "MEDIA_CATEGORY_STYLE";
|
|
37
41
|
}>;
|
package/dist/Constants.js
CHANGED
|
@@ -33,5 +33,11 @@ export const ImageRefinementModel = Object.freeze({
|
|
|
33
33
|
});
|
|
34
34
|
export const VideoGenerationModel = Object.freeze({
|
|
35
35
|
VEO_3_1: "VEO_3_1_I2V_12STEP",
|
|
36
|
-
|
|
36
|
+
// https://github.com/rohitaryal/whisk-api/issues/13
|
|
37
|
+
// VEO_FAST_3_1: "veo_3_1_i2v_s_fast",
|
|
38
|
+
});
|
|
39
|
+
export const MediaCategory = Object.freeze({
|
|
40
|
+
SUBJECT: "MEDIA_CATEGORY_SUBJECT",
|
|
41
|
+
SCENE: "MEDIA_CATEGORY_SCENE",
|
|
42
|
+
STYLE: "MEDIA_CATEGORY_STYLE",
|
|
37
43
|
});
|
package/dist/Media.js
CHANGED
|
@@ -150,21 +150,23 @@ export class Media {
|
|
|
150
150
|
// Await for 2 second before each request
|
|
151
151
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
152
152
|
if (videoResults.status === "MEDIA_GENERATION_STATUS_SUCCESSFUL") {
|
|
153
|
-
const video = videoResults.operation.metadata.video;
|
|
153
|
+
const video = videoResults.operations[0].operation.metadata.video;
|
|
154
154
|
return new Media({
|
|
155
155
|
seed: video.seed,
|
|
156
156
|
prompt: video.prompt,
|
|
157
157
|
workflowId: this.workflowId,
|
|
158
|
-
encodedMedia: videoResults.rawBytes,
|
|
159
|
-
mediaGenerationId: videoResults.mediaGenerationId,
|
|
158
|
+
encodedMedia: videoResults.operations[0].rawBytes,
|
|
159
|
+
mediaGenerationId: videoResults.operations[0].mediaGenerationId,
|
|
160
160
|
aspectRatio: video.aspectRatio,
|
|
161
161
|
mediaType: "VIDEO",
|
|
162
162
|
model: video.model,
|
|
163
163
|
account: this.account,
|
|
164
164
|
});
|
|
165
165
|
}
|
|
166
|
-
|
|
167
|
-
|
|
166
|
+
// Wait for few more time
|
|
167
|
+
// https://github.com/rohitaryal/whisk-api/issues/4
|
|
168
|
+
if (i >= 60) {
|
|
169
|
+
throw new Error("failed to generate video: " + JSON.stringify(videoResults));
|
|
168
170
|
}
|
|
169
171
|
}
|
|
170
172
|
}
|
package/dist/Project.d.ts
CHANGED
|
@@ -1,13 +1,40 @@
|
|
|
1
1
|
import { Media } from "./Media.js";
|
|
2
|
-
import type { PromptConfig } from "./Types.js";
|
|
2
|
+
import type { ImageInput, MediaReference, PromptConfig } from "./Types.js";
|
|
3
3
|
import { Account } from "./Whisk.js";
|
|
4
4
|
export declare class Project {
|
|
5
5
|
readonly account: Account;
|
|
6
6
|
readonly projectId: string;
|
|
7
|
-
readonly subjects:
|
|
8
|
-
readonly scenes:
|
|
9
|
-
readonly styles:
|
|
7
|
+
readonly subjects: MediaReference[];
|
|
8
|
+
readonly scenes: MediaReference[];
|
|
9
|
+
readonly styles: MediaReference[];
|
|
10
10
|
constructor(projectId: string, account: Account);
|
|
11
|
+
/**
|
|
12
|
+
* Uploads a custom image and adds it as a subject reference
|
|
13
|
+
*
|
|
14
|
+
* @param input Image as { file: string }, { url: string }, or { base64: string }
|
|
15
|
+
*/
|
|
16
|
+
addSubject(input: ImageInput): Promise<MediaReference>;
|
|
17
|
+
/**
|
|
18
|
+
* Uploads a custom image and adds it as a scene reference
|
|
19
|
+
*
|
|
20
|
+
* @param input Image as { file: string }, { url: string }, or { base64: string }
|
|
21
|
+
*/
|
|
22
|
+
addScene(input: ImageInput): Promise<MediaReference>;
|
|
23
|
+
/**
|
|
24
|
+
* Uploads a custom image and adds it as a style reference
|
|
25
|
+
*
|
|
26
|
+
* @param input Image as { file: string }, { url: string }, or { base64: string }
|
|
27
|
+
*/
|
|
28
|
+
addStyle(input: ImageInput): Promise<MediaReference>;
|
|
29
|
+
addSubjectById(mediaGenerationId: string, prompt: string): void;
|
|
30
|
+
addSceneById(mediaGenerationId: string, prompt: string): void;
|
|
31
|
+
addStyleById(mediaGenerationId: string, prompt: string): void;
|
|
32
|
+
removeSubject(mediaGenerationId: string): MediaReference;
|
|
33
|
+
removeScene(mediaGenerationId: string): MediaReference;
|
|
34
|
+
removeStyle(mediaGenerationId: string): MediaReference;
|
|
35
|
+
private addById;
|
|
36
|
+
private removeById;
|
|
37
|
+
private addReference;
|
|
11
38
|
generateImage(input: string | PromptConfig): Promise<Media>;
|
|
12
39
|
/**
|
|
13
40
|
* Generate image but with subject, scene, style attached
|
package/dist/Project.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { ImageGenerationModel } from "./Constants.js";
|
|
1
|
+
import { ImageGenerationModel, MediaCategory } from "./Constants.js";
|
|
2
2
|
import { Media } from "./Media.js";
|
|
3
|
-
import { request } from "./Utils.js";
|
|
4
|
-
import { Account } from "./Whisk.js";
|
|
3
|
+
import { request, resolveImageInput } from "./Utils.js";
|
|
4
|
+
import { Account, Whisk } from "./Whisk.js";
|
|
5
5
|
export class Project {
|
|
6
6
|
account;
|
|
7
7
|
projectId;
|
|
@@ -21,6 +21,98 @@ export class Project {
|
|
|
21
21
|
this.scenes = [];
|
|
22
22
|
this.styles = [];
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Uploads a custom image and adds it as a subject reference
|
|
26
|
+
*
|
|
27
|
+
* @param input Image as { file: string }, { url: string }, or { base64: string }
|
|
28
|
+
*/
|
|
29
|
+
async addSubject(input) {
|
|
30
|
+
return this.addReference(input, MediaCategory.SUBJECT, this.subjects);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Uploads a custom image and adds it as a scene reference
|
|
34
|
+
*
|
|
35
|
+
* @param input Image as { file: string }, { url: string }, or { base64: string }
|
|
36
|
+
*/
|
|
37
|
+
async addScene(input) {
|
|
38
|
+
return this.addReference(input, MediaCategory.SCENE, this.scenes);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Uploads a custom image and adds it as a style reference
|
|
42
|
+
*
|
|
43
|
+
* @param input Image as { file: string }, { url: string }, or { base64: string }
|
|
44
|
+
*/
|
|
45
|
+
async addStyle(input) {
|
|
46
|
+
return this.addReference(input, MediaCategory.STYLE, this.styles);
|
|
47
|
+
}
|
|
48
|
+
addSubjectById(mediaGenerationId, prompt) {
|
|
49
|
+
this.addById(mediaGenerationId, prompt, this.subjects);
|
|
50
|
+
}
|
|
51
|
+
addSceneById(mediaGenerationId, prompt) {
|
|
52
|
+
this.addById(mediaGenerationId, prompt, this.scenes);
|
|
53
|
+
}
|
|
54
|
+
addStyleById(mediaGenerationId, prompt) {
|
|
55
|
+
this.addById(mediaGenerationId, prompt, this.styles);
|
|
56
|
+
}
|
|
57
|
+
removeSubject(mediaGenerationId) {
|
|
58
|
+
return this.removeById(mediaGenerationId, this.subjects);
|
|
59
|
+
}
|
|
60
|
+
removeScene(mediaGenerationId) {
|
|
61
|
+
return this.removeById(mediaGenerationId, this.scenes);
|
|
62
|
+
}
|
|
63
|
+
removeStyle(mediaGenerationId) {
|
|
64
|
+
return this.removeById(mediaGenerationId, this.styles);
|
|
65
|
+
}
|
|
66
|
+
addById(mediaGenerationId, prompt, target) {
|
|
67
|
+
if (typeof mediaGenerationId !== "string" || !mediaGenerationId.trim()) {
|
|
68
|
+
throw new Error("media generation id is required");
|
|
69
|
+
}
|
|
70
|
+
if (typeof prompt !== "string" || !prompt.trim()) {
|
|
71
|
+
throw new Error("prompt is required");
|
|
72
|
+
}
|
|
73
|
+
if (target.some(ref => ref.mediaGenerationId === mediaGenerationId)) {
|
|
74
|
+
throw new Error(`'${mediaGenerationId}': reference already exists`);
|
|
75
|
+
}
|
|
76
|
+
target.push({ prompt, mediaGenerationId });
|
|
77
|
+
}
|
|
78
|
+
removeById(mediaGenerationId, target) {
|
|
79
|
+
if (typeof mediaGenerationId !== "string" || !mediaGenerationId.trim()) {
|
|
80
|
+
throw new Error("media generation id is required");
|
|
81
|
+
}
|
|
82
|
+
const index = target.findIndex(ref => ref.mediaGenerationId === mediaGenerationId);
|
|
83
|
+
if (index === -1) {
|
|
84
|
+
throw new Error(`'${mediaGenerationId}': reference not found`);
|
|
85
|
+
}
|
|
86
|
+
return target.splice(index, 1)[0];
|
|
87
|
+
}
|
|
88
|
+
async addReference(input, category, target) {
|
|
89
|
+
const rawBytes = await resolveImageInput(input);
|
|
90
|
+
if (!(rawBytes?.trim?.())) {
|
|
91
|
+
throw new Error("image data is required");
|
|
92
|
+
}
|
|
93
|
+
const captions = await request("https://labs.google/fx/api/trpc/backbone.captionImage", {
|
|
94
|
+
headers: { cookie: this.account.getCookie() },
|
|
95
|
+
body: JSON.stringify({
|
|
96
|
+
"json": {
|
|
97
|
+
"clientContext": {
|
|
98
|
+
"workflowId": this.projectId
|
|
99
|
+
},
|
|
100
|
+
"captionInput": {
|
|
101
|
+
"candidatesCount": 1,
|
|
102
|
+
"mediaInput": {
|
|
103
|
+
"mediaCategory": category,
|
|
104
|
+
"rawBytes": rawBytes
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
})
|
|
109
|
+
});
|
|
110
|
+
const caption = captions.candidates[0].output;
|
|
111
|
+
const uploadMediaGenerationId = await Whisk.uploadImage(rawBytes, caption, category, this.projectId, this.account);
|
|
112
|
+
const ref = { prompt: caption, mediaGenerationId: uploadMediaGenerationId };
|
|
113
|
+
target.push(ref);
|
|
114
|
+
return ref;
|
|
115
|
+
}
|
|
24
116
|
async generateImage(input) {
|
|
25
117
|
if (typeof input === "string") {
|
|
26
118
|
input = {
|
package/dist/Types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Account } from "./Whisk.js";
|
|
2
|
-
import { ImageAspectRatio, VideoAspectRatio, ImageExtension, ImageGenerationModel, VideoGenerationModel, ImageRefinementModel } from "./Constants.js";
|
|
2
|
+
import { ImageAspectRatio, VideoAspectRatio, ImageExtension, ImageGenerationModel, VideoGenerationModel, ImageRefinementModel, MediaCategory } from "./Constants.js";
|
|
3
3
|
export interface MediaConfig {
|
|
4
4
|
seed: number;
|
|
5
5
|
prompt: string;
|
|
@@ -24,3 +24,15 @@ export type ImageExtensionTypes = typeof ImageExtension[keyof typeof ImageExtens
|
|
|
24
24
|
export type ImageGenerationModelType = typeof ImageGenerationModel[keyof typeof ImageGenerationModel];
|
|
25
25
|
export type VideoGenerationModelType = typeof VideoGenerationModel[keyof typeof VideoGenerationModel];
|
|
26
26
|
export type ImageRefinementModelType = typeof ImageRefinementModel[keyof typeof ImageRefinementModel];
|
|
27
|
+
export type MediaCategoryType = typeof MediaCategory[keyof typeof MediaCategory];
|
|
28
|
+
export interface MediaReference {
|
|
29
|
+
prompt: string;
|
|
30
|
+
mediaGenerationId: string;
|
|
31
|
+
}
|
|
32
|
+
export type ImageInput = {
|
|
33
|
+
file: string;
|
|
34
|
+
} | {
|
|
35
|
+
url: string;
|
|
36
|
+
} | {
|
|
37
|
+
base64: string;
|
|
38
|
+
};
|
package/dist/Utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ImageInput } from "./Types.js";
|
|
2
2
|
/**
|
|
3
3
|
* Make a request, thats all
|
|
4
4
|
*
|
|
@@ -7,9 +7,10 @@ import type { ImageExtensionTypes } from "./Types.js";
|
|
|
7
7
|
*/
|
|
8
8
|
export declare function request<T>(input: RequestInfo | URL, init?: RequestInit): Promise<T>;
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* Fetches an image from a URL and returns base64 encoded string
|
|
11
11
|
*
|
|
12
|
-
* @param
|
|
13
|
-
* @param imageType Extension of image (if that matters)
|
|
12
|
+
* @param url URL of the image to fetch
|
|
14
13
|
*/
|
|
15
|
-
export declare function
|
|
14
|
+
export declare function imageFromUrl(url: string): Promise<string>;
|
|
15
|
+
export declare function resolveImageInput(input: ImageInput): Promise<string>;
|
|
16
|
+
export declare function imageToBase64(imagePath: string): Promise<string>;
|
package/dist/Utils.js
CHANGED
|
@@ -20,22 +20,40 @@ export async function request(input, init) {
|
|
|
20
20
|
return (json.result?.data?.json?.result || json);
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
23
|
+
* Fetches an image from a URL and returns base64 encoded string
|
|
24
24
|
*
|
|
25
|
-
* @param
|
|
26
|
-
* @param imageType Extension of image (if that matters)
|
|
25
|
+
* @param url URL of the image to fetch
|
|
27
26
|
*/
|
|
28
|
-
export async function
|
|
27
|
+
export async function imageFromUrl(url) {
|
|
28
|
+
if (!(url?.trim?.())) {
|
|
29
|
+
throw new Error("url is required");
|
|
30
|
+
}
|
|
31
|
+
const response = await fetch(url);
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
throw new Error(`Failed to fetch image (${response.status}): ${url}`);
|
|
34
|
+
}
|
|
35
|
+
const contentType = response.headers.get("content-type") || "";
|
|
36
|
+
const imageType = contentType.split("/")[1]?.split(";")[0];
|
|
37
|
+
if (!Object.values(ImageExtension).includes(imageType)) {
|
|
38
|
+
throw new Error(`'${url}': unsupported image type '${contentType}'`);
|
|
39
|
+
}
|
|
40
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
41
|
+
return `data:image/${imageType};base64,${buffer.toString("base64")}`;
|
|
42
|
+
}
|
|
43
|
+
export async function resolveImageInput(input) {
|
|
44
|
+
if ("file" in input)
|
|
45
|
+
return await imageToBase64(input.file);
|
|
46
|
+
if ("url" in input)
|
|
47
|
+
return await imageFromUrl(input.url);
|
|
48
|
+
return input.base64;
|
|
49
|
+
}
|
|
50
|
+
export async function imageToBase64(imagePath) {
|
|
29
51
|
if (!(imagePath?.trim?.()) || !fs.existsSync(imagePath)) {
|
|
30
52
|
throw new Error(`'${imagePath}': image not found`);
|
|
31
53
|
}
|
|
32
|
-
|
|
33
|
-
if (!(imageType?.trim?.())) {
|
|
34
|
-
imageType = path.extname(imagePath).slice(1);
|
|
35
|
-
}
|
|
36
|
-
// If extension not in valid extension list throw error
|
|
54
|
+
const imageType = path.extname(imagePath).slice(1);
|
|
37
55
|
if (!Object.values(ImageExtension).includes(imageType)) {
|
|
38
|
-
throw new Error(`'${imagePath}':
|
|
56
|
+
throw new Error(`'${imagePath}': unsupported image type '${imageType}'`);
|
|
39
57
|
}
|
|
40
58
|
const base64Header = `data:image/${imageType};base64,`;
|
|
41
59
|
return new Promise((resolve, reject) => {
|
package/dist/Whisk.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Media } from "./Media.js";
|
|
2
2
|
import { Project } from "./Project.js";
|
|
3
|
-
import { PromptConfig } from "./Types.js";
|
|
3
|
+
import { MediaCategoryType, PromptConfig } from "./Types.js";
|
|
4
4
|
export declare class Account {
|
|
5
5
|
private cookie;
|
|
6
6
|
private authToken?;
|
|
@@ -32,6 +32,16 @@ export declare class Whisk {
|
|
|
32
32
|
* @param count Number of captions to generate (min: 0, max: 8)
|
|
33
33
|
*/
|
|
34
34
|
static generateCaption(input: string, account: Account, count?: number): Promise<string[]>;
|
|
35
|
+
/**
|
|
36
|
+
* Upload a custom image to Whisk's storage
|
|
37
|
+
*
|
|
38
|
+
* @param rawBytes Base64 encoded image
|
|
39
|
+
* @param caption Caption describing the image
|
|
40
|
+
* @param category Media category (SUBJECT, SCENE, or STYLE)
|
|
41
|
+
* @param workflowId Project workflow id
|
|
42
|
+
* @param account Account{} object
|
|
43
|
+
*/
|
|
44
|
+
static uploadImage(rawBytes: string, caption: string, category: MediaCategoryType, workflowId: string, account: Account): Promise<string>;
|
|
35
45
|
/**
|
|
36
46
|
* Tries to get media from their unique id
|
|
37
47
|
*
|
package/dist/Whisk.js
CHANGED
|
@@ -113,6 +113,42 @@ export class Whisk {
|
|
|
113
113
|
});
|
|
114
114
|
return captionResults.candidates.map(item => item.output);
|
|
115
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* Upload a custom image to Whisk's storage
|
|
118
|
+
*
|
|
119
|
+
* @param rawBytes Base64 encoded image
|
|
120
|
+
* @param caption Caption describing the image
|
|
121
|
+
* @param category Media category (SUBJECT, SCENE, or STYLE)
|
|
122
|
+
* @param workflowId Project workflow id
|
|
123
|
+
* @param account Account{} object
|
|
124
|
+
*/
|
|
125
|
+
static async uploadImage(rawBytes, caption, category, workflowId, account) {
|
|
126
|
+
if (!(rawBytes?.trim?.())) {
|
|
127
|
+
throw new Error("image data is required");
|
|
128
|
+
}
|
|
129
|
+
if (!(caption?.trim?.())) {
|
|
130
|
+
throw new Error("caption is required");
|
|
131
|
+
}
|
|
132
|
+
if (!(account instanceof Account)) {
|
|
133
|
+
throw new Error("invalid or missing account");
|
|
134
|
+
}
|
|
135
|
+
const uploadResult = await request("https://labs.google/fx/api/trpc/backbone.uploadImage", {
|
|
136
|
+
headers: { cookie: account.getCookie() },
|
|
137
|
+
body: JSON.stringify({
|
|
138
|
+
"json": {
|
|
139
|
+
"clientContext": {
|
|
140
|
+
"workflowId": workflowId
|
|
141
|
+
},
|
|
142
|
+
"uploadMediaInput": {
|
|
143
|
+
"mediaCategory": category,
|
|
144
|
+
"rawBytes": rawBytes,
|
|
145
|
+
"caption": caption
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
});
|
|
150
|
+
return uploadResult.uploadMediaGenerationId;
|
|
151
|
+
}
|
|
116
152
|
/**
|
|
117
153
|
* Tries to get media from their unique id
|
|
118
154
|
*
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED