diffio 0.1.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/LICENSE +21 -0
- package/README.md +204 -0
- package/dist/BaseClient.d.ts +26 -0
- package/dist/BaseClient.js +17 -0
- package/dist/Client.d.ts +109 -0
- package/dist/Client.js +689 -0
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.js +18 -0
- package/dist/api/resources/audioIsolation/client/Client.d.ts +41 -0
- package/dist/api/resources/audioIsolation/client/Client.js +32 -0
- package/dist/api/resources/generations/client/Client.d.ts +47 -0
- package/dist/api/resources/generations/client/Client.js +36 -0
- package/dist/api/resources/index.d.ts +4 -0
- package/dist/api/resources/index.js +11 -0
- package/dist/api/resources/projects/client/Client.d.ts +15 -0
- package/dist/api/resources/projects/client/Client.js +15 -0
- package/dist/api/resources/webhooks/client/Client.d.ts +20 -0
- package/dist/api/resources/webhooks/client/Client.js +15 -0
- package/dist/api/serialization.d.ts +14 -0
- package/dist/api/serialization.js +138 -0
- package/dist/api/types.d.ts +122 -0
- package/dist/api/types.js +2 -0
- package/dist/core/headers.d.ts +6 -0
- package/dist/core/headers.js +44 -0
- package/dist/core/retry.d.ts +7 -0
- package/dist/core/retry.js +31 -0
- package/dist/core/supplier.d.ts +4 -0
- package/dist/core/supplier.js +14 -0
- package/dist/core/url.d.ts +1 -0
- package/dist/core/url.js +14 -0
- package/dist/errors/index.d.ts +14 -0
- package/dist/errors/index.js +26 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +45 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +4 -0
- package/package.json +36 -0
package/dist/Client.js
ADDED
|
@@ -0,0 +1,689 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.DiffioClient = void 0;
|
|
37
|
+
const BaseClient_1 = require("./BaseClient");
|
|
38
|
+
const headers_1 = require("./core/headers");
|
|
39
|
+
const supplier_1 = require("./core/supplier");
|
|
40
|
+
const url_1 = require("./core/url");
|
|
41
|
+
const retry_1 = require("./core/retry");
|
|
42
|
+
const errors_1 = require("./errors");
|
|
43
|
+
const serialization_1 = require("./api/serialization");
|
|
44
|
+
const resources_1 = require("./api/resources");
|
|
45
|
+
const mime_types_1 = require("mime-types");
|
|
46
|
+
const DEFAULT_BASE_URL = "https://us-central1-diffioai.cloudfunctions.net";
|
|
47
|
+
const API_PREFIX = "v1";
|
|
48
|
+
const MODEL_ENDPOINTS = {
|
|
49
|
+
"diffio-2": "diffio-2.0-generation",
|
|
50
|
+
"diffio-2-flash": "diffio-2.0-flash-generation",
|
|
51
|
+
"diffio-3": "diffio-3.0-generation"
|
|
52
|
+
};
|
|
53
|
+
const DEFAULT_RETRY_STATUS_CODES = [408, 429, 500, 502, 503, 504];
|
|
54
|
+
const DEFAULT_RETRY_BACKOFF = 0.5;
|
|
55
|
+
const DEFAULT_TIMEOUT_SECONDS = 60;
|
|
56
|
+
const WEBHOOK_EVENT_TYPES = [
|
|
57
|
+
"generation.queued",
|
|
58
|
+
"generation.processing",
|
|
59
|
+
"generation.failed",
|
|
60
|
+
"generation.completed"
|
|
61
|
+
];
|
|
62
|
+
const WEBHOOK_MODES = ["test", "live"];
|
|
63
|
+
class DiffioClient {
|
|
64
|
+
constructor(options = {}) {
|
|
65
|
+
const envApiKey = typeof process !== "undefined" ? process.env.DIFFIO_API_KEY : undefined;
|
|
66
|
+
const apiKey = options.apiKey ?? envApiKey;
|
|
67
|
+
if (apiKey == null) {
|
|
68
|
+
throw new errors_1.DiffioApiError("apiKey is required");
|
|
69
|
+
}
|
|
70
|
+
this._options = (0, BaseClient_1.normalizeClientOptions)({ ...options, apiKey });
|
|
71
|
+
this.audioIsolation = new resources_1.AudioIsolationClient(this);
|
|
72
|
+
this.generations = new resources_1.GenerationsClient(this);
|
|
73
|
+
this.projects = new resources_1.ProjectsClient(this);
|
|
74
|
+
this.webhooks = new resources_1.WebhooksClient(this);
|
|
75
|
+
}
|
|
76
|
+
close() {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
async createProject(options) {
|
|
80
|
+
const { filePath, contentType, contentLength, params, fileFormat, requestOptions } = options;
|
|
81
|
+
if (!filePath) {
|
|
82
|
+
throw new errors_1.DiffioApiError("filePath is required");
|
|
83
|
+
}
|
|
84
|
+
const resolvedFileName = getBaseName(filePath);
|
|
85
|
+
const resolvedContentType = contentType ?? guessContentType(filePath) ?? "application/octet-stream";
|
|
86
|
+
let resolvedContentLength = contentLength;
|
|
87
|
+
if (resolvedContentLength == null && filePath) {
|
|
88
|
+
resolvedContentLength = getFileSize(filePath);
|
|
89
|
+
}
|
|
90
|
+
const payload = {
|
|
91
|
+
fileName: resolvedFileName,
|
|
92
|
+
contentType: resolvedContentType
|
|
93
|
+
};
|
|
94
|
+
if (resolvedContentLength != null) {
|
|
95
|
+
payload.contentLength = Number(resolvedContentLength);
|
|
96
|
+
}
|
|
97
|
+
if (params) {
|
|
98
|
+
payload.params = params;
|
|
99
|
+
}
|
|
100
|
+
if (fileFormat != null) {
|
|
101
|
+
payload.fileFormat = fileFormat;
|
|
102
|
+
}
|
|
103
|
+
const response = await this._requestJson("POST", "create_project", payload, requestOptions);
|
|
104
|
+
const project = (0, serialization_1.parseCreateProjectResponse)(response);
|
|
105
|
+
await this._uploadFile({
|
|
106
|
+
uploadUrl: project.uploadUrl,
|
|
107
|
+
uploadMethod: project.uploadMethod,
|
|
108
|
+
filePath,
|
|
109
|
+
contentType: resolvedContentType,
|
|
110
|
+
requestOptions
|
|
111
|
+
});
|
|
112
|
+
return project;
|
|
113
|
+
}
|
|
114
|
+
async _uploadFile(options) {
|
|
115
|
+
const { uploadUrl, uploadMethod, filePath, data, contentType, requestOptions } = options;
|
|
116
|
+
if ((filePath == null) === (data == null)) {
|
|
117
|
+
throw new errors_1.DiffioApiError("Provide filePath or data");
|
|
118
|
+
}
|
|
119
|
+
let resolvedContentType = contentType;
|
|
120
|
+
if (!resolvedContentType && filePath) {
|
|
121
|
+
const guessed = (0, mime_types_1.lookup)(filePath);
|
|
122
|
+
resolvedContentType = typeof guessed === "string" ? guessed : undefined;
|
|
123
|
+
}
|
|
124
|
+
if (!resolvedContentType) {
|
|
125
|
+
resolvedContentType = "application/octet-stream";
|
|
126
|
+
}
|
|
127
|
+
const method = (uploadMethod || "PUT").toUpperCase();
|
|
128
|
+
const extraHeaders = {
|
|
129
|
+
"Content-Type": resolvedContentType
|
|
130
|
+
};
|
|
131
|
+
if (isStorageEmulatorUrl(uploadUrl)) {
|
|
132
|
+
extraHeaders.Authorization = "Bearer owner";
|
|
133
|
+
}
|
|
134
|
+
const body = filePath ? await createFileReadStream(filePath) : data;
|
|
135
|
+
await this._requestBinary(method, uploadUrl, body, requestOptions, extraHeaders, true);
|
|
136
|
+
}
|
|
137
|
+
async createGeneration(options) {
|
|
138
|
+
const { apiProjectId, model = "diffio-2", sampling, params, requestOptions } = options;
|
|
139
|
+
const endpoint = MODEL_ENDPOINTS[model];
|
|
140
|
+
if (!endpoint) {
|
|
141
|
+
throw new errors_1.DiffioApiError(`Unsupported model: ${model}`);
|
|
142
|
+
}
|
|
143
|
+
const payload = { apiProjectId };
|
|
144
|
+
if (sampling != null) {
|
|
145
|
+
payload.sampling = sampling;
|
|
146
|
+
}
|
|
147
|
+
if (params) {
|
|
148
|
+
payload.params = params;
|
|
149
|
+
}
|
|
150
|
+
const response = await this._requestJson("POST", endpoint, payload, requestOptions);
|
|
151
|
+
return (0, serialization_1.parseCreateGenerationResponse)(response);
|
|
152
|
+
}
|
|
153
|
+
async listProjects(options = {}) {
|
|
154
|
+
const response = await this._requestJson("POST", "list_projects", {}, options.requestOptions);
|
|
155
|
+
return (0, serialization_1.parseListProjectsResponse)(response);
|
|
156
|
+
}
|
|
157
|
+
async listProjectGenerations(options) {
|
|
158
|
+
const { apiProjectId, requestOptions } = options;
|
|
159
|
+
if (!apiProjectId) {
|
|
160
|
+
throw new errors_1.DiffioApiError("apiProjectId is required");
|
|
161
|
+
}
|
|
162
|
+
const response = await this._requestJson("POST", "list_project_generations", { apiProjectId }, requestOptions);
|
|
163
|
+
return (0, serialization_1.parseListProjectGenerationsResponse)(response);
|
|
164
|
+
}
|
|
165
|
+
async getGenerationProgress(options) {
|
|
166
|
+
const { generationId, apiProjectId, requestOptions } = options;
|
|
167
|
+
const payload = { generationId };
|
|
168
|
+
if (apiProjectId != null) {
|
|
169
|
+
payload.apiProjectId = apiProjectId;
|
|
170
|
+
}
|
|
171
|
+
const response = await this._requestJson("POST", "get_generation_progress", payload, requestOptions);
|
|
172
|
+
return (0, serialization_1.parseGenerationProgressResponse)(response);
|
|
173
|
+
}
|
|
174
|
+
async waitForGeneration(options) {
|
|
175
|
+
const { generationId, apiProjectId, pollInterval = 2, timeout, timeoutInSeconds, onProgress, showProgress, requestOptions } = options;
|
|
176
|
+
const timeoutSeconds = timeoutInSeconds ?? timeout ?? DEFAULT_TIMEOUT_SECONDS;
|
|
177
|
+
const deadline = Date.now() + timeoutSeconds * 1000;
|
|
178
|
+
let lastProgress = null;
|
|
179
|
+
while (Date.now() < deadline) {
|
|
180
|
+
const progress = await this.getGenerationProgress({ generationId, apiProjectId, requestOptions });
|
|
181
|
+
lastProgress = progress;
|
|
182
|
+
await reportProgress(progress, onProgress, showProgress);
|
|
183
|
+
if (progress.status === "complete") {
|
|
184
|
+
return progress;
|
|
185
|
+
}
|
|
186
|
+
if (progress.status === "failed") {
|
|
187
|
+
throw new errors_1.DiffioApiError("Generation failed" +
|
|
188
|
+
` (preProcessing=${progress.preProcessing.status},` +
|
|
189
|
+
` inference=${progress.inference.status},` +
|
|
190
|
+
` error=${progress.error},` +
|
|
191
|
+
` details=${progress.errorDetails})`);
|
|
192
|
+
}
|
|
193
|
+
await sleepSeconds(pollInterval);
|
|
194
|
+
}
|
|
195
|
+
throw new errors_1.DiffioApiError("Timed out waiting for generation completion" +
|
|
196
|
+
` (lastStatus=${lastProgress ? lastProgress.status : "unknown"})`);
|
|
197
|
+
}
|
|
198
|
+
async getGenerationDownload(options) {
|
|
199
|
+
const { generationId, apiProjectId, downloadType, requestOptions } = options;
|
|
200
|
+
const payload = { generationId, apiProjectId };
|
|
201
|
+
if (downloadType != null) {
|
|
202
|
+
if (downloadType !== "audio" && downloadType !== "video") {
|
|
203
|
+
throw new errors_1.DiffioApiError("downloadType must be audio or video");
|
|
204
|
+
}
|
|
205
|
+
payload.downloadType = downloadType;
|
|
206
|
+
}
|
|
207
|
+
const response = await this._requestJson("POST", "get_generation_download", payload, requestOptions);
|
|
208
|
+
return (0, serialization_1.parseGenerationDownloadResponse)(response);
|
|
209
|
+
}
|
|
210
|
+
async getWebhooksPortalAccess(options) {
|
|
211
|
+
const { mode, apiKeyId, requestOptions } = options;
|
|
212
|
+
if (!WEBHOOK_MODES.includes(mode)) {
|
|
213
|
+
throw new errors_1.DiffioApiError("mode must be test or live");
|
|
214
|
+
}
|
|
215
|
+
const payload = { mode };
|
|
216
|
+
if (apiKeyId != null) {
|
|
217
|
+
payload.apiKeyId = apiKeyId;
|
|
218
|
+
}
|
|
219
|
+
const response = await this._requestJson("POST", "webhooks/app_portal_access", payload, requestOptions);
|
|
220
|
+
return (0, serialization_1.parseWebhookPortalResponse)(response);
|
|
221
|
+
}
|
|
222
|
+
async sendWebhookTestEvent(options) {
|
|
223
|
+
const { eventType, mode, apiKeyId, samplePayload, requestOptions } = options;
|
|
224
|
+
if (!WEBHOOK_EVENT_TYPES.includes(eventType)) {
|
|
225
|
+
throw new errors_1.DiffioApiError("eventType is not supported");
|
|
226
|
+
}
|
|
227
|
+
if (!WEBHOOK_MODES.includes(mode)) {
|
|
228
|
+
throw new errors_1.DiffioApiError("mode must be test or live");
|
|
229
|
+
}
|
|
230
|
+
if (samplePayload != null && (typeof samplePayload !== "object" || Array.isArray(samplePayload))) {
|
|
231
|
+
throw new errors_1.DiffioApiError("samplePayload must be an object");
|
|
232
|
+
}
|
|
233
|
+
const payload = { eventType, mode };
|
|
234
|
+
if (apiKeyId != null) {
|
|
235
|
+
payload.apiKeyId = apiKeyId;
|
|
236
|
+
}
|
|
237
|
+
if (samplePayload != null) {
|
|
238
|
+
payload.samplePayload = samplePayload;
|
|
239
|
+
}
|
|
240
|
+
const response = await this._requestJson("POST", "webhooks/send_test_event", payload, requestOptions);
|
|
241
|
+
return (0, serialization_1.parseWebhookTestEventResponse)(response);
|
|
242
|
+
}
|
|
243
|
+
async restoreAudio(options) {
|
|
244
|
+
const metadata = initRestoreMetadata();
|
|
245
|
+
metadata.downloadType = options.downloadType ?? "audio";
|
|
246
|
+
const resolvedProgressOptions = options.progressRequestOptions ?? options.requestOptions;
|
|
247
|
+
const resolvedDownloadOptions = options.downloadRequestOptions ?? options.requestOptions;
|
|
248
|
+
let result = null;
|
|
249
|
+
try {
|
|
250
|
+
result = await this.audioIsolationIsolate({
|
|
251
|
+
filePath: options.filePath,
|
|
252
|
+
contentType: options.contentType,
|
|
253
|
+
contentLength: options.contentLength,
|
|
254
|
+
fileFormat: options.fileFormat,
|
|
255
|
+
model: options.model,
|
|
256
|
+
sampling: options.sampling,
|
|
257
|
+
projectParams: options.projectParams,
|
|
258
|
+
generationParams: options.generationParams,
|
|
259
|
+
requestOptions: options.requestOptions
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
metadata.stage = "isolate";
|
|
264
|
+
setRestoreError(metadata, error);
|
|
265
|
+
if (options.raiseOnError) {
|
|
266
|
+
attachRestoreMetadata(error, metadata);
|
|
267
|
+
throw error;
|
|
268
|
+
}
|
|
269
|
+
return [null, metadata];
|
|
270
|
+
}
|
|
271
|
+
metadata.project = result.project;
|
|
272
|
+
metadata.generation = result.generation;
|
|
273
|
+
metadata.apiProjectId = result.project.apiProjectId;
|
|
274
|
+
metadata.generationId = result.generation.generationId;
|
|
275
|
+
metadata.stage = "generation";
|
|
276
|
+
let progress = null;
|
|
277
|
+
try {
|
|
278
|
+
progress = await this.waitForGeneration({
|
|
279
|
+
generationId: result.generation.generationId,
|
|
280
|
+
apiProjectId: result.project.apiProjectId,
|
|
281
|
+
pollInterval: options.pollInterval,
|
|
282
|
+
timeout: options.timeout,
|
|
283
|
+
timeoutInSeconds: options.timeoutInSeconds,
|
|
284
|
+
onProgress: options.onProgress,
|
|
285
|
+
showProgress: options.showProgress,
|
|
286
|
+
requestOptions: resolvedProgressOptions
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
catch (error) {
|
|
290
|
+
metadata.stage = "progress";
|
|
291
|
+
try {
|
|
292
|
+
progress = await this.getGenerationProgress({
|
|
293
|
+
generationId: result.generation.generationId,
|
|
294
|
+
apiProjectId: result.project.apiProjectId,
|
|
295
|
+
requestOptions: resolvedProgressOptions
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
progress = null;
|
|
300
|
+
}
|
|
301
|
+
metadata.progress = progress;
|
|
302
|
+
metadata.status = progress?.status ?? null;
|
|
303
|
+
setRestoreError(metadata, error);
|
|
304
|
+
if (progress) {
|
|
305
|
+
metadata.error = progress.error ?? String(error);
|
|
306
|
+
metadata.errorDetails = progress.errorDetails ?? null;
|
|
307
|
+
}
|
|
308
|
+
if (options.raiseOnError) {
|
|
309
|
+
attachRestoreMetadata(error, metadata);
|
|
310
|
+
throw error;
|
|
311
|
+
}
|
|
312
|
+
return [null, metadata];
|
|
313
|
+
}
|
|
314
|
+
metadata.progress = progress;
|
|
315
|
+
metadata.status = progress.status;
|
|
316
|
+
metadata.error = progress.error ?? null;
|
|
317
|
+
metadata.errorDetails = progress.errorDetails ?? null;
|
|
318
|
+
metadata.stage = "download_info";
|
|
319
|
+
let download;
|
|
320
|
+
try {
|
|
321
|
+
download = await this.getGenerationDownload({
|
|
322
|
+
generationId: result.generation.generationId,
|
|
323
|
+
apiProjectId: result.project.apiProjectId,
|
|
324
|
+
downloadType: options.downloadType ?? "audio",
|
|
325
|
+
requestOptions: resolvedDownloadOptions
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
setRestoreError(metadata, error);
|
|
330
|
+
if (options.raiseOnError) {
|
|
331
|
+
attachRestoreMetadata(error, metadata);
|
|
332
|
+
throw error;
|
|
333
|
+
}
|
|
334
|
+
return [null, metadata];
|
|
335
|
+
}
|
|
336
|
+
metadata.download = download;
|
|
337
|
+
metadata.downloadType = download.downloadType;
|
|
338
|
+
metadata.downloadUrl = download.downloadUrl;
|
|
339
|
+
metadata.fileName = download.fileName;
|
|
340
|
+
metadata.mimeType = download.mimeType;
|
|
341
|
+
metadata.stage = "download";
|
|
342
|
+
let content;
|
|
343
|
+
try {
|
|
344
|
+
content = await this._downloadBinary(download.downloadUrl, resolvedDownloadOptions);
|
|
345
|
+
}
|
|
346
|
+
catch (error) {
|
|
347
|
+
setRestoreError(metadata, error);
|
|
348
|
+
if (options.raiseOnError) {
|
|
349
|
+
attachRestoreMetadata(error, metadata);
|
|
350
|
+
throw error;
|
|
351
|
+
}
|
|
352
|
+
return [null, metadata];
|
|
353
|
+
}
|
|
354
|
+
metadata.stage = "complete";
|
|
355
|
+
metadata.ok = true;
|
|
356
|
+
return [content, metadata];
|
|
357
|
+
}
|
|
358
|
+
async restore(options) {
|
|
359
|
+
return this.restoreAudio(options);
|
|
360
|
+
}
|
|
361
|
+
async audioIsolationIsolate(options) {
|
|
362
|
+
const project = await this.createProject({
|
|
363
|
+
filePath: options.filePath,
|
|
364
|
+
contentType: options.contentType,
|
|
365
|
+
contentLength: options.contentLength,
|
|
366
|
+
params: options.projectParams,
|
|
367
|
+
fileFormat: options.fileFormat,
|
|
368
|
+
requestOptions: options.requestOptions
|
|
369
|
+
});
|
|
370
|
+
const generation = await this.createGeneration({
|
|
371
|
+
apiProjectId: project.apiProjectId,
|
|
372
|
+
model: options.model,
|
|
373
|
+
sampling: options.sampling,
|
|
374
|
+
params: options.generationParams,
|
|
375
|
+
requestOptions: options.requestOptions
|
|
376
|
+
});
|
|
377
|
+
return (0, serialization_1.createAudioIsolationResult)(project, generation);
|
|
378
|
+
}
|
|
379
|
+
async _downloadBinary(downloadUrl, requestOptions) {
|
|
380
|
+
const extraHeaders = {};
|
|
381
|
+
if (isStorageEmulatorUrl(downloadUrl)) {
|
|
382
|
+
extraHeaders.Authorization = "Bearer owner";
|
|
383
|
+
}
|
|
384
|
+
const response = await this._requestBinary("GET", downloadUrl, undefined, requestOptions, extraHeaders, true);
|
|
385
|
+
return response;
|
|
386
|
+
}
|
|
387
|
+
async _requestJson(method, path, payload, requestOptions) {
|
|
388
|
+
const { url, headers, timeoutMs, maxRetries, retryBackoff, retryStatusCodes, fetchFn, abortSignal } = await this._buildRequest(method, path, requestOptions, {
|
|
389
|
+
"Content-Type": "application/json"
|
|
390
|
+
});
|
|
391
|
+
const requestBody = JSON.stringify(payload);
|
|
392
|
+
const makeRequest = () => fetchWithTimeout(fetchFn, url, {
|
|
393
|
+
method,
|
|
394
|
+
headers,
|
|
395
|
+
body: requestBody
|
|
396
|
+
}, timeoutMs, abortSignal);
|
|
397
|
+
const response = await (0, retry_1.requestWithRetries)(makeRequest, { maxRetries, retryBackoff, retryStatusCodes });
|
|
398
|
+
return parseJsonResponse(response);
|
|
399
|
+
}
|
|
400
|
+
async _requestBinary(method, urlOrPath, body, requestOptions, extraHeaders, isAbsoluteUrl = false) {
|
|
401
|
+
const { url, headers, timeoutMs, maxRetries, retryBackoff, retryStatusCodes, fetchFn, abortSignal } = await this._buildRequest(method, urlOrPath, requestOptions, extraHeaders, isAbsoluteUrl);
|
|
402
|
+
const requestInit = {
|
|
403
|
+
method,
|
|
404
|
+
headers,
|
|
405
|
+
body: body,
|
|
406
|
+
signal: abortSignal
|
|
407
|
+
};
|
|
408
|
+
if (body && isNodeReadable(body)) {
|
|
409
|
+
requestInit.duplex = "half";
|
|
410
|
+
}
|
|
411
|
+
const makeRequest = () => fetchWithTimeout(fetchFn, url, requestInit, timeoutMs, abortSignal);
|
|
412
|
+
const response = await (0, retry_1.requestWithRetries)(makeRequest, { maxRetries, retryBackoff, retryStatusCodes });
|
|
413
|
+
if (method === "GET") {
|
|
414
|
+
return parseBinaryResponse(response);
|
|
415
|
+
}
|
|
416
|
+
await parseJsonResponse(response);
|
|
417
|
+
}
|
|
418
|
+
async _buildRequest(method, path, requestOptions, extraHeaders, isAbsoluteUrl = false) {
|
|
419
|
+
const { baseUrl, apiPrefix } = await resolveBaseUrl(this._options.baseUrl);
|
|
420
|
+
const apiKey = requestOptions?.apiKey ?? (await supplier_1.Supplier.get(this._options.apiKey));
|
|
421
|
+
if (!apiKey && !isAbsoluteUrl) {
|
|
422
|
+
throw new errors_1.DiffioApiError("apiKey is required");
|
|
423
|
+
}
|
|
424
|
+
const baseHeaders = isAbsoluteUrl ? undefined : this._options.headers;
|
|
425
|
+
const authHeaders = !isAbsoluteUrl && apiKey ? (0, headers_1.mergeOnlyDefinedHeaders)({ Authorization: `Bearer ${apiKey}` }) : undefined;
|
|
426
|
+
const mergedHeaders = (0, headers_1.mergeHeaders)(baseHeaders, authHeaders, requestOptions?.headers, extraHeaders);
|
|
427
|
+
const headers = await (0, headers_1.resolveHeaders)(mergedHeaders);
|
|
428
|
+
const timeoutSeconds = requestOptions?.timeoutInSeconds ??
|
|
429
|
+
requestOptions?.timeout ??
|
|
430
|
+
this._options.timeoutInSeconds ??
|
|
431
|
+
this._options.timeout ??
|
|
432
|
+
DEFAULT_TIMEOUT_SECONDS;
|
|
433
|
+
const timeoutMs = timeoutSeconds != null ? timeoutSeconds * 1000 : undefined;
|
|
434
|
+
const maxRetries = requestOptions?.maxRetries ?? this._options.maxRetries ?? 0;
|
|
435
|
+
const retryBackoff = requestOptions?.retryBackoff ?? this._options.retryBackoff ?? DEFAULT_RETRY_BACKOFF;
|
|
436
|
+
const retryStatusCodes = requestOptions?.retryStatusCodes ?? this._options.retryStatusCodes ?? DEFAULT_RETRY_STATUS_CODES;
|
|
437
|
+
const fetchFn = this._options.fetch ?? globalThis.fetch;
|
|
438
|
+
if (!fetchFn) {
|
|
439
|
+
throw new errors_1.DiffioApiError("fetch is not available in this runtime");
|
|
440
|
+
}
|
|
441
|
+
const requestPath = path.replace(/^\/+/, "");
|
|
442
|
+
const url = isAbsoluteUrl
|
|
443
|
+
? path
|
|
444
|
+
: (0, url_1.join)(baseUrl, apiPrefix ? `${apiPrefix}/${requestPath}` : requestPath);
|
|
445
|
+
return {
|
|
446
|
+
url,
|
|
447
|
+
headers,
|
|
448
|
+
timeoutMs,
|
|
449
|
+
maxRetries,
|
|
450
|
+
retryBackoff,
|
|
451
|
+
retryStatusCodes,
|
|
452
|
+
fetchFn,
|
|
453
|
+
abortSignal: requestOptions?.abortSignal
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
exports.DiffioClient = DiffioClient;
|
|
458
|
+
function isAbortError(error) {
|
|
459
|
+
return typeof error === "object" && error != null && error.name === "AbortError";
|
|
460
|
+
}
|
|
461
|
+
function createAbortSignal(timeoutMs, externalSignal) {
|
|
462
|
+
if (!timeoutMs && !externalSignal) {
|
|
463
|
+
return { cleanup: () => undefined, didTimeout: () => false };
|
|
464
|
+
}
|
|
465
|
+
const controller = new AbortController();
|
|
466
|
+
let timedOut = false;
|
|
467
|
+
let timeoutId = null;
|
|
468
|
+
const abortListener = () => controller.abort();
|
|
469
|
+
if (externalSignal) {
|
|
470
|
+
if (externalSignal.aborted) {
|
|
471
|
+
controller.abort();
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
externalSignal.addEventListener("abort", abortListener, { once: true });
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
if (timeoutMs != null) {
|
|
478
|
+
timeoutId = setTimeout(() => {
|
|
479
|
+
timedOut = true;
|
|
480
|
+
controller.abort();
|
|
481
|
+
}, timeoutMs);
|
|
482
|
+
}
|
|
483
|
+
const cleanup = () => {
|
|
484
|
+
if (timeoutId) {
|
|
485
|
+
clearTimeout(timeoutId);
|
|
486
|
+
}
|
|
487
|
+
if (externalSignal) {
|
|
488
|
+
externalSignal.removeEventListener("abort", abortListener);
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
return { signal: controller.signal, cleanup, didTimeout: () => timedOut };
|
|
492
|
+
}
|
|
493
|
+
async function fetchWithTimeout(fetchFn, url, init, timeoutMs, externalSignal) {
|
|
494
|
+
const { signal, cleanup, didTimeout } = createAbortSignal(timeoutMs, externalSignal);
|
|
495
|
+
try {
|
|
496
|
+
return await fetchFn(url, { ...init, signal });
|
|
497
|
+
}
|
|
498
|
+
catch (error) {
|
|
499
|
+
if (isAbortError(error) && didTimeout()) {
|
|
500
|
+
throw new errors_1.DiffioTimeoutError("Request timed out");
|
|
501
|
+
}
|
|
502
|
+
throw error;
|
|
503
|
+
}
|
|
504
|
+
finally {
|
|
505
|
+
cleanup();
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
function parseJsonResponse(response) {
|
|
509
|
+
if (response.ok) {
|
|
510
|
+
const contentType = response.headers.get("Content-Type") || "";
|
|
511
|
+
if (contentType.includes("application/json")) {
|
|
512
|
+
return response.json();
|
|
513
|
+
}
|
|
514
|
+
if (response.status === 204) {
|
|
515
|
+
return Promise.resolve({});
|
|
516
|
+
}
|
|
517
|
+
return response.text().then((text) => {
|
|
518
|
+
if (!text) {
|
|
519
|
+
return {};
|
|
520
|
+
}
|
|
521
|
+
try {
|
|
522
|
+
return JSON.parse(text);
|
|
523
|
+
}
|
|
524
|
+
catch {
|
|
525
|
+
return {};
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
return parseErrorResponse(response).then((errorBody) => {
|
|
530
|
+
const message = getErrorMessage(errorBody, response.status);
|
|
531
|
+
throw new errors_1.DiffioApiError(message, { statusCode: response.status, responseBody: errorBody });
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
async function parseBinaryResponse(response) {
|
|
535
|
+
if (response.ok) {
|
|
536
|
+
const buffer = await response.arrayBuffer();
|
|
537
|
+
return new Uint8Array(buffer);
|
|
538
|
+
}
|
|
539
|
+
const errorBody = await parseErrorResponse(response);
|
|
540
|
+
const message = getErrorMessage(errorBody, response.status);
|
|
541
|
+
throw new errors_1.DiffioApiError(message, { statusCode: response.status, responseBody: errorBody });
|
|
542
|
+
}
|
|
543
|
+
async function parseErrorResponse(response) {
|
|
544
|
+
const contentType = response.headers.get("Content-Type") || "";
|
|
545
|
+
if (contentType.includes("application/json")) {
|
|
546
|
+
try {
|
|
547
|
+
return await response.json();
|
|
548
|
+
}
|
|
549
|
+
catch {
|
|
550
|
+
return null;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
const text = await response.text();
|
|
554
|
+
return text || null;
|
|
555
|
+
}
|
|
556
|
+
function getErrorMessage(body, status) {
|
|
557
|
+
if (body && typeof body === "object" && "error" in body) {
|
|
558
|
+
const message = body.error;
|
|
559
|
+
if (message) {
|
|
560
|
+
return String(message);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
return `Request failed with status ${status}`;
|
|
564
|
+
}
|
|
565
|
+
async function resolveBaseUrl(baseUrlSupplier) {
|
|
566
|
+
const envBase = typeof process !== "undefined" ? process.env.DIFFIO_API_BASE_URL : undefined;
|
|
567
|
+
const resolvedBase = (await supplier_1.Supplier.get(baseUrlSupplier)) ?? envBase ?? DEFAULT_BASE_URL;
|
|
568
|
+
const trimmed = resolvedBase.replace(/\/+$/, "");
|
|
569
|
+
const apiPrefix = trimmed.endsWith(`/${API_PREFIX}`) ? "" : API_PREFIX;
|
|
570
|
+
return { baseUrl: trimmed, apiPrefix };
|
|
571
|
+
}
|
|
572
|
+
function guessContentType(filePath) {
|
|
573
|
+
const guessed = (0, mime_types_1.lookup)(filePath);
|
|
574
|
+
if (typeof guessed === "string") {
|
|
575
|
+
return guessed;
|
|
576
|
+
}
|
|
577
|
+
return undefined;
|
|
578
|
+
}
|
|
579
|
+
function isStorageEmulatorUrl(url) {
|
|
580
|
+
try {
|
|
581
|
+
const parsed = new URL(url);
|
|
582
|
+
const host = parsed.hostname.toLowerCase();
|
|
583
|
+
const port = parsed.port ? Number(parsed.port) : parsed.protocol === "https:" ? 443 : 80;
|
|
584
|
+
if (["127.0.0.1", "localhost", "0.0.0.0", "::1"].includes(host)) {
|
|
585
|
+
if (!parsed.port || port === 9199) {
|
|
586
|
+
return true;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
const envHost = typeof process !== "undefined"
|
|
590
|
+
? process.env.STORAGE_EMULATOR_HOST || process.env.FIREBASE_STORAGE_EMULATOR_HOST
|
|
591
|
+
: undefined;
|
|
592
|
+
if (!envHost) {
|
|
593
|
+
return false;
|
|
594
|
+
}
|
|
595
|
+
const normalized = envHost.startsWith("http://") || envHost.startsWith("https://") ? envHost : `http://${envHost}`;
|
|
596
|
+
const emulatorParsed = new URL(normalized);
|
|
597
|
+
const emulatorHost = emulatorParsed.hostname.toLowerCase();
|
|
598
|
+
const emulatorPort = emulatorParsed.port
|
|
599
|
+
? Number(emulatorParsed.port)
|
|
600
|
+
: emulatorParsed.protocol === "https:"
|
|
601
|
+
? 443
|
|
602
|
+
: 80;
|
|
603
|
+
return host === emulatorHost && port === emulatorPort;
|
|
604
|
+
}
|
|
605
|
+
catch {
|
|
606
|
+
return false;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
function isNodeReadable(value) {
|
|
610
|
+
return Boolean(value) && typeof value === "object" && typeof value.pipe === "function";
|
|
611
|
+
}
|
|
612
|
+
async function createFileReadStream(filePath) {
|
|
613
|
+
const fs = await Promise.resolve().then(() => __importStar(require("node:fs")));
|
|
614
|
+
return fs.createReadStream(filePath);
|
|
615
|
+
}
|
|
616
|
+
function getFileSize(filePath) {
|
|
617
|
+
const fs = require("node:fs");
|
|
618
|
+
return fs.statSync(filePath).size;
|
|
619
|
+
}
|
|
620
|
+
function getBaseName(filePath) {
|
|
621
|
+
const path = require("node:path");
|
|
622
|
+
return path.basename(filePath);
|
|
623
|
+
}
|
|
624
|
+
function initRestoreMetadata() {
|
|
625
|
+
return {
|
|
626
|
+
ok: false,
|
|
627
|
+
stage: "start",
|
|
628
|
+
apiProjectId: null,
|
|
629
|
+
generationId: null,
|
|
630
|
+
project: null,
|
|
631
|
+
generation: null,
|
|
632
|
+
progress: null,
|
|
633
|
+
download: null,
|
|
634
|
+
downloadType: null,
|
|
635
|
+
downloadUrl: null,
|
|
636
|
+
fileName: null,
|
|
637
|
+
mimeType: null,
|
|
638
|
+
status: null,
|
|
639
|
+
error: null,
|
|
640
|
+
errorDetails: null,
|
|
641
|
+
exceptionType: null,
|
|
642
|
+
exceptionMessage: null
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
function setRestoreError(metadata, error) {
|
|
646
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
647
|
+
metadata.error = message;
|
|
648
|
+
metadata.exceptionType = error instanceof Error ? error.name : typeof error;
|
|
649
|
+
metadata.exceptionMessage = message;
|
|
650
|
+
}
|
|
651
|
+
function attachRestoreMetadata(error, metadata) {
|
|
652
|
+
if (error && typeof error === "object") {
|
|
653
|
+
try {
|
|
654
|
+
error.restoreInfo = metadata;
|
|
655
|
+
}
|
|
656
|
+
catch {
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
async function reportProgress(progress, onProgress, showProgress) {
|
|
662
|
+
if (onProgress) {
|
|
663
|
+
await onProgress(progress);
|
|
664
|
+
}
|
|
665
|
+
if (showProgress) {
|
|
666
|
+
// eslint-disable-next-line no-console
|
|
667
|
+
console.log(formatProgress(progress));
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
function formatProgress(progress) {
|
|
671
|
+
const parts = [];
|
|
672
|
+
if (progress.preProcessing) {
|
|
673
|
+
parts.push(`pre=${progress.preProcessing.status}:${progress.preProcessing.progress}%`);
|
|
674
|
+
}
|
|
675
|
+
if (progress.inference) {
|
|
676
|
+
parts.push(`inf=${progress.inference.status}:${progress.inference.progress}%`);
|
|
677
|
+
}
|
|
678
|
+
if (progress.restoredVideo) {
|
|
679
|
+
parts.push(`vid=${progress.restoredVideo.status}:${progress.restoredVideo.progress}%`);
|
|
680
|
+
}
|
|
681
|
+
const joined = parts.join(", ");
|
|
682
|
+
if (joined) {
|
|
683
|
+
return `${progress.status} (${joined})`;
|
|
684
|
+
}
|
|
685
|
+
return progress.status;
|
|
686
|
+
}
|
|
687
|
+
async function sleepSeconds(seconds) {
|
|
688
|
+
await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
|
|
689
|
+
}
|