@umituz/react-native-ai-generation-content 1.75.2 → 1.75.4
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 +1 -1
- package/src/domains/generation/wizard/infrastructure/strategies/video-generation.executor.ts +57 -30
- package/src/domains/generation/wizard/infrastructure/utils/credit-value-extractors.ts +28 -7
- package/src/domains/generation/wizard/infrastructure/utils/wizard-data-validators.ts +14 -19
- package/src/domains/generation/wizard/presentation/hooks/useWizardFlowHandlers.ts +11 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.75.
|
|
3
|
+
"version": "1.75.4",
|
|
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/video-generation.executor.ts
CHANGED
|
@@ -61,25 +61,41 @@ export async function executeVideoGeneration(
|
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
// Detect Minimax subject reference model
|
|
65
|
+
const isSubjectReference = model.includes("minimax") || model.includes("hailuo");
|
|
66
|
+
|
|
64
67
|
const modelInput: Record<string, unknown> = {
|
|
65
68
|
prompt: input.prompt,
|
|
66
69
|
};
|
|
67
70
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
71
|
+
if (isSubjectReference) {
|
|
72
|
+
// Minimax Hailuo: Use subject_reference_image_url for face/identity only
|
|
73
|
+
if (sourceImage && sourceImage.length > 0) {
|
|
74
|
+
modelInput.subject_reference_image_url = sourceImage;
|
|
75
|
+
}
|
|
76
|
+
modelInput.prompt_optimizer = true;
|
|
77
|
+
// Minimax expects duration as string
|
|
78
|
+
if (input.duration) {
|
|
79
|
+
modelInput.duration = String(input.duration);
|
|
80
|
+
}
|
|
81
|
+
// Minimax uses uppercase resolution format (512P, 768P)
|
|
82
|
+
if (input.resolution) {
|
|
83
|
+
modelInput.resolution = input.resolution;
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
// Standard models (Grok, etc.): Use image_url
|
|
87
|
+
if (sourceImage && sourceImage.length > 0) {
|
|
88
|
+
modelInput.image_url = sourceImage;
|
|
89
|
+
}
|
|
90
|
+
if (input.duration) {
|
|
91
|
+
modelInput.duration = input.duration;
|
|
92
|
+
}
|
|
93
|
+
if (input.aspectRatio) {
|
|
94
|
+
modelInput.aspect_ratio = input.aspectRatio;
|
|
95
|
+
}
|
|
96
|
+
if (input.resolution) {
|
|
97
|
+
modelInput.resolution = input.resolution;
|
|
98
|
+
}
|
|
83
99
|
}
|
|
84
100
|
|
|
85
101
|
let lastStatus = "";
|
|
@@ -131,25 +147,36 @@ export async function submitVideoGenerationToQueue(
|
|
|
131
147
|
try {
|
|
132
148
|
const sourceImage = formatBase64(input.sourceImageBase64);
|
|
133
149
|
|
|
150
|
+
const isSubjectReference = model.includes("minimax") || model.includes("hailuo");
|
|
151
|
+
|
|
134
152
|
const modelInput: Record<string, unknown> = {
|
|
135
153
|
prompt: input.prompt,
|
|
136
154
|
};
|
|
137
155
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
156
|
+
if (isSubjectReference) {
|
|
157
|
+
if (sourceImage && sourceImage.length > 0) {
|
|
158
|
+
modelInput.subject_reference_image_url = sourceImage;
|
|
159
|
+
}
|
|
160
|
+
modelInput.prompt_optimizer = true;
|
|
161
|
+
if (input.duration) {
|
|
162
|
+
modelInput.duration = String(input.duration);
|
|
163
|
+
}
|
|
164
|
+
if (input.resolution) {
|
|
165
|
+
modelInput.resolution = input.resolution;
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
if (sourceImage && sourceImage.length > 0) {
|
|
169
|
+
modelInput.image_url = sourceImage;
|
|
170
|
+
}
|
|
171
|
+
if (input.duration) {
|
|
172
|
+
modelInput.duration = input.duration;
|
|
173
|
+
}
|
|
174
|
+
if (input.aspectRatio) {
|
|
175
|
+
modelInput.aspect_ratio = input.aspectRatio;
|
|
176
|
+
}
|
|
177
|
+
if (input.resolution) {
|
|
178
|
+
modelInput.resolution = input.resolution;
|
|
179
|
+
}
|
|
153
180
|
}
|
|
154
181
|
|
|
155
182
|
const submission = await provider.submitJob(model, modelInput);
|
|
@@ -2,24 +2,42 @@
|
|
|
2
2
|
* Credit Value Extractors
|
|
3
3
|
* Pure utility functions to extract and normalize values from customData
|
|
4
4
|
* Single Responsibility: Data transformation for credit calculation
|
|
5
|
+
*
|
|
6
|
+
* Handles both raw values and selection format objects:
|
|
7
|
+
* - Raw: "4s", 4, "480p"
|
|
8
|
+
* - Selection format: { uri: "4s", selection: "4s" | 4, previewUrl: "" }
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Unwrap selection format to get the actual value
|
|
13
|
+
* Selection steps store data as { uri, selection, previewUrl } objects
|
|
5
14
|
*/
|
|
15
|
+
function unwrapSelection(value: unknown): unknown {
|
|
16
|
+
if (typeof value === "object" && value !== null && "selection" in value) {
|
|
17
|
+
return (value as Record<string, unknown>).selection;
|
|
18
|
+
}
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
6
21
|
|
|
7
22
|
/**
|
|
8
23
|
* Extract duration value from customData
|
|
9
24
|
* Handles both number and string formats ("4s", "5s", "6s")
|
|
25
|
+
* Also handles selection format objects from wizard steps
|
|
10
26
|
*
|
|
11
27
|
* @param value - Raw value from customData
|
|
12
28
|
* @returns Normalized duration number, or undefined if invalid
|
|
13
29
|
*/
|
|
14
30
|
export function extractDuration(value: unknown): number | undefined {
|
|
31
|
+
const unwrapped = unwrapSelection(value);
|
|
32
|
+
|
|
15
33
|
// Already a number
|
|
16
|
-
if (typeof
|
|
17
|
-
return
|
|
34
|
+
if (typeof unwrapped === "number" && unwrapped > 0) {
|
|
35
|
+
return unwrapped;
|
|
18
36
|
}
|
|
19
37
|
|
|
20
38
|
// String format: "4s", "5s", "6s" → parse to number
|
|
21
|
-
if (typeof
|
|
22
|
-
const match =
|
|
39
|
+
if (typeof unwrapped === "string") {
|
|
40
|
+
const match = unwrapped.match(/^(\d+)s?$/);
|
|
23
41
|
if (match) {
|
|
24
42
|
const parsed = parseInt(match[1], 10);
|
|
25
43
|
return parsed > 0 ? parsed : undefined;
|
|
@@ -32,13 +50,16 @@ export function extractDuration(value: unknown): number | undefined {
|
|
|
32
50
|
/**
|
|
33
51
|
* Extract resolution value from customData
|
|
34
52
|
* Validates against allowed values
|
|
53
|
+
* Also handles selection format objects from wizard steps
|
|
35
54
|
*
|
|
36
55
|
* @param value - Raw value from customData
|
|
37
56
|
* @returns Normalized resolution string, or undefined if invalid
|
|
38
57
|
*/
|
|
39
|
-
export function extractResolution(value: unknown):
|
|
40
|
-
|
|
41
|
-
|
|
58
|
+
export function extractResolution(value: unknown): string | undefined {
|
|
59
|
+
const unwrapped = unwrapSelection(value);
|
|
60
|
+
|
|
61
|
+
if (typeof unwrapped === "string" && unwrapped.length > 0) {
|
|
62
|
+
return unwrapped;
|
|
42
63
|
}
|
|
43
64
|
return undefined;
|
|
44
65
|
}
|
|
@@ -2,8 +2,14 @@
|
|
|
2
2
|
* Wizard Data Validators
|
|
3
3
|
* Centralized validation utilities for wizard generation data
|
|
4
4
|
* DRY: Used across AIGenerateScreen, TextToVideoWizardScreen, ImageToVideoWizardScreen
|
|
5
|
+
*
|
|
6
|
+
* Handles both raw values and selection format objects from wizard steps:
|
|
7
|
+
* - Raw: 4, "480p"
|
|
8
|
+
* - Selection format: { uri: "4s", selection: 4, previewUrl: "" }
|
|
5
9
|
*/
|
|
6
10
|
|
|
11
|
+
import { extractDuration, extractResolution } from "./credit-value-extractors";
|
|
12
|
+
|
|
7
13
|
export interface ValidationResult<T> {
|
|
8
14
|
value?: T;
|
|
9
15
|
error?: string;
|
|
@@ -18,11 +24,11 @@ export interface ValidationResult<T> {
|
|
|
18
24
|
export function validateDuration(
|
|
19
25
|
data: Record<string, unknown>,
|
|
20
26
|
): ValidationResult<number> {
|
|
21
|
-
const duration = data.duration
|
|
27
|
+
const duration = extractDuration(data.duration);
|
|
22
28
|
|
|
23
|
-
if (!duration
|
|
29
|
+
if (!duration) {
|
|
24
30
|
return {
|
|
25
|
-
error: `Invalid duration: ${duration}. Must be a positive number.`,
|
|
31
|
+
error: `Invalid duration: ${JSON.stringify(data.duration)}. Must be a positive number.`,
|
|
26
32
|
};
|
|
27
33
|
}
|
|
28
34
|
|
|
@@ -37,25 +43,14 @@ export function validateDuration(
|
|
|
37
43
|
*/
|
|
38
44
|
export function validateResolution(
|
|
39
45
|
data: Record<string, unknown>,
|
|
40
|
-
): ValidationResult<
|
|
41
|
-
const
|
|
46
|
+
): ValidationResult<string> {
|
|
47
|
+
const resolution = extractResolution(data.resolution);
|
|
42
48
|
|
|
43
|
-
if (!
|
|
49
|
+
if (!resolution) {
|
|
44
50
|
return {
|
|
45
|
-
error: `Invalid resolution: ${
|
|
51
|
+
error: `Invalid resolution: ${JSON.stringify(data.resolution)}. Must be a valid resolution string.`,
|
|
46
52
|
};
|
|
47
53
|
}
|
|
48
54
|
|
|
49
|
-
|
|
50
|
-
if (resolutionValue === "480p") {
|
|
51
|
-
return { value: "480p" };
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (resolutionValue === "720p") {
|
|
55
|
-
return { value: "720p" };
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return {
|
|
59
|
-
error: `Invalid resolution value: "${resolutionValue}". Must be "480p" or "720p".`,
|
|
60
|
-
};
|
|
55
|
+
return { value: resolution };
|
|
61
56
|
}
|
|
@@ -94,8 +94,13 @@ export function useWizardFlowHandlers(props: UseWizardFlowHandlersProps) {
|
|
|
94
94
|
else previousStep();
|
|
95
95
|
}, [currentStepIndex, previousStep, onBack]);
|
|
96
96
|
|
|
97
|
-
const handleNextStep = useCallback(() => {
|
|
97
|
+
const handleNextStep = useCallback((additionalData?: Record<string, unknown>) => {
|
|
98
98
|
const nextStepDef = flowSteps[currentStepIndex + 1];
|
|
99
|
+
// Merge additionalData to avoid stale closure issue
|
|
100
|
+
// When called from handlePhotoContinue, customData in closure may not include the just-set value
|
|
101
|
+
// Guard: Only merge plain objects (ignore SyntheticEvents from onPress handlers)
|
|
102
|
+
const isPlainObject = additionalData && typeof additionalData === "object" && !("nativeEvent" in additionalData) && !Array.isArray(additionalData);
|
|
103
|
+
const mergedData = isPlainObject ? { ...customData, ...additionalData } : customData;
|
|
99
104
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
100
105
|
console.log("[handleNextStep] Called", {
|
|
101
106
|
currentStepIndex,
|
|
@@ -103,14 +108,15 @@ export function useWizardFlowHandlers(props: UseWizardFlowHandlersProps) {
|
|
|
103
108
|
nextStepId: nextStepDef?.id,
|
|
104
109
|
totalSteps: flowSteps.length,
|
|
105
110
|
hasOnGenerationStart: !!onGenerationStart,
|
|
111
|
+
dataKeys: Object.keys(mergedData),
|
|
106
112
|
});
|
|
107
113
|
}
|
|
108
114
|
if (nextStepDef?.type === StepType.GENERATING && onGenerationStart) {
|
|
109
|
-
onGenerationStart(
|
|
115
|
+
onGenerationStart(mergedData, nextStep, handleGenerationError);
|
|
110
116
|
return;
|
|
111
117
|
}
|
|
112
118
|
nextStep();
|
|
113
|
-
}, [currentStepIndex, flowSteps, customData, onGenerationStart, nextStep]);
|
|
119
|
+
}, [currentStepIndex, flowSteps, customData, onGenerationStart, nextStep, handleGenerationError]);
|
|
114
120
|
|
|
115
121
|
const handlePhotoContinue = useCallback(
|
|
116
122
|
(stepId: string, image: UploadedImage) => {
|
|
@@ -118,7 +124,8 @@ export function useWizardFlowHandlers(props: UseWizardFlowHandlersProps) {
|
|
|
118
124
|
console.log("[handlePhotoContinue] Called", { stepId, hasImage: !!image, currentStepIndex });
|
|
119
125
|
}
|
|
120
126
|
setCustomData(stepId, image);
|
|
121
|
-
|
|
127
|
+
// Pass the just-set data to avoid stale closure issue
|
|
128
|
+
handleNextStep({ [stepId]: image });
|
|
122
129
|
},
|
|
123
130
|
[setCustomData, handleNextStep, currentStepIndex],
|
|
124
131
|
);
|