@umituz/react-native-ai-generation-content 1.89.32 → 1.89.34
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.89.
|
|
3
|
+
"version": "1.89.34",
|
|
4
4
|
"description": "Provider-agnostic AI generation orchestration for React Native with result preview components",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
package/src/domains/generation/wizard/infrastructure/strategies/shared/photo-extraction.utils.ts
CHANGED
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
* Shared photo extraction logic for wizard strategies
|
|
4
4
|
*
|
|
5
5
|
* Resize strategy:
|
|
6
|
-
* - Small images (<
|
|
6
|
+
* - Small images (<768px): scale UP to 768px minimum (good face preservation)
|
|
7
7
|
* - Large images (>1536px): scale DOWN to 1536px maximum (reduces upload size ~10x)
|
|
8
8
|
* - Normal images: pass through unchanged
|
|
9
|
+
*
|
|
10
|
+
* IMPORTANT: 768px minimum ensures AI models have enough detail for face preservation
|
|
9
11
|
*/
|
|
10
12
|
|
|
11
13
|
import { readFileAsBase64 } from "@umituz/react-native-design-system/filesystem";
|
|
@@ -14,7 +16,7 @@ import { Image } from "react-native";
|
|
|
14
16
|
import { PHOTO_KEY_PREFIX } from "../wizard-strategy.constants";
|
|
15
17
|
|
|
16
18
|
|
|
17
|
-
const MIN_IMAGE_DIMENSION =
|
|
19
|
+
const MIN_IMAGE_DIMENSION = 768; // Minimum for good face preservation
|
|
18
20
|
const MAX_IMAGE_DIMENSION = 1536;
|
|
19
21
|
|
|
20
22
|
/**
|
|
@@ -28,7 +30,7 @@ function getImageSize(uri: string): Promise<{ width: number; height: number }> {
|
|
|
28
30
|
|
|
29
31
|
/**
|
|
30
32
|
* Ensure image is within optimal dimensions for AI generation.
|
|
31
|
-
* - Too small (<
|
|
33
|
+
* - Too small (<768px): scale up (good face preservation)
|
|
32
34
|
* - Too large (>1536px): scale down (reduces upload size, prevents timeouts)
|
|
33
35
|
* - Within range: return as-is
|
|
34
36
|
*/
|
|
@@ -37,41 +39,76 @@ async function ensureOptimalSize(uri: string): Promise<string> {
|
|
|
37
39
|
const { width, height } = await getImageSize(uri);
|
|
38
40
|
const maxDim = Math.max(width, height);
|
|
39
41
|
|
|
42
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
43
|
+
console.log("[PhotoExtraction] Analyzing image", {
|
|
44
|
+
originalDimensions: `${width}x${height}`,
|
|
45
|
+
maxDim,
|
|
46
|
+
minDim: Math.min(width, height),
|
|
47
|
+
isTooSmall: width < MIN_IMAGE_DIMENSION || height < MIN_IMAGE_DIMENSION,
|
|
48
|
+
isTooLarge: maxDim > MAX_IMAGE_DIMENSION,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
40
52
|
// Already within optimal range
|
|
41
53
|
if (width >= MIN_IMAGE_DIMENSION && height >= MIN_IMAGE_DIMENSION && maxDim <= MAX_IMAGE_DIMENSION) {
|
|
54
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
55
|
+
console.log("[PhotoExtraction] Image already optimal, skipping resize", {
|
|
56
|
+
dimensions: `${width}x${height}`,
|
|
57
|
+
minRequired: MIN_IMAGE_DIMENSION,
|
|
58
|
+
maxAllowed: MAX_IMAGE_DIMENSION,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
42
61
|
return uri;
|
|
43
62
|
}
|
|
44
63
|
|
|
45
64
|
let newWidth: number;
|
|
46
65
|
let newHeight: number;
|
|
66
|
+
let direction: string;
|
|
47
67
|
|
|
48
68
|
if (maxDim > MAX_IMAGE_DIMENSION) {
|
|
49
69
|
// Scale DOWN — largest dimension becomes MAX_IMAGE_DIMENSION
|
|
50
70
|
const scale = MAX_IMAGE_DIMENSION / maxDim;
|
|
51
71
|
newWidth = Math.round(width * scale);
|
|
52
72
|
newHeight = Math.round(height * scale);
|
|
73
|
+
direction = "down";
|
|
53
74
|
} else {
|
|
54
75
|
// Scale UP — smallest dimension becomes MIN_IMAGE_DIMENSION
|
|
55
76
|
const scale = Math.max(MIN_IMAGE_DIMENSION / width, MIN_IMAGE_DIMENSION / height);
|
|
56
77
|
newWidth = Math.ceil(width * scale);
|
|
57
78
|
newHeight = Math.ceil(height * scale);
|
|
79
|
+
direction = "up";
|
|
58
80
|
}
|
|
59
81
|
|
|
82
|
+
const compressQuality = maxDim > MAX_IMAGE_DIMENSION ? 0.8 : 1.0; // Lossless for scale-up
|
|
83
|
+
|
|
60
84
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
61
|
-
const direction = maxDim > MAX_IMAGE_DIMENSION ? "down" : "up";
|
|
62
85
|
console.log(`[PhotoExtraction] Resizing ${direction}`, {
|
|
63
86
|
from: `${width}x${height}`,
|
|
64
87
|
to: `${newWidth}x${newHeight}`,
|
|
88
|
+
scaleChange: `${((Math.max(newWidth, newHeight) / maxDim - 1) * 100).toFixed(0)}%`,
|
|
89
|
+
compressQuality,
|
|
65
90
|
});
|
|
66
91
|
}
|
|
67
92
|
|
|
68
93
|
const result = await manipulateAsync(uri, [{ resize: { width: newWidth, height: newHeight } }], {
|
|
69
94
|
format: SaveFormat.JPEG,
|
|
70
|
-
compress:
|
|
95
|
+
compress: compressQuality,
|
|
71
96
|
});
|
|
72
97
|
|
|
98
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
99
|
+
console.log("[PhotoExtraction] Resize complete", {
|
|
100
|
+
original: `${width}x${height}`,
|
|
101
|
+
resized: `${newWidth}x${newHeight}`,
|
|
102
|
+
action: direction,
|
|
103
|
+
quality: compressQuality === 1.0 ? "lossless" : `${compressQuality * 100}%`,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
73
107
|
return result.uri;
|
|
74
|
-
} catch {
|
|
108
|
+
} catch (error) {
|
|
109
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
110
|
+
console.error("[PhotoExtraction] Resize failed, using original", error);
|
|
111
|
+
}
|
|
75
112
|
return uri;
|
|
76
113
|
}
|
|
77
114
|
}
|
|
@@ -108,18 +145,27 @@ export async function extractPhotosAsBase64(
|
|
|
108
145
|
enableDebugLogs = false,
|
|
109
146
|
): Promise<string[]> {
|
|
110
147
|
if (enableDebugLogs && typeof __DEV__ !== "undefined" && __DEV__) {
|
|
111
|
-
console.log("[PhotoExtraction]
|
|
148
|
+
console.log("[PhotoExtraction] >>> extractPhotosAsBase64 START", {
|
|
112
149
|
wizardDataKeys: Object.keys(wizardData),
|
|
150
|
+
photoKeyPrefix: PHOTO_KEY_PREFIX,
|
|
151
|
+
minDimension: MIN_IMAGE_DIMENSION,
|
|
152
|
+
maxDimension: MAX_IMAGE_DIMENSION,
|
|
113
153
|
});
|
|
114
154
|
}
|
|
115
155
|
|
|
116
156
|
const photoUris = extractPhotoUris(wizardData);
|
|
117
157
|
|
|
118
158
|
if (enableDebugLogs && typeof __DEV__ !== "undefined" && __DEV__) {
|
|
119
|
-
console.log("[PhotoExtraction]
|
|
159
|
+
console.log("[PhotoExtraction] Photo URIs extracted", {
|
|
160
|
+
count: photoUris.length,
|
|
161
|
+
keys: Object.keys(wizardData).filter(k => k.includes(PHOTO_KEY_PREFIX)),
|
|
162
|
+
});
|
|
120
163
|
}
|
|
121
164
|
|
|
122
165
|
if (photoUris.length === 0) {
|
|
166
|
+
if (enableDebugLogs && typeof __DEV__ !== "undefined" && __DEV__) {
|
|
167
|
+
console.log("[PhotoExtraction] No photos found, returning empty array");
|
|
168
|
+
}
|
|
123
169
|
return [];
|
|
124
170
|
}
|
|
125
171
|
|
|
@@ -127,11 +173,29 @@ export async function extractPhotosAsBase64(
|
|
|
127
173
|
const results = await Promise.allSettled(
|
|
128
174
|
photoUris.map(async (uri, index) => {
|
|
129
175
|
try {
|
|
176
|
+
if (enableDebugLogs && typeof __DEV__ !== "undefined" && __DEV__) {
|
|
177
|
+
console.log(`[PhotoExtraction] Processing photo ${index + 1}/${photoUris.length}`, {
|
|
178
|
+
uri: uri.substring(0, 50) + "...",
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
130
182
|
const optimizedUri = await ensureOptimalSize(uri);
|
|
131
|
-
|
|
183
|
+
const base64 = await readFileAsBase64(optimizedUri);
|
|
184
|
+
|
|
185
|
+
if (enableDebugLogs && typeof __DEV__ !== "undefined" && __DEV__) {
|
|
186
|
+
console.log(`[PhotoExtraction] Photo ${index + 1} processed`, {
|
|
187
|
+
sizeKB: (base64.length / 1024).toFixed(1),
|
|
188
|
+
originalUri: uri.substring(0, 30) + "...",
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return base64;
|
|
132
193
|
} catch (error) {
|
|
133
194
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
134
|
-
console.error(`[PhotoExtraction] Failed to
|
|
195
|
+
console.error(`[PhotoExtraction] ❌ Failed to process photo ${index + 1}:`, {
|
|
196
|
+
uri: uri.substring(0, 50) + "...",
|
|
197
|
+
error: error instanceof Error ? error.message : String(error),
|
|
198
|
+
});
|
|
135
199
|
}
|
|
136
200
|
return null;
|
|
137
201
|
}
|
|
@@ -143,15 +207,21 @@ export async function extractPhotosAsBase64(
|
|
|
143
207
|
.map((result) => (result.status === "fulfilled" ? result.value : null))
|
|
144
208
|
.filter((photo): photo is string => typeof photo === "string" && photo.length > 0);
|
|
145
209
|
|
|
210
|
+
const failedCount = results.filter((r) => r.status === "rejected").length;
|
|
211
|
+
|
|
146
212
|
if (enableDebugLogs && typeof __DEV__ !== "undefined" && __DEV__) {
|
|
147
|
-
|
|
148
|
-
console.log("[PhotoExtraction] Converted photos", {
|
|
213
|
+
console.log("[PhotoExtraction] <<< extractPhotosAsBase64 COMPLETE", {
|
|
149
214
|
total: photoUris.length,
|
|
150
|
-
|
|
215
|
+
successful: validPhotos.length,
|
|
151
216
|
failed: failedCount,
|
|
152
|
-
sizes: validPhotos.map((p) =>
|
|
217
|
+
sizes: validPhotos.map((p, i) => `Photo ${i + 1}: ${(p.length / 1024).toFixed(1)}KB`),
|
|
218
|
+
totalSizeMB: ((validPhotos.reduce((sum, p) => sum + p.length, 0) / 1024 / 1024).toFixed(2)),
|
|
153
219
|
});
|
|
154
220
|
}
|
|
155
221
|
|
|
222
|
+
if (failedCount > 0 && typeof __DEV__ !== "undefined" && __DEV__) {
|
|
223
|
+
console.warn(`[PhotoExtraction] ⚠️ ${failedCount} photo(s) failed to process`);
|
|
224
|
+
}
|
|
225
|
+
|
|
156
226
|
return validPhotos;
|
|
157
227
|
}
|