@umituz/react-native-ai-generation-content 1.61.63 → 1.61.64
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/core/index.ts +1 -1
- package/src/domain/entities/index.ts +1 -1
- package/src/domain/interfaces/ai-provider.interface.ts +1 -1
- package/src/domain/interfaces/index.ts +1 -1
- package/src/domains/background/domain/entities/index.ts +1 -0
- package/src/domains/background/domain/interfaces/index.ts +1 -0
- package/src/{domain → domains/background/domain}/interfaces/provider-job-manager.interface.ts +1 -1
- package/src/domains/background/domain/types/background-generation.types.ts +28 -0
- package/src/domains/background/infrastructure/executors/backgroundJobExecutor.ts +105 -0
- package/src/{infrastructure → domains/background/infrastructure}/services/job-poller-factory.ts +1 -1
- package/src/{infrastructure → domains/background/infrastructure}/services/job-poller.service.ts +1 -1
- package/src/{infrastructure → domains/background/infrastructure}/services/job-poller.types.ts +2 -2
- package/src/{infrastructure → domains/background/infrastructure}/utils/polling-interval.util.ts +1 -1
- package/src/{infrastructure → domains/background/infrastructure}/utils/status-checker.util.ts +1 -1
- package/src/domains/background/presentation/hooks/use-background-generation.ts +97 -0
- package/src/domains/creations/presentation/components/PendingJobsSection.tsx +1 -1
- package/src/domains/image-to-video/presentation/hooks/imageToVideoStrategy.ts +77 -0
- package/src/domains/image-to-video/presentation/hooks/useImageToVideoFeature.ts +102 -0
- package/src/domains/scenarios/presentation/containers/CategoryNavigationContainer.tsx +4 -80
- package/src/{features → domains}/text-to-image/infrastructure/services/text-to-image-executor.ts +2 -82
- package/src/domains/text-to-image/infrastructure/utils/imageResultExtractor.ts +58 -0
- package/src/domains/text-to-video/presentation/hooks/textToVideoStrategy.ts +75 -0
- package/src/domains/text-to-video/presentation/hooks/useTextToVideoFeature.ts +120 -0
- package/src/exports/features.ts +12 -12
- package/src/presentation/components/PendingJobCard.tsx +1 -1
- package/src/features/image-to-video/presentation/hooks/useImageToVideoFeature.ts +0 -186
- package/src/features/text-to-video/presentation/hooks/useTextToVideoFeature.ts +0 -186
- package/src/presentation/hooks/use-background-generation.ts +0 -185
- /package/src/{domain → domains/background/domain}/entities/job.types.ts +0 -0
- /package/src/{infrastructure → domains/background/infrastructure}/utils/result-validator.util.ts +0 -0
- /package/src/{presentation → domains/background/presentation}/hooks/use-pending-jobs.ts +0 -0
- /package/src/{features → domains}/image-to-video/README.md +0 -0
- /package/src/{features → domains}/image-to-video/domain/constants/animation.constants.ts +0 -0
- /package/src/{features → domains}/image-to-video/domain/constants/duration.constants.ts +0 -0
- /package/src/{features → domains}/image-to-video/domain/constants/form.constants.ts +0 -0
- /package/src/{features → domains}/image-to-video/domain/constants/index.ts +0 -0
- /package/src/{features → domains}/image-to-video/domain/constants/music.constants.ts +0 -0
- /package/src/{features → domains}/image-to-video/domain/index.ts +0 -0
- /package/src/{features → domains}/image-to-video/domain/types/animation.types.ts +0 -0
- /package/src/{features → domains}/image-to-video/domain/types/config.types.ts +0 -0
- /package/src/{features → domains}/image-to-video/domain/types/duration.types.ts +0 -0
- /package/src/{features → domains}/image-to-video/domain/types/form.types.ts +0 -0
- /package/src/{features → domains}/image-to-video/domain/types/image-to-video.types.ts +0 -0
- /package/src/{features → domains}/image-to-video/domain/types/index.ts +0 -0
- /package/src/{features → domains}/image-to-video/domain/types/music.types.ts +0 -0
- /package/src/{features → domains}/image-to-video/index.ts +0 -0
- /package/src/{features → domains}/image-to-video/infrastructure/index.ts +0 -0
- /package/src/{features → domains}/image-to-video/infrastructure/services/image-to-video-executor.ts +0 -0
- /package/src/{features → domains}/image-to-video/infrastructure/services/index.ts +0 -0
- /package/src/{features → domains}/image-to-video/presentation/components/AddMoreCard.tsx +0 -0
- /package/src/{features → domains}/image-to-video/presentation/components/AnimationStyleSelector.tsx +0 -0
- /package/src/{features → domains}/image-to-video/presentation/components/DurationSelector.tsx +0 -0
- /package/src/{features → domains}/image-to-video/presentation/components/EmptyGridState.tsx +0 -0
- /package/src/{features → domains}/image-to-video/presentation/components/GridImageItem.tsx +0 -0
- /package/src/{features → domains}/image-to-video/presentation/components/ImageSelectionGrid.styles.ts +0 -0
- /package/src/{features → domains}/image-to-video/presentation/components/ImageSelectionGrid.tsx +0 -0
- /package/src/{features → domains}/image-to-video/presentation/components/ImageSelectionGrid.types.ts +0 -0
- /package/src/{features → domains}/image-to-video/presentation/components/MusicMoodSelector.tsx +0 -0
- /package/src/{features → domains}/image-to-video/presentation/components/index.ts +0 -0
- /package/src/{features → domains}/image-to-video/presentation/hooks/image-to-video-feature.types.ts +0 -0
- /package/src/{features → domains}/image-to-video/presentation/hooks/index.ts +0 -0
- /package/src/{features → domains}/image-to-video/presentation/hooks/useFormState.ts +0 -0
- /package/src/{features → domains}/image-to-video/presentation/hooks/useGeneration.ts +0 -0
- /package/src/{features → domains}/image-to-video/presentation/hooks/useImageToVideoForm.ts +0 -0
- /package/src/{features → domains}/image-to-video/presentation/index.ts +0 -0
- /package/src/{features → domains}/image-to-video/presentation/screens/ImageToVideoWizardFlow.tsx +0 -0
- /package/src/{features → domains}/shared/index.ts +0 -0
- /package/src/{features → domains}/shared/presentation/components/AutoSkipPreview.tsx +0 -0
- /package/src/{features → domains}/shared/presentation/components/index.ts +0 -0
- /package/src/{features → domains}/shared/presentation/utils/index.ts +0 -0
- /package/src/{features → domains}/shared/presentation/utils/wizard-flow.utils.ts +0 -0
- /package/src/{features → domains}/text-to-image/README.md +0 -0
- /package/src/{features → domains}/text-to-image/domain/constants/index.ts +0 -0
- /package/src/{features → domains}/text-to-image/domain/constants/options.constants.ts +0 -0
- /package/src/{features → domains}/text-to-image/domain/constants/styles.constants.ts +0 -0
- /package/src/{features → domains}/text-to-image/domain/index.ts +0 -0
- /package/src/{features → domains}/text-to-image/domain/types/config.types.ts +0 -0
- /package/src/{features → domains}/text-to-image/domain/types/form.types.ts +0 -0
- /package/src/{features → domains}/text-to-image/domain/types/index.ts +0 -0
- /package/src/{features → domains}/text-to-image/domain/types/text-to-image.types.ts +0 -0
- /package/src/{features → domains}/text-to-image/index.ts +0 -0
- /package/src/{features → domains}/text-to-image/infrastructure/index.ts +0 -0
- /package/src/{features → domains}/text-to-image/infrastructure/services/index.ts +0 -0
- /package/src/{features → domains}/text-to-image/presentation/components/index.ts +0 -0
- /package/src/{features → domains}/text-to-image/presentation/hooks/index.ts +0 -0
- /package/src/{features → domains}/text-to-image/presentation/hooks/useFormState.ts +0 -0
- /package/src/{features → domains}/text-to-image/presentation/hooks/useGeneration.ts +0 -0
- /package/src/{features → domains}/text-to-image/presentation/hooks/useTextToImageForm.ts +0 -0
- /package/src/{features → domains}/text-to-image/presentation/index.ts +0 -0
- /package/src/{features → domains}/text-to-image/presentation/screens/TextToImageWizardFlow.tsx +0 -0
- /package/src/{features → domains}/text-to-image/presentation/screens/TextToImageWizardFlow.types.ts +0 -0
- /package/src/{features → domains}/text-to-video/README.md +0 -0
- /package/src/{features → domains}/text-to-video/domain/index.ts +0 -0
- /package/src/{features → domains}/text-to-video/domain/types/callback.types.ts +0 -0
- /package/src/{features → domains}/text-to-video/domain/types/component.types.ts +0 -0
- /package/src/{features → domains}/text-to-video/domain/types/config.types.ts +0 -0
- /package/src/{features → domains}/text-to-video/domain/types/index.ts +0 -0
- /package/src/{features → domains}/text-to-video/domain/types/request.types.ts +0 -0
- /package/src/{features → domains}/text-to-video/domain/types/state.types.ts +0 -0
- /package/src/{features → domains}/text-to-video/index.ts +0 -0
- /package/src/{features → domains}/text-to-video/infrastructure/index.ts +0 -0
- /package/src/{features → domains}/text-to-video/infrastructure/services/index.ts +0 -0
- /package/src/{features → domains}/text-to-video/infrastructure/services/text-to-video-executor.ts +0 -0
- /package/src/{features → domains}/text-to-video/presentation/components/FrameSelector.tsx +0 -0
- /package/src/{features → domains}/text-to-video/presentation/components/GenerationTabs.tsx +0 -0
- /package/src/{features → domains}/text-to-video/presentation/components/HeroSection.tsx +0 -0
- /package/src/{features → domains}/text-to-video/presentation/components/HintCarousel.tsx +0 -0
- /package/src/{features → domains}/text-to-video/presentation/components/OptionsPanel.tsx +0 -0
- /package/src/{features → domains}/text-to-video/presentation/components/index.ts +0 -0
- /package/src/{features → domains}/text-to-video/presentation/hooks/index.ts +0 -0
- /package/src/{features → domains}/text-to-video/presentation/hooks/useTextToVideoForm.ts +0 -0
- /package/src/{features → domains}/text-to-video/presentation/index.ts +0 -0
- /package/src/{features → domains}/text-to-video/presentation/screens/TextToVideoWizardFlow.tsx +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.61.
|
|
3
|
+
"version": "1.61.64",
|
|
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/core/index.ts
CHANGED
|
@@ -57,7 +57,7 @@ export type {
|
|
|
57
57
|
// Segregated provider sub-interfaces
|
|
58
58
|
export type { IAIProviderLifecycle } from "../domain/interfaces/provider-lifecycle.interface";
|
|
59
59
|
export type { IAIProviderCapabilities } from "../domain/interfaces/provider-capabilities.interface";
|
|
60
|
-
export type { IAIProviderJobManager } from "../domain/interfaces/provider-job-manager.interface";
|
|
60
|
+
export type { IAIProviderJobManager } from "../domains/background/domain/interfaces/provider-job-manager.interface";
|
|
61
61
|
export type { IAIProviderExecutor } from "../domain/interfaces/provider-executor.interface";
|
|
62
62
|
export type { IAIProviderImageFeatures } from "../domain/interfaces/provider-image-features.interface";
|
|
63
63
|
export type { IAIProviderVideoFeatures } from "../domain/interfaces/provider-video-features.interface";
|
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
export * from "./error.types";
|
|
7
7
|
export * from "./generation.types";
|
|
8
8
|
export * from "./polling.types";
|
|
9
|
-
export * from "
|
|
9
|
+
export * from "../../domains/background/domain/entities/job.types";
|
|
10
10
|
export * from "./processing-modes.types";
|
|
11
11
|
export * from "./flow-config.types";
|
|
@@ -155,7 +155,7 @@ export interface VideoFeatureInputData {
|
|
|
155
155
|
|
|
156
156
|
import type { IAIProviderLifecycle } from "./provider-lifecycle.interface";
|
|
157
157
|
import type { IAIProviderCapabilities } from "./provider-capabilities.interface";
|
|
158
|
-
import type { IAIProviderJobManager } from "
|
|
158
|
+
import type { IAIProviderJobManager } from "../../domains/background/domain/interfaces/provider-job-manager.interface";
|
|
159
159
|
import type { IAIProviderExecutor } from "./provider-executor.interface";
|
|
160
160
|
import type { IAIProviderImageFeatures } from "./provider-image-features.interface";
|
|
161
161
|
import type { IAIProviderVideoFeatures } from "./provider-video-features.interface";
|
|
@@ -9,7 +9,7 @@ export * from "./app-services.interface";
|
|
|
9
9
|
// Interface Segregation - Split provider interfaces
|
|
10
10
|
export type { IAIProviderLifecycle } from "./provider-lifecycle.interface";
|
|
11
11
|
export type { IAIProviderCapabilities } from "./provider-capabilities.interface";
|
|
12
|
-
export type { IAIProviderJobManager } from "
|
|
12
|
+
export type { IAIProviderJobManager } from "../../domains/background/domain/interfaces/provider-job-manager.interface";
|
|
13
13
|
export type { IAIProviderExecutor } from "./provider-executor.interface";
|
|
14
14
|
export type { IAIProviderImageFeatures } from "./provider-image-features.interface";
|
|
15
15
|
export type { IAIProviderVideoFeatures } from "./provider-video-features.interface";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./job.types";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./provider-job-manager.interface";
|
package/src/{domain → domains/background/domain}/interfaces/provider-job-manager.interface.ts
RENAMED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Single Responsibility: Async job submission, status checking, and result retrieval
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { JobSubmission, JobStatus } from "
|
|
6
|
+
import type { JobSubmission, JobStatus } from "../../../../domain/interfaces/ai-provider.interface";
|
|
7
7
|
|
|
8
8
|
export interface IAIProviderJobManager {
|
|
9
9
|
/**
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BackgroundJob,
|
|
3
|
+
BackgroundQueueConfig,
|
|
4
|
+
JobExecutorConfig,
|
|
5
|
+
} from "../entities/job.types";
|
|
6
|
+
import type { executeDirectGeneration } from "../../infrastructure/executors/backgroundJobExecutor";
|
|
7
|
+
|
|
8
|
+
export type { DirectExecutionResult } from "../../infrastructure/executors/backgroundJobExecutor";
|
|
9
|
+
|
|
10
|
+
export interface UseBackgroundGenerationOptions<TInput, TResult>
|
|
11
|
+
extends Partial<BackgroundQueueConfig> {
|
|
12
|
+
readonly executor: JobExecutorConfig<TInput, TResult>;
|
|
13
|
+
readonly onJobComplete?: (job: BackgroundJob<TInput, TResult>) => void;
|
|
14
|
+
readonly onJobError?: (job: BackgroundJob<TInput, TResult>) => void;
|
|
15
|
+
readonly onAllComplete?: () => void;
|
|
16
|
+
readonly onProgress?: (progress: number) => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface UseBackgroundGenerationReturn<TInput, TResult> {
|
|
20
|
+
readonly startJob: (input: TInput, type: string) => Promise<string>;
|
|
21
|
+
readonly executeDirectly: (input: TInput) => ReturnType<typeof executeDirectGeneration<TInput, TResult>>;
|
|
22
|
+
readonly cancelJob: (id: string) => void;
|
|
23
|
+
readonly pendingJobs: BackgroundJob<TInput, TResult>[];
|
|
24
|
+
readonly activeJobCount: number;
|
|
25
|
+
readonly hasActiveJobs: boolean;
|
|
26
|
+
readonly isProcessing: boolean;
|
|
27
|
+
readonly progress: number;
|
|
28
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { BackgroundJob, JobExecutorConfig } from "../../domain/entities/job.types";
|
|
2
|
+
|
|
3
|
+
export interface DirectExecutionResult<TResult> {
|
|
4
|
+
readonly success: boolean;
|
|
5
|
+
readonly result?: TResult;
|
|
6
|
+
readonly error?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface DirectExecutionParams<TInput, TResult> {
|
|
10
|
+
input: TInput;
|
|
11
|
+
executor: JobExecutorConfig<TInput, TResult>;
|
|
12
|
+
onProgress?: (progress: number) => void;
|
|
13
|
+
setProgress: (progress: number) => void;
|
|
14
|
+
setIsProcessing: (isProcessing: boolean) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const executeDirectGeneration = async <TInput, TResult>(
|
|
18
|
+
params: DirectExecutionParams<TInput, TResult>,
|
|
19
|
+
): Promise<DirectExecutionResult<TResult>> => {
|
|
20
|
+
const { input, executor, onProgress, setProgress, setIsProcessing } = params;
|
|
21
|
+
|
|
22
|
+
setIsProcessing(true);
|
|
23
|
+
setProgress(0);
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const result = await executor.execute(input, (p) => {
|
|
27
|
+
setProgress(p);
|
|
28
|
+
onProgress?.(p);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
setProgress(100);
|
|
32
|
+
return { success: true, result };
|
|
33
|
+
} catch (error) {
|
|
34
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
35
|
+
return { success: false, error: errorMsg };
|
|
36
|
+
} finally {
|
|
37
|
+
setIsProcessing(false);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
interface QueuedExecutionParams<TInput, TResult> {
|
|
42
|
+
jobId: string;
|
|
43
|
+
input: TInput;
|
|
44
|
+
executor: JobExecutorConfig<TInput, TResult>;
|
|
45
|
+
updateJob: (params: { id: string; updates: Partial<BackgroundJob<TInput, TResult>> }) => void;
|
|
46
|
+
removeJob: (id: string) => void;
|
|
47
|
+
getJob: (id: string) => BackgroundJob<TInput, TResult> | undefined;
|
|
48
|
+
activeJobsRef: React.MutableRefObject<Set<string>>;
|
|
49
|
+
onJobComplete?: (job: BackgroundJob<TInput, TResult>) => void;
|
|
50
|
+
onJobError?: (job: BackgroundJob<TInput, TResult>) => void;
|
|
51
|
+
onAllComplete?: () => void;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const executeQueuedJob = async <TInput, TResult>(
|
|
55
|
+
params: QueuedExecutionParams<TInput, TResult>,
|
|
56
|
+
): Promise<void> => {
|
|
57
|
+
const {
|
|
58
|
+
jobId,
|
|
59
|
+
input,
|
|
60
|
+
executor,
|
|
61
|
+
updateJob,
|
|
62
|
+
removeJob,
|
|
63
|
+
getJob,
|
|
64
|
+
activeJobsRef,
|
|
65
|
+
onJobComplete,
|
|
66
|
+
onJobError,
|
|
67
|
+
onAllComplete,
|
|
68
|
+
} = params;
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
updateJob({ id: jobId, updates: { status: "processing", progress: 10 } });
|
|
72
|
+
|
|
73
|
+
const result = await executor.execute(input, (p) => {
|
|
74
|
+
updateJob({ id: jobId, updates: { progress: p } });
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
updateJob({
|
|
78
|
+
id: jobId,
|
|
79
|
+
updates: { status: "completed", progress: 100, result, completedAt: new Date() },
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const completedJob = getJob(jobId);
|
|
83
|
+
if (completedJob) {
|
|
84
|
+
await executor.onComplete?.(completedJob);
|
|
85
|
+
onJobComplete?.(completedJob);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
removeJob(jobId);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
91
|
+
|
|
92
|
+
updateJob({ id: jobId, updates: { status: "failed", error: errorMsg, progress: 0 } });
|
|
93
|
+
|
|
94
|
+
const failedJob = getJob(jobId);
|
|
95
|
+
if (failedJob) {
|
|
96
|
+
await executor.onError?.(failedJob, error instanceof Error ? error : new Error(errorMsg));
|
|
97
|
+
onJobError?.(failedJob);
|
|
98
|
+
}
|
|
99
|
+
} finally {
|
|
100
|
+
activeJobsRef.current.delete(jobId);
|
|
101
|
+
if (activeJobsRef.current.size === 0) {
|
|
102
|
+
onAllComplete?.();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
package/src/{infrastructure → domains/background/infrastructure}/services/job-poller-factory.ts
RENAMED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Creates pre-configured job poller instances
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { PollingConfig } from "
|
|
6
|
+
import type { PollingConfig } from "../../../../domain/entities/polling.types";
|
|
7
7
|
import type { PollJobOptions } from "./job-poller.types";
|
|
8
8
|
import { pollJob } from "./job-poller.service";
|
|
9
9
|
|
package/src/{infrastructure → domains/background/infrastructure}/services/job-poller.service.ts
RENAMED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Reports only real status - no fake progress
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { DEFAULT_POLLING_CONFIG } from "
|
|
7
|
+
import { DEFAULT_POLLING_CONFIG } from "../../../../domain/entities/polling.types";
|
|
8
8
|
import { calculatePollingInterval } from "../utils/polling-interval.util";
|
|
9
9
|
import { checkStatusForErrors, isJobComplete } from "../utils/status-checker.util";
|
|
10
10
|
import { validateResult } from "../utils/result-validator.util";
|
package/src/{infrastructure → domains/background/infrastructure}/services/job-poller.types.ts
RENAMED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Job Poller Type Definitions
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type { IAIProvider, JobStatus } from "
|
|
6
|
-
import type { PollingConfig } from "
|
|
5
|
+
import type { IAIProvider, JobStatus } from "../../../../domain/interfaces/ai-provider.interface";
|
|
6
|
+
import type { PollingConfig } from "../../../../domain/entities/polling.types";
|
|
7
7
|
|
|
8
8
|
export interface PollJobOptions {
|
|
9
9
|
provider: IAIProvider;
|
package/src/{infrastructure → domains/background/infrastructure}/utils/status-checker.util.ts
RENAMED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Checks job status responses for errors
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { JobStatus, AILogEntry } from "
|
|
6
|
+
import type { JobStatus, AILogEntry } from "../../../../domain/interfaces/ai-provider.interface";
|
|
7
7
|
|
|
8
8
|
export interface StatusCheckResult {
|
|
9
9
|
status: string;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { useCallback, useRef, useState } from "react";
|
|
2
|
+
import { usePendingJobs } from "./use-pending-jobs";
|
|
3
|
+
import { executeDirectGeneration, executeQueuedJob } from "../../infrastructure/executors/backgroundJobExecutor";
|
|
4
|
+
import { DEFAULT_QUEUE_CONFIG } from "../../domain/entities/job.types";
|
|
5
|
+
import type {
|
|
6
|
+
UseBackgroundGenerationOptions,
|
|
7
|
+
UseBackgroundGenerationReturn,
|
|
8
|
+
} from "../../domain/types/background-generation.types";
|
|
9
|
+
|
|
10
|
+
export type { DirectExecutionResult, UseBackgroundGenerationOptions, UseBackgroundGenerationReturn } from "../../domain/types/background-generation.types";
|
|
11
|
+
|
|
12
|
+
export function useBackgroundGeneration<TInput = unknown, TResult = unknown>(
|
|
13
|
+
options: UseBackgroundGenerationOptions<TInput, TResult>,
|
|
14
|
+
): UseBackgroundGenerationReturn<TInput, TResult> {
|
|
15
|
+
const config = { ...DEFAULT_QUEUE_CONFIG, ...options };
|
|
16
|
+
const activeJobsRef = useRef<Set<string>>(new Set());
|
|
17
|
+
const jobInputsRef = useRef<Map<string, { input: TInput; type: string }>>(
|
|
18
|
+
new Map(),
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const [isProcessing, setIsProcessing] = useState(false);
|
|
22
|
+
const [progress, setProgress] = useState(0);
|
|
23
|
+
|
|
24
|
+
const { jobs, addJobAsync, updateJob, removeJob, getJob } = usePendingJobs<
|
|
25
|
+
TInput,
|
|
26
|
+
TResult
|
|
27
|
+
>({
|
|
28
|
+
queryKey: config.queryKey,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const { executor, onProgress, onJobComplete, onJobError, onAllComplete } = options;
|
|
32
|
+
|
|
33
|
+
const executeDirectly = useCallback(
|
|
34
|
+
(input: TInput) =>
|
|
35
|
+
executeDirectGeneration({ input, executor, onProgress, setProgress, setIsProcessing }),
|
|
36
|
+
[executor, onProgress],
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const executeJob = useCallback(
|
|
40
|
+
(jobId: string, input: TInput) =>
|
|
41
|
+
executeQueuedJob({
|
|
42
|
+
jobId,
|
|
43
|
+
input,
|
|
44
|
+
executor,
|
|
45
|
+
updateJob,
|
|
46
|
+
removeJob,
|
|
47
|
+
getJob,
|
|
48
|
+
activeJobsRef,
|
|
49
|
+
onJobComplete,
|
|
50
|
+
onJobError,
|
|
51
|
+
onAllComplete,
|
|
52
|
+
}),
|
|
53
|
+
[executor, onJobComplete, onJobError, onAllComplete, updateJob, removeJob, getJob],
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const startJob = useCallback(
|
|
57
|
+
async (input: TInput, type: string): Promise<string> => {
|
|
58
|
+
const jobId = `job-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
59
|
+
|
|
60
|
+
jobInputsRef.current.set(jobId, { input, type });
|
|
61
|
+
|
|
62
|
+
await addJobAsync({
|
|
63
|
+
id: jobId,
|
|
64
|
+
input,
|
|
65
|
+
type,
|
|
66
|
+
status: "queued",
|
|
67
|
+
progress: 0,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
activeJobsRef.current.add(jobId);
|
|
71
|
+
void executeJob(jobId, input);
|
|
72
|
+
|
|
73
|
+
return jobId;
|
|
74
|
+
},
|
|
75
|
+
[addJobAsync, executeJob],
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const cancelJob = useCallback(
|
|
79
|
+
(id: string) => {
|
|
80
|
+
activeJobsRef.current.delete(id);
|
|
81
|
+
jobInputsRef.current.delete(id);
|
|
82
|
+
removeJob(id);
|
|
83
|
+
},
|
|
84
|
+
[removeJob],
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
startJob,
|
|
89
|
+
executeDirectly,
|
|
90
|
+
cancelJob,
|
|
91
|
+
pendingJobs: jobs,
|
|
92
|
+
activeJobCount: activeJobsRef.current.size,
|
|
93
|
+
hasActiveJobs: activeJobsRef.current.size > 0,
|
|
94
|
+
isProcessing,
|
|
95
|
+
progress,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import React from "react";
|
|
7
7
|
import { View, StyleSheet } from "react-native";
|
|
8
8
|
import { AtomicText, useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
9
|
-
import type { BackgroundJob } from "
|
|
9
|
+
import type { BackgroundJob } from "../../../background/domain/entities/job.types";
|
|
10
10
|
import { PendingJobCard } from "../../../../presentation/components/PendingJobCard";
|
|
11
11
|
|
|
12
12
|
export interface PendingJobsSectionProps {
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { executeImageToVideo } from "../../infrastructure/services";
|
|
2
|
+
import type { GenerationStrategy } from "../../../../presentation/hooks/generation";
|
|
3
|
+
import type {
|
|
4
|
+
ImageToVideoConfig,
|
|
5
|
+
ImageToVideoCallbacks,
|
|
6
|
+
ImageToVideoResult,
|
|
7
|
+
ImageToVideoOptions,
|
|
8
|
+
ImageToVideoInputBuilder,
|
|
9
|
+
ImageToVideoResultExtractor,
|
|
10
|
+
} from "../../domain/types";
|
|
11
|
+
|
|
12
|
+
interface VideoGenerationInput {
|
|
13
|
+
imageUrl: string;
|
|
14
|
+
prompt: string;
|
|
15
|
+
options?: ImageToVideoOptions;
|
|
16
|
+
creationId: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface CreateStrategyParams {
|
|
20
|
+
config: ImageToVideoConfig;
|
|
21
|
+
callbacks: ImageToVideoCallbacks;
|
|
22
|
+
buildInput: ImageToVideoInputBuilder;
|
|
23
|
+
extractResult?: ImageToVideoResultExtractor;
|
|
24
|
+
userId: string;
|
|
25
|
+
currentPrompt: string;
|
|
26
|
+
creationIdRef: React.MutableRefObject<string>;
|
|
27
|
+
updateState: (videoUrl: string | null, thumbnailUrl: string | null) => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const createImageToVideoStrategy = (
|
|
31
|
+
params: CreateStrategyParams,
|
|
32
|
+
): GenerationStrategy<VideoGenerationInput, ImageToVideoResult> => {
|
|
33
|
+
const { config, callbacks, buildInput, extractResult, userId, currentPrompt, creationIdRef, updateState } = params;
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
execute: async (input) => {
|
|
37
|
+
creationIdRef.current = input.creationId;
|
|
38
|
+
|
|
39
|
+
callbacks.onGenerationStart?.({
|
|
40
|
+
creationId: input.creationId,
|
|
41
|
+
type: "image-to-video",
|
|
42
|
+
imageUrl: input.imageUrl,
|
|
43
|
+
prompt: input.prompt,
|
|
44
|
+
metadata: input.options as Record<string, unknown> | undefined,
|
|
45
|
+
}).catch(() => {});
|
|
46
|
+
|
|
47
|
+
const result = await executeImageToVideo(
|
|
48
|
+
{ imageUrl: input.imageUrl, prompt: input.prompt, userId, options: input.options },
|
|
49
|
+
{ model: config.model, buildInput, extractResult },
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
if (!result.success || !result.videoUrl) {
|
|
53
|
+
throw new Error(result.error || "Generation failed");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
updateState(result.videoUrl ?? null, result.thumbnailUrl ?? null);
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
success: true,
|
|
60
|
+
videoUrl: result.videoUrl,
|
|
61
|
+
thumbnailUrl: result.thumbnailUrl,
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
getCreditCost: () => config.creditCost,
|
|
65
|
+
save: async (result) => {
|
|
66
|
+
if (result.success && result.videoUrl && creationIdRef.current) {
|
|
67
|
+
await callbacks.onCreationSave?.({
|
|
68
|
+
creationId: creationIdRef.current,
|
|
69
|
+
type: "image-to-video",
|
|
70
|
+
videoUrl: result.videoUrl,
|
|
71
|
+
thumbnailUrl: result.thumbnailUrl,
|
|
72
|
+
prompt: currentPrompt,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { useState, useCallback, useMemo, useRef } from "react";
|
|
2
|
+
import { useGenerationOrchestrator } from "../../../../presentation/hooks/generation";
|
|
3
|
+
import { createImageToVideoStrategy } from "./imageToVideoStrategy";
|
|
4
|
+
import type {
|
|
5
|
+
UseImageToVideoFeatureProps,
|
|
6
|
+
UseImageToVideoFeatureReturn,
|
|
7
|
+
INITIAL_STATE,
|
|
8
|
+
DEFAULT_ALERT_MESSAGES,
|
|
9
|
+
} from "./image-to-video-feature.types";
|
|
10
|
+
|
|
11
|
+
export type {
|
|
12
|
+
UseImageToVideoFeatureProps,
|
|
13
|
+
UseImageToVideoFeatureReturn,
|
|
14
|
+
} from "./image-to-video-feature.types";
|
|
15
|
+
|
|
16
|
+
export function useImageToVideoFeature(props: UseImageToVideoFeatureProps): UseImageToVideoFeatureReturn {
|
|
17
|
+
const { config, callbacks, userId } = props;
|
|
18
|
+
const [state, setState] = useState(INITIAL_STATE);
|
|
19
|
+
const creationIdRef = useRef("");
|
|
20
|
+
|
|
21
|
+
const updateState = useCallback((videoUrl: string | null, thumbnailUrl: string | null) => {
|
|
22
|
+
setState((prev) => ({ ...prev, videoUrl, thumbnailUrl }));
|
|
23
|
+
}, []);
|
|
24
|
+
|
|
25
|
+
const strategy = useMemo(
|
|
26
|
+
() =>
|
|
27
|
+
createImageToVideoStrategy({
|
|
28
|
+
config,
|
|
29
|
+
callbacks,
|
|
30
|
+
buildInput: config.buildInput,
|
|
31
|
+
extractResult: config.extractResult,
|
|
32
|
+
userId,
|
|
33
|
+
currentPrompt: state.motionPrompt || "",
|
|
34
|
+
creationIdRef,
|
|
35
|
+
updateState,
|
|
36
|
+
}),
|
|
37
|
+
[config, callbacks, userId, state.motionPrompt, updateState],
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const orchestrator = useGenerationOrchestrator(strategy, {
|
|
41
|
+
userId,
|
|
42
|
+
alertMessages: DEFAULT_ALERT_MESSAGES,
|
|
43
|
+
onCreditsExhausted: () => callbacks.onShowPaywall?.(config.creditCost ?? 0),
|
|
44
|
+
onSuccess: (result) => {
|
|
45
|
+
config.onProcessingComplete?.();
|
|
46
|
+
callbacks.onGenerate?.(result);
|
|
47
|
+
},
|
|
48
|
+
onError: (err) => {
|
|
49
|
+
config.onProcessingError?.(err.message);
|
|
50
|
+
callbacks.onError?.(err.message);
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const setImageUri = useCallback((imageUri: string) => {
|
|
55
|
+
setState((prev) => ({ ...prev, imageUri, error: null }));
|
|
56
|
+
}, []);
|
|
57
|
+
|
|
58
|
+
const setMotionPrompt = useCallback((motionPrompt: string) => {
|
|
59
|
+
setState((prev) => ({ ...prev, motionPrompt, error: null }));
|
|
60
|
+
}, []);
|
|
61
|
+
|
|
62
|
+
const generate = useCallback(
|
|
63
|
+
async (params?: any) => {
|
|
64
|
+
const imageUri = params?.imageUri || state.imageUri;
|
|
65
|
+
if (!imageUri) {
|
|
66
|
+
const error = "Image is required";
|
|
67
|
+
setState((prev) => ({ ...prev, error }));
|
|
68
|
+
return { success: false, error };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
setState((prev) => ({ ...prev, isProcessing: true, error: null }));
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
const result = await orchestrator.generate({
|
|
75
|
+
imageUrl: imageUri,
|
|
76
|
+
prompt: state.motionPrompt || "",
|
|
77
|
+
options: params,
|
|
78
|
+
creationId: `image-to-video-${Date.now()}`,
|
|
79
|
+
});
|
|
80
|
+
setState((prev) => ({ ...prev, isProcessing: false }));
|
|
81
|
+
return result;
|
|
82
|
+
} catch (error) {
|
|
83
|
+
const message = error instanceof Error ? error.message : "Generation failed";
|
|
84
|
+
setState((prev) => ({ ...prev, isProcessing: false, error: message }));
|
|
85
|
+
return { success: false, error: message };
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
[state.imageUri, state.motionPrompt, orchestrator],
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const reset = useCallback(() => setState(INITIAL_STATE), []);
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
state,
|
|
95
|
+
setImageUri,
|
|
96
|
+
setMotionPrompt,
|
|
97
|
+
generate,
|
|
98
|
+
reset,
|
|
99
|
+
isReady: !orchestrator.isGenerating && !state.isProcessing,
|
|
100
|
+
canGenerate: !orchestrator.isGenerating && !state.isProcessing && !!state.imageUri,
|
|
101
|
+
};
|
|
102
|
+
}
|