@umituz/react-native-ai-generation-content 1.17.138 → 1.17.140
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/features/ai-hug/presentation/components/AIHugFeature.tsx +0 -1
- package/src/features/ai-kiss/presentation/components/AIKissFeature.tsx +0 -1
- package/src/features/face-swap/presentation/components/FaceSwapFeature.tsx +0 -1
- package/src/features/image-to-video/domain/types/image-to-video.types.ts +33 -0
- package/src/features/image-to-video/domain/types/index.ts +4 -0
- package/src/features/image-to-video/index.ts +4 -0
- package/src/features/image-to-video/infrastructure/services/image-to-video-executor.ts +21 -2
- package/src/features/image-to-video/presentation/hooks/useImageToVideoFeature.ts +175 -47
package/package.json
CHANGED
|
@@ -121,7 +121,6 @@ export const AIHugFeature: React.FC<AIHugFeatureProps> = ({
|
|
|
121
121
|
sourcePlaceholder={translations.sourceUploadTitle}
|
|
122
122
|
targetPlaceholder={translations.targetUploadTitle}
|
|
123
123
|
layout="horizontal"
|
|
124
|
-
variant="portrait"
|
|
125
124
|
/>
|
|
126
125
|
</View>
|
|
127
126
|
</AIGenerationForm>
|
|
@@ -115,7 +115,6 @@ export const AIKissFeature: React.FC<AIKissFeatureProps> = ({
|
|
|
115
115
|
sourcePlaceholder={translations.sourceUploadTitle}
|
|
116
116
|
targetPlaceholder={translations.targetUploadTitle}
|
|
117
117
|
layout="horizontal"
|
|
118
|
-
variant="portrait"
|
|
119
118
|
/>
|
|
120
119
|
</View>
|
|
121
120
|
</AIGenerationForm>
|
|
@@ -118,7 +118,6 @@ export const FaceSwapFeature: React.FC<FaceSwapFeatureProps> = ({
|
|
|
118
118
|
sourcePlaceholder={translations.sourceUploadTitle}
|
|
119
119
|
targetPlaceholder={translations.targetUploadTitle}
|
|
120
120
|
layout="horizontal"
|
|
121
|
-
variant="portrait"
|
|
122
121
|
/>
|
|
123
122
|
</View>
|
|
124
123
|
</AIGenerationForm>
|
|
@@ -16,6 +16,11 @@ export interface ImageToVideoOptions {
|
|
|
16
16
|
musicMood?: MusicMoodId;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
export interface ImageToVideoGenerateParams extends ImageToVideoOptions {
|
|
20
|
+
imageUri?: string;
|
|
21
|
+
motionPrompt?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
19
24
|
export interface ImageToVideoRequest {
|
|
20
25
|
imageUri: string;
|
|
21
26
|
imageBase64?: string;
|
|
@@ -75,6 +80,34 @@ export type ImageToVideoResultExtractor = (
|
|
|
75
80
|
result: unknown,
|
|
76
81
|
) => { videoUrl?: string; thumbnailUrl?: string } | undefined;
|
|
77
82
|
|
|
83
|
+
export interface ImageToVideoFeatureCallbacks {
|
|
84
|
+
onCreditCheck?: (cost: number) => Promise<boolean>;
|
|
85
|
+
onCreditDeduct?: (cost: number) => Promise<void>;
|
|
86
|
+
onAuthCheck?: () => boolean;
|
|
87
|
+
onShowPaywall?: (creditCost: number) => void;
|
|
88
|
+
onGenerationStart?: (data: ImageToVideoGenerationStartData) => Promise<void>;
|
|
89
|
+
onCreationSave?: (data: ImageToVideoCreationData) => Promise<void>;
|
|
90
|
+
onGenerate?: (result: ImageToVideoResult) => void;
|
|
91
|
+
onProgress?: (progress: number) => void;
|
|
92
|
+
onError?: (error: string) => void;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface ImageToVideoGenerationStartData {
|
|
96
|
+
creationId: string;
|
|
97
|
+
type: string;
|
|
98
|
+
imageUri: string;
|
|
99
|
+
metadata?: Record<string, unknown>;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface ImageToVideoCreationData {
|
|
103
|
+
creationId: string;
|
|
104
|
+
type: string;
|
|
105
|
+
videoUrl: string;
|
|
106
|
+
thumbnailUrl?: string;
|
|
107
|
+
imageUri: string;
|
|
108
|
+
metadata?: Record<string, unknown>;
|
|
109
|
+
}
|
|
110
|
+
|
|
78
111
|
export interface ImageToVideoFeatureConfig {
|
|
79
112
|
providerId?: string;
|
|
80
113
|
creditCost?: number;
|
|
@@ -24,6 +24,7 @@ export type {
|
|
|
24
24
|
// Core Feature Types
|
|
25
25
|
export type {
|
|
26
26
|
ImageToVideoOptions,
|
|
27
|
+
ImageToVideoGenerateParams,
|
|
27
28
|
ImageToVideoRequest,
|
|
28
29
|
ImageToVideoResult,
|
|
29
30
|
ImageToVideoGenerationState,
|
|
@@ -31,5 +32,8 @@ export type {
|
|
|
31
32
|
ImageToVideoTranslations,
|
|
32
33
|
ImageToVideoInputBuilder,
|
|
33
34
|
ImageToVideoResultExtractor,
|
|
35
|
+
ImageToVideoFeatureCallbacks,
|
|
36
|
+
ImageToVideoGenerationStartData,
|
|
37
|
+
ImageToVideoCreationData,
|
|
34
38
|
ImageToVideoFeatureConfig,
|
|
35
39
|
} from "./image-to-video.types";
|
|
@@ -33,6 +33,7 @@ export type {
|
|
|
33
33
|
// Core Feature Types
|
|
34
34
|
export type {
|
|
35
35
|
ImageToVideoOptions,
|
|
36
|
+
ImageToVideoGenerateParams,
|
|
36
37
|
ImageToVideoRequest,
|
|
37
38
|
ImageToVideoResult,
|
|
38
39
|
ImageToVideoGenerationState,
|
|
@@ -40,6 +41,9 @@ export type {
|
|
|
40
41
|
ImageToVideoTranslations,
|
|
41
42
|
ImageToVideoInputBuilder,
|
|
42
43
|
ImageToVideoResultExtractor,
|
|
44
|
+
ImageToVideoFeatureCallbacks,
|
|
45
|
+
ImageToVideoGenerationStartData,
|
|
46
|
+
ImageToVideoCreationData,
|
|
43
47
|
ImageToVideoFeatureConfig,
|
|
44
48
|
} from "./domain";
|
|
45
49
|
|
|
@@ -78,9 +78,21 @@ export async function executeImageToVideo(
|
|
|
78
78
|
onProgress?.(30);
|
|
79
79
|
|
|
80
80
|
const input = buildInput(imageBase64, request.motionPrompt, request.options);
|
|
81
|
+
|
|
82
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
83
|
+
// eslint-disable-next-line no-console
|
|
84
|
+
console.log("[ImageToVideo] Input keys:", Object.keys(input));
|
|
85
|
+
}
|
|
86
|
+
|
|
81
87
|
onProgress?.(40);
|
|
82
88
|
|
|
83
89
|
const result = await provider.run(model, input);
|
|
90
|
+
|
|
91
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
92
|
+
// eslint-disable-next-line no-console
|
|
93
|
+
console.log("[ImageToVideo] Result received:", JSON.stringify(result, null, 2).slice(0, 500));
|
|
94
|
+
}
|
|
95
|
+
|
|
84
96
|
onProgress?.(90);
|
|
85
97
|
|
|
86
98
|
const extractor = extractResult || defaultExtractResult;
|
|
@@ -88,6 +100,10 @@ export async function executeImageToVideo(
|
|
|
88
100
|
onProgress?.(100);
|
|
89
101
|
|
|
90
102
|
if (!extracted?.videoUrl) {
|
|
103
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
104
|
+
// eslint-disable-next-line no-console
|
|
105
|
+
console.error("[ImageToVideo] No video URL in extracted result:", extracted);
|
|
106
|
+
}
|
|
91
107
|
return { success: false, error: "No video in response" };
|
|
92
108
|
}
|
|
93
109
|
|
|
@@ -98,11 +114,14 @@ export async function executeImageToVideo(
|
|
|
98
114
|
};
|
|
99
115
|
} catch (error) {
|
|
100
116
|
const message = error instanceof Error ? error.message : String(error);
|
|
117
|
+
const fullError = error instanceof Error ? error.stack : JSON.stringify(error);
|
|
101
118
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
102
119
|
// eslint-disable-next-line no-console
|
|
103
|
-
console.error("[ImageToVideo]
|
|
120
|
+
console.error("[ImageToVideo] Execution error:", message);
|
|
121
|
+
// eslint-disable-next-line no-console
|
|
122
|
+
console.error("[ImageToVideo] Full error:", fullError);
|
|
104
123
|
}
|
|
105
|
-
return { success: false, error: message };
|
|
124
|
+
return { success: false, error: message || "Generation failed" };
|
|
106
125
|
}
|
|
107
126
|
}
|
|
108
127
|
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Image-to-Video Feature Hook
|
|
3
|
-
* Provider-agnostic hook
|
|
3
|
+
* Provider-agnostic hook with callbacks integration
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { useState, useCallback } from "react";
|
|
6
|
+
import { useState, useCallback, useMemo } from "react";
|
|
7
7
|
import { executeImageToVideo } from "../../infrastructure/services";
|
|
8
8
|
import type {
|
|
9
9
|
ImageToVideoFeatureState,
|
|
10
10
|
ImageToVideoFeatureConfig,
|
|
11
11
|
ImageToVideoResult,
|
|
12
|
-
|
|
12
|
+
ImageToVideoFeatureCallbacks,
|
|
13
|
+
ImageToVideoGenerateParams,
|
|
13
14
|
} from "../../domain/types";
|
|
14
15
|
|
|
16
|
+
declare const __DEV__: boolean;
|
|
17
|
+
|
|
15
18
|
export interface UseImageToVideoFeatureProps {
|
|
16
19
|
config: ImageToVideoFeatureConfig;
|
|
20
|
+
callbacks?: ImageToVideoFeatureCallbacks;
|
|
17
21
|
userId: string;
|
|
18
22
|
}
|
|
19
23
|
|
|
@@ -21,12 +25,13 @@ export interface UseImageToVideoFeatureReturn {
|
|
|
21
25
|
state: ImageToVideoFeatureState;
|
|
22
26
|
setImageUri: (uri: string) => void;
|
|
23
27
|
setMotionPrompt: (prompt: string) => void;
|
|
24
|
-
generate: (
|
|
28
|
+
generate: (params?: ImageToVideoGenerateParams) => Promise<ImageToVideoResult>;
|
|
25
29
|
reset: () => void;
|
|
26
30
|
isReady: boolean;
|
|
31
|
+
canGenerate: boolean;
|
|
27
32
|
}
|
|
28
33
|
|
|
29
|
-
const
|
|
34
|
+
const INITIAL_STATE: ImageToVideoFeatureState = {
|
|
30
35
|
imageUri: null,
|
|
31
36
|
motionPrompt: "",
|
|
32
37
|
videoUrl: null,
|
|
@@ -39,8 +44,8 @@ const initialState: ImageToVideoFeatureState = {
|
|
|
39
44
|
export function useImageToVideoFeature(
|
|
40
45
|
props: UseImageToVideoFeatureProps,
|
|
41
46
|
): UseImageToVideoFeatureReturn {
|
|
42
|
-
const { config, userId } = props;
|
|
43
|
-
const [state, setState] = useState<ImageToVideoFeatureState>(
|
|
47
|
+
const { config, callbacks, userId } = props;
|
|
48
|
+
const [state, setState] = useState<ImageToVideoFeatureState>(INITIAL_STATE);
|
|
44
49
|
|
|
45
50
|
const setImageUri = useCallback(
|
|
46
51
|
(uri: string) => {
|
|
@@ -54,68 +59,191 @@ export function useImageToVideoFeature(
|
|
|
54
59
|
setState((prev) => ({ ...prev, motionPrompt: prompt }));
|
|
55
60
|
}, []);
|
|
56
61
|
|
|
57
|
-
const
|
|
58
|
-
async (
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
62
|
+
const executeGeneration = useCallback(
|
|
63
|
+
async (
|
|
64
|
+
imageUri: string,
|
|
65
|
+
motionPrompt: string,
|
|
66
|
+
options?: Omit<ImageToVideoGenerateParams, "imageUri" | "motionPrompt">,
|
|
67
|
+
): Promise<ImageToVideoResult> => {
|
|
68
|
+
const creationId = `image-to-video_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
64
69
|
|
|
65
70
|
setState((prev) => ({
|
|
66
71
|
...prev,
|
|
72
|
+
imageUri,
|
|
67
73
|
isProcessing: true,
|
|
68
74
|
progress: 0,
|
|
69
75
|
error: null,
|
|
70
76
|
}));
|
|
71
77
|
|
|
78
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
79
|
+
// eslint-disable-next-line no-console
|
|
80
|
+
console.log("[ImageToVideoFeature] Starting generation:", { imageUri, creationId });
|
|
81
|
+
}
|
|
82
|
+
|
|
72
83
|
config.onProcessingStart?.();
|
|
73
84
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
imageUri
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
extractResult: config.extractResult,
|
|
88
|
-
onProgress: (progress) => {
|
|
89
|
-
setState((prev) => ({ ...prev, progress }));
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
);
|
|
85
|
+
if (callbacks?.onGenerationStart) {
|
|
86
|
+
callbacks.onGenerationStart({
|
|
87
|
+
creationId,
|
|
88
|
+
type: "image-to-video",
|
|
89
|
+
imageUri,
|
|
90
|
+
metadata: options as Record<string, unknown> | undefined,
|
|
91
|
+
}).catch((err) => {
|
|
92
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
93
|
+
// eslint-disable-next-line no-console
|
|
94
|
+
console.warn("[ImageToVideoFeature] onGenerationStart failed:", err);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
93
98
|
|
|
94
|
-
|
|
99
|
+
try {
|
|
100
|
+
const imageBase64 = await config.prepareImage(imageUri);
|
|
101
|
+
|
|
102
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
103
|
+
// eslint-disable-next-line no-console
|
|
104
|
+
console.log("[ImageToVideoFeature] Image prepared, calling executeImageToVideo");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const result = await executeImageToVideo(
|
|
108
|
+
{
|
|
109
|
+
imageUri,
|
|
110
|
+
imageBase64,
|
|
111
|
+
userId,
|
|
112
|
+
motionPrompt: motionPrompt || undefined,
|
|
113
|
+
options,
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
model: config.model,
|
|
117
|
+
buildInput: config.buildInput,
|
|
118
|
+
extractResult: config.extractResult,
|
|
119
|
+
onProgress: (progress) => {
|
|
120
|
+
setState((prev) => ({ ...prev, progress }));
|
|
121
|
+
callbacks?.onProgress?.(progress);
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
if (result.success && result.videoUrl) {
|
|
127
|
+
setState((prev) => ({
|
|
128
|
+
...prev,
|
|
129
|
+
videoUrl: result.videoUrl ?? null,
|
|
130
|
+
thumbnailUrl: result.thumbnailUrl ?? null,
|
|
131
|
+
isProcessing: false,
|
|
132
|
+
progress: 100,
|
|
133
|
+
}));
|
|
134
|
+
|
|
135
|
+
if (callbacks?.onCreditDeduct && config.creditCost) {
|
|
136
|
+
await callbacks.onCreditDeduct(config.creditCost);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (callbacks?.onCreationSave) {
|
|
140
|
+
await callbacks.onCreationSave({
|
|
141
|
+
creationId,
|
|
142
|
+
type: "image-to-video",
|
|
143
|
+
videoUrl: result.videoUrl,
|
|
144
|
+
thumbnailUrl: result.thumbnailUrl,
|
|
145
|
+
imageUri,
|
|
146
|
+
metadata: options as Record<string, unknown> | undefined,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
callbacks?.onGenerate?.(result);
|
|
151
|
+
} else {
|
|
152
|
+
const error = result.error || "Generation failed";
|
|
153
|
+
setState((prev) => ({ ...prev, isProcessing: false, error }));
|
|
154
|
+
config.onError?.(error);
|
|
155
|
+
callbacks?.onError?.(error);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
config.onProcessingComplete?.(result);
|
|
159
|
+
return result;
|
|
160
|
+
} catch (err) {
|
|
161
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
162
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
163
|
+
// eslint-disable-next-line no-console
|
|
164
|
+
console.error("[ImageToVideoFeature] Generation error:", errorMessage);
|
|
165
|
+
}
|
|
95
166
|
setState((prev) => ({
|
|
96
167
|
...prev,
|
|
97
|
-
videoUrl: result.videoUrl ?? null,
|
|
98
|
-
thumbnailUrl: result.thumbnailUrl ?? null,
|
|
99
168
|
isProcessing: false,
|
|
100
|
-
|
|
169
|
+
error: errorMessage,
|
|
101
170
|
}));
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
config.onError?.(error);
|
|
171
|
+
config.onError?.(errorMessage);
|
|
172
|
+
callbacks?.onError?.(errorMessage);
|
|
173
|
+
return { success: false, error: errorMessage };
|
|
106
174
|
}
|
|
175
|
+
},
|
|
176
|
+
[userId, config, callbacks],
|
|
177
|
+
);
|
|
107
178
|
|
|
108
|
-
|
|
109
|
-
|
|
179
|
+
const generate = useCallback(
|
|
180
|
+
async (params?: ImageToVideoGenerateParams): Promise<ImageToVideoResult> => {
|
|
181
|
+
const { imageUri: paramImageUri, motionPrompt: paramMotionPrompt, ...options } = params || {};
|
|
182
|
+
const effectiveImageUri = paramImageUri || state.imageUri;
|
|
183
|
+
const effectiveMotionPrompt = paramMotionPrompt ?? state.motionPrompt;
|
|
184
|
+
|
|
185
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
186
|
+
// eslint-disable-next-line no-console
|
|
187
|
+
console.log("[ImageToVideoFeature] generate called:", {
|
|
188
|
+
paramImageUri,
|
|
189
|
+
stateImageUri: state.imageUri,
|
|
190
|
+
effectiveImageUri,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (!effectiveImageUri) {
|
|
195
|
+
const error = "Image is required";
|
|
196
|
+
setState((prev) => ({ ...prev, error }));
|
|
197
|
+
callbacks?.onError?.(error);
|
|
198
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
199
|
+
// eslint-disable-next-line no-console
|
|
200
|
+
console.log("[ImageToVideoFeature] Generate failed: Image is required");
|
|
201
|
+
}
|
|
202
|
+
return { success: false, error };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (paramImageUri) {
|
|
206
|
+
setState((prev) => ({ ...prev, imageUri: paramImageUri }));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (callbacks?.onAuthCheck && !callbacks.onAuthCheck()) {
|
|
210
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
211
|
+
// eslint-disable-next-line no-console
|
|
212
|
+
console.log("[ImageToVideoFeature] Generate failed: Authentication required");
|
|
213
|
+
}
|
|
214
|
+
return { success: false, error: "Authentication required" };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (callbacks?.onCreditCheck && config.creditCost) {
|
|
218
|
+
const hasCredits = await callbacks.onCreditCheck(config.creditCost);
|
|
219
|
+
if (!hasCredits) {
|
|
220
|
+
callbacks?.onShowPaywall?.(config.creditCost);
|
|
221
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
222
|
+
// eslint-disable-next-line no-console
|
|
223
|
+
console.log("[ImageToVideoFeature] Generate failed: Insufficient credits");
|
|
224
|
+
}
|
|
225
|
+
return { success: false, error: "Insufficient credits" };
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return executeGeneration(effectiveImageUri, effectiveMotionPrompt, options);
|
|
110
230
|
},
|
|
111
|
-
[state.imageUri, state.motionPrompt,
|
|
231
|
+
[state.imageUri, state.motionPrompt, callbacks, config.creditCost, executeGeneration],
|
|
112
232
|
);
|
|
113
233
|
|
|
114
234
|
const reset = useCallback(() => {
|
|
115
|
-
setState(
|
|
235
|
+
setState(INITIAL_STATE);
|
|
116
236
|
}, []);
|
|
117
237
|
|
|
118
|
-
const isReady =
|
|
238
|
+
const isReady = useMemo(
|
|
239
|
+
() => state.imageUri !== null && !state.isProcessing,
|
|
240
|
+
[state.imageUri, state.isProcessing],
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
const canGenerate = useMemo(
|
|
244
|
+
() => isReady && !state.error,
|
|
245
|
+
[isReady, state.error],
|
|
246
|
+
);
|
|
119
247
|
|
|
120
|
-
return { state, setImageUri, setMotionPrompt, generate, reset, isReady };
|
|
248
|
+
return { state, setImageUri, setMotionPrompt, generate, reset, isReady, canGenerate };
|
|
121
249
|
}
|