@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-generation-content",
3
- "version": "1.17.138",
3
+ "version": "1.17.140",
4
4
  "description": "Provider-agnostic AI generation orchestration for React Native",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -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] Error:", message);
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 for image-to-video generation
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
- ImageToVideoOptions,
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: (options?: ImageToVideoOptions) => Promise<ImageToVideoResult>;
28
+ generate: (params?: ImageToVideoGenerateParams) => Promise<ImageToVideoResult>;
25
29
  reset: () => void;
26
30
  isReady: boolean;
31
+ canGenerate: boolean;
27
32
  }
28
33
 
29
- const initialState: ImageToVideoFeatureState = {
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>(initialState);
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 generate = useCallback(
58
- async (options?: ImageToVideoOptions): Promise<ImageToVideoResult> => {
59
- if (!state.imageUri) {
60
- const error = "Image is required";
61
- setState((prev) => ({ ...prev, error }));
62
- return { success: false, error };
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
- const imageBase64 = await config.prepareImage(state.imageUri);
75
-
76
- const result = await executeImageToVideo(
77
- {
78
- imageUri: state.imageUri,
79
- imageBase64,
80
- userId,
81
- motionPrompt: state.motionPrompt || undefined,
82
- options,
83
- },
84
- {
85
- model: config.model,
86
- buildInput: config.buildInput,
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
- if (result.success && result.videoUrl) {
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
- progress: 100,
169
+ error: errorMessage,
101
170
  }));
102
- } else {
103
- const error = result.error || "Generation failed";
104
- setState((prev) => ({ ...prev, isProcessing: false, error }));
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
- config.onProcessingComplete?.(result);
109
- return result;
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, userId, config],
231
+ [state.imageUri, state.motionPrompt, callbacks, config.creditCost, executeGeneration],
112
232
  );
113
233
 
114
234
  const reset = useCallback(() => {
115
- setState(initialState);
235
+ setState(INITIAL_STATE);
116
236
  }, []);
117
237
 
118
- const isReady = state.imageUri !== null && !state.isProcessing;
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
  }