@sogni-ai/sogni-client 5.63.2 → 5.63.3
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/CHANGELOG.md +7 -0
- package/README.md +5 -2
- package/dist/Projects/index.js +19 -58
- package/dist/Projects/index.js.map +1 -1
- package/dist/Projects/utils/fileContentType.d.ts +18 -0
- package/dist/Projects/utils/fileContentType.js +63 -0
- package/dist/Projects/utils/fileContentType.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist-esm/Projects/index.js +9 -48
- package/dist-esm/Projects/index.js.map +1 -1
- package/dist-esm/Projects/utils/fileContentType.js +60 -0
- package/dist-esm/Projects/utils/fileContentType.js.map +1 -0
- package/dist-esm/version.js +1 -1
- package/package.json +2 -1
- package/src/Projects/index.ts +9 -43
- package/src/Projects/utils/fileContentType.ts +55 -0
- package/src/version.ts +1 -1
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect the content type of a reference file from its bytes.
|
|
3
|
+
*
|
|
4
|
+
* A browser `File`/`Blob` carries its own `type`. A Node `Buffer` is sniffed
|
|
5
|
+
* by magic bytes. `slot` says which reference the buffer fills: a
|
|
6
|
+
* Matroska/WebM container (EBML header) and an Ogg container carry either
|
|
7
|
+
* audio or video, and the magic bytes do not say which, so the slot decides
|
|
8
|
+
* between `audio/webm` and `video/webm` (`audio/ogg` and `video/ogg`).
|
|
9
|
+
*
|
|
10
|
+
* The declared type matters past the upload: the socket keeps audio guides the
|
|
11
|
+
* Mac app's AVFoundation decoder cannot read (OGG, WebM) off Mac app workers
|
|
12
|
+
* only when the type is declared; an undeclared guide is routed blind and the
|
|
13
|
+
* render fails there (sogni-socket constants/appMlxWorker.js
|
|
14
|
+
* APP_MLX_AUDIO_GUIDE_CONTENT_TYPES, 2026-10).
|
|
15
|
+
*
|
|
16
|
+
* Returns undefined when the bytes match nothing known.
|
|
17
|
+
*/
|
|
18
|
+
export function getFileContentType(file, slot) {
|
|
19
|
+
if (file instanceof Blob && 'type' in file && file.type) {
|
|
20
|
+
return file.type;
|
|
21
|
+
}
|
|
22
|
+
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(file)) {
|
|
23
|
+
if (file.length >= 12) {
|
|
24
|
+
if (file[0] === 0xff && file[1] === 0xd8 && file[2] === 0xff)
|
|
25
|
+
return 'image/jpeg';
|
|
26
|
+
if (file[0] === 0x89 && file[1] === 0x50 && file[2] === 0x4e && file[3] === 0x47) {
|
|
27
|
+
return 'image/png';
|
|
28
|
+
}
|
|
29
|
+
if (file.toString('ascii', 0, 4) === 'RIFF' && file.toString('ascii', 8, 12) === 'WEBP') {
|
|
30
|
+
return 'image/webp';
|
|
31
|
+
}
|
|
32
|
+
if (file.toString('ascii', 0, 3) === 'GIF')
|
|
33
|
+
return 'image/gif';
|
|
34
|
+
if (file.toString('ascii', 4, 8) === 'ftyp') {
|
|
35
|
+
const brand = file.toString('ascii', 8, 12).toLowerCase();
|
|
36
|
+
if (brand.includes('m4a') || brand.includes('m4b'))
|
|
37
|
+
return 'audio/mp4';
|
|
38
|
+
if (brand.includes('qt'))
|
|
39
|
+
return 'video/quicktime';
|
|
40
|
+
return 'video/mp4';
|
|
41
|
+
}
|
|
42
|
+
if (file.toString('ascii', 0, 4) === 'RIFF' && file.toString('ascii', 8, 12) === 'WAVE') {
|
|
43
|
+
return 'audio/wav';
|
|
44
|
+
}
|
|
45
|
+
if (file.toString('ascii', 0, 4) === 'fLaC')
|
|
46
|
+
return 'audio/flac';
|
|
47
|
+
if (file.toString('ascii', 0, 4) === 'OggS')
|
|
48
|
+
return slot === 'video' ? 'video/ogg' : 'audio/ogg';
|
|
49
|
+
if (file[0] === 0x1a && file[1] === 0x45 && file[2] === 0xdf && file[3] === 0xa3) {
|
|
50
|
+
return slot === 'audio' ? 'audio/webm' : 'video/webm';
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (file.length >= 3 && file.toString('ascii', 0, 3) === 'ID3')
|
|
54
|
+
return 'audio/mpeg';
|
|
55
|
+
if (file.length >= 2 && file[0] === 0xff && (file[1] & 0xe0) === 0xe0)
|
|
56
|
+
return 'audio/mpeg';
|
|
57
|
+
}
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=fileContentType.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileContentType.js","sourceRoot":"","sources":["../../../src/Projects/utils/fileContentType.ts"],"names":[],"mappings":"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"}
|
package/dist-esm/version.js
CHANGED
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "5.63.
|
|
6
|
+
"version": "5.63.3",
|
|
7
7
|
"engines": {
|
|
8
8
|
"node": ">=22"
|
|
9
9
|
},
|
|
@@ -80,6 +80,7 @@
|
|
|
80
80
|
"test:capability-exports": "npm run build && node scripts/check-capability-exports.cjs",
|
|
81
81
|
"test:generation-receipt-transport": "npm run build && node scripts/check-generation-receipt-transport.cjs",
|
|
82
82
|
"test:prompt-expansion": "npm run build && node scripts/check-prompt-expansion-transport.cjs",
|
|
83
|
+
"test:reference-content-types": "npm run build && node scripts/check-reference-content-types.cjs",
|
|
83
84
|
"test:minimax-h3-audio-policy": "node scripts/check-minimax-h3-source-audio-policy.cjs",
|
|
84
85
|
"test:minimax-h3-balanced": "npm run build && node scripts/check-minimax-h3-balanced.cjs",
|
|
85
86
|
"test:minimax-h3-two-stage": "npm run build && node scripts/check-minimax-h3-two-stage.cjs",
|
package/src/Projects/index.ts
CHANGED
|
@@ -88,6 +88,7 @@ import {
|
|
|
88
88
|
type ResultMediaKind,
|
|
89
89
|
usesReferenceMask
|
|
90
90
|
} from './utils/index.js';
|
|
91
|
+
import { getFileContentType } from './utils/fileContentType.js';
|
|
91
92
|
import { TokenType } from '../types/token.js';
|
|
92
93
|
import type { JobProvenance } from './types/JobProvenance.js';
|
|
93
94
|
import { getMaxContextImages, validateSampler, isGptImageModel } from '../lib/validation.js';
|
|
@@ -249,41 +250,6 @@ function deriveLoraCapableModelIds(loras: LoraCatalogEntry[]): string[] {
|
|
|
249
250
|
return Array.from(modelIds).sort();
|
|
250
251
|
}
|
|
251
252
|
|
|
252
|
-
/**
|
|
253
|
-
* Detect content type from a file object.
|
|
254
|
-
* For File objects in browser, uses the type property.
|
|
255
|
-
* Returns undefined if content type cannot be detected.
|
|
256
|
-
*/
|
|
257
|
-
function getFileContentType(file: File | Buffer | Blob): string | undefined {
|
|
258
|
-
if (file instanceof Blob && 'type' in file && file.type) {
|
|
259
|
-
return file.type;
|
|
260
|
-
}
|
|
261
|
-
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(file)) {
|
|
262
|
-
if (file.length >= 12) {
|
|
263
|
-
if (file[0] === 0xff && file[1] === 0xd8 && file[2] === 0xff) return 'image/jpeg';
|
|
264
|
-
if (file[0] === 0x89 && file[1] === 0x50 && file[2] === 0x4e && file[3] === 0x47) {
|
|
265
|
-
return 'image/png';
|
|
266
|
-
}
|
|
267
|
-
if (file.toString('ascii', 0, 4) === 'RIFF' && file.toString('ascii', 8, 12) === 'WEBP') {
|
|
268
|
-
return 'image/webp';
|
|
269
|
-
}
|
|
270
|
-
if (file.toString('ascii', 0, 3) === 'GIF') return 'image/gif';
|
|
271
|
-
if (file.toString('ascii', 4, 8) === 'ftyp') {
|
|
272
|
-
const brand = file.toString('ascii', 8, 12).toLowerCase();
|
|
273
|
-
if (brand.includes('m4a') || brand.includes('m4b')) return 'audio/mp4';
|
|
274
|
-
if (brand.includes('qt')) return 'video/quicktime';
|
|
275
|
-
return 'video/mp4';
|
|
276
|
-
}
|
|
277
|
-
if (file.toString('ascii', 0, 4) === 'RIFF' && file.toString('ascii', 8, 12) === 'WAVE') {
|
|
278
|
-
return 'audio/wav';
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
if (file.length >= 3 && file.toString('ascii', 0, 3) === 'ID3') return 'audio/mpeg';
|
|
282
|
-
if (file.length >= 2 && file[0] === 0xff && (file[1] & 0xe0) === 0xe0) return 'audio/mpeg';
|
|
283
|
-
}
|
|
284
|
-
return undefined;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
253
|
/**
|
|
288
254
|
* Convert file to a format compatible with fetch body.
|
|
289
255
|
* Converts Node.js Buffer to Blob for cross-platform compatibility.
|
|
@@ -2473,7 +2439,7 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
|
|
|
2473
2439
|
const keyFrame = request.keyFrames?.[0];
|
|
2474
2440
|
if (!keyFrame) return;
|
|
2475
2441
|
if (data.referenceAudio && data.referenceAudio !== true) {
|
|
2476
|
-
keyFrame.referenceAudioContentType = getFileContentType(data.referenceAudio);
|
|
2442
|
+
keyFrame.referenceAudioContentType = getFileContentType(data.referenceAudio, 'audio');
|
|
2477
2443
|
}
|
|
2478
2444
|
}
|
|
2479
2445
|
|
|
@@ -2548,25 +2514,25 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
|
|
|
2548
2514
|
}
|
|
2549
2515
|
const isMinimaxH3R2v = isMinimaxH3ReferenceModel(data.modelId);
|
|
2550
2516
|
if (!isMinimaxH3R2v && data.referenceAudio && data.referenceAudio !== true) {
|
|
2551
|
-
keyFrame.referenceAudioContentType = getFileContentType(data.referenceAudio);
|
|
2517
|
+
keyFrame.referenceAudioContentType = getFileContentType(data.referenceAudio, 'audio');
|
|
2552
2518
|
}
|
|
2553
2519
|
if (data.referenceAudioIdentity && data.referenceAudioIdentity !== true) {
|
|
2554
|
-
const contentType = getFileContentType(data.referenceAudioIdentity);
|
|
2520
|
+
const contentType = getFileContentType(data.referenceAudioIdentity, 'audio');
|
|
2555
2521
|
keyFrame.referenceAudioIdentityContentType = contentType;
|
|
2556
2522
|
keyFrame.referenceAudioContentType ??= contentType;
|
|
2557
2523
|
}
|
|
2558
2524
|
if (!isMinimaxH3R2v && data.referenceVideo && data.referenceVideo !== true) {
|
|
2559
|
-
keyFrame.referenceVideoContentType = getFileContentType(data.referenceVideo);
|
|
2525
|
+
keyFrame.referenceVideoContentType = getFileContentType(data.referenceVideo, 'video');
|
|
2560
2526
|
}
|
|
2561
2527
|
if (isMinimaxH3R2v) {
|
|
2562
2528
|
for (const { slot, media } of getMinimaxH3ReferenceAudioSlots(data)) {
|
|
2563
2529
|
if (typeof media !== 'boolean') {
|
|
2564
|
-
keyFrame[`referenceAudio${slot}ContentType`] = getFileContentType(media);
|
|
2530
|
+
keyFrame[`referenceAudio${slot}ContentType`] = getFileContentType(media, 'audio');
|
|
2565
2531
|
}
|
|
2566
2532
|
}
|
|
2567
2533
|
for (const { slot, media } of getMinimaxH3ReferenceVideoSlots(data)) {
|
|
2568
2534
|
if (typeof media !== 'boolean') {
|
|
2569
|
-
keyFrame[`referenceVideo${slot}ContentType`] = getFileContentType(media);
|
|
2535
|
+
keyFrame[`referenceVideo${slot}ContentType`] = getFileContentType(media, 'video');
|
|
2570
2536
|
}
|
|
2571
2537
|
}
|
|
2572
2538
|
}
|
|
@@ -3233,7 +3199,7 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
|
|
|
3233
3199
|
const assertSession =
|
|
3234
3200
|
this._preparingSessions.get(projectId) ?? captureRequestSession(this.client.auth);
|
|
3235
3201
|
assertSession();
|
|
3236
|
-
const contentType = getFileContentType(file);
|
|
3202
|
+
const contentType = getFileContentType(file, 'audio');
|
|
3237
3203
|
if (await this.assets.tryBindFile(file, contentType, { projectId, type: 'referenceAudio', id }))
|
|
3238
3204
|
return;
|
|
3239
3205
|
assertSession();
|
|
@@ -3272,7 +3238,7 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
|
|
|
3272
3238
|
const assertSession =
|
|
3273
3239
|
this._preparingSessions.get(projectId) ?? captureRequestSession(this.client.auth);
|
|
3274
3240
|
assertSession();
|
|
3275
|
-
const contentType = getFileContentType(file);
|
|
3241
|
+
const contentType = getFileContentType(file, 'video');
|
|
3276
3242
|
if (await this.assets.tryBindFile(file, contentType, { projectId, type: 'referenceVideo', id }))
|
|
3277
3243
|
return;
|
|
3278
3244
|
assertSession();
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect the content type of a reference file from its bytes.
|
|
3
|
+
*
|
|
4
|
+
* A browser `File`/`Blob` carries its own `type`. A Node `Buffer` is sniffed
|
|
5
|
+
* by magic bytes. `slot` says which reference the buffer fills: a
|
|
6
|
+
* Matroska/WebM container (EBML header) and an Ogg container carry either
|
|
7
|
+
* audio or video, and the magic bytes do not say which, so the slot decides
|
|
8
|
+
* between `audio/webm` and `video/webm` (`audio/ogg` and `video/ogg`).
|
|
9
|
+
*
|
|
10
|
+
* The declared type matters past the upload: the socket keeps audio guides the
|
|
11
|
+
* Mac app's AVFoundation decoder cannot read (OGG, WebM) off Mac app workers
|
|
12
|
+
* only when the type is declared; an undeclared guide is routed blind and the
|
|
13
|
+
* render fails there (sogni-socket constants/appMlxWorker.js
|
|
14
|
+
* APP_MLX_AUDIO_GUIDE_CONTENT_TYPES, 2026-10).
|
|
15
|
+
*
|
|
16
|
+
* Returns undefined when the bytes match nothing known.
|
|
17
|
+
*/
|
|
18
|
+
export function getFileContentType(
|
|
19
|
+
file: File | Buffer | Blob,
|
|
20
|
+
slot?: 'audio' | 'video'
|
|
21
|
+
): string | undefined {
|
|
22
|
+
if (file instanceof Blob && 'type' in file && file.type) {
|
|
23
|
+
return file.type;
|
|
24
|
+
}
|
|
25
|
+
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(file)) {
|
|
26
|
+
if (file.length >= 12) {
|
|
27
|
+
if (file[0] === 0xff && file[1] === 0xd8 && file[2] === 0xff) return 'image/jpeg';
|
|
28
|
+
if (file[0] === 0x89 && file[1] === 0x50 && file[2] === 0x4e && file[3] === 0x47) {
|
|
29
|
+
return 'image/png';
|
|
30
|
+
}
|
|
31
|
+
if (file.toString('ascii', 0, 4) === 'RIFF' && file.toString('ascii', 8, 12) === 'WEBP') {
|
|
32
|
+
return 'image/webp';
|
|
33
|
+
}
|
|
34
|
+
if (file.toString('ascii', 0, 3) === 'GIF') return 'image/gif';
|
|
35
|
+
if (file.toString('ascii', 4, 8) === 'ftyp') {
|
|
36
|
+
const brand = file.toString('ascii', 8, 12).toLowerCase();
|
|
37
|
+
if (brand.includes('m4a') || brand.includes('m4b')) return 'audio/mp4';
|
|
38
|
+
if (brand.includes('qt')) return 'video/quicktime';
|
|
39
|
+
return 'video/mp4';
|
|
40
|
+
}
|
|
41
|
+
if (file.toString('ascii', 0, 4) === 'RIFF' && file.toString('ascii', 8, 12) === 'WAVE') {
|
|
42
|
+
return 'audio/wav';
|
|
43
|
+
}
|
|
44
|
+
if (file.toString('ascii', 0, 4) === 'fLaC') return 'audio/flac';
|
|
45
|
+
if (file.toString('ascii', 0, 4) === 'OggS')
|
|
46
|
+
return slot === 'video' ? 'video/ogg' : 'audio/ogg';
|
|
47
|
+
if (file[0] === 0x1a && file[1] === 0x45 && file[2] === 0xdf && file[3] === 0xa3) {
|
|
48
|
+
return slot === 'audio' ? 'audio/webm' : 'video/webm';
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (file.length >= 3 && file.toString('ascii', 0, 3) === 'ID3') return 'audio/mpeg';
|
|
52
|
+
if (file.length >= 2 && file[0] === 0xff && (file[1] & 0xe0) === 0xe0) return 'audio/mpeg';
|
|
53
|
+
}
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
package/src/version.ts
CHANGED