@umituz/react-native-ai-gemini-provider 1.10.7 → 1.12.0
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/index.ts +5 -0
- package/src/domains/upscaling/domain/index.ts +1 -0
- package/src/domains/upscaling/domain/types/index.ts +9 -0
- package/src/domains/upscaling/domain/types/upscale.types.ts +47 -0
- package/src/domains/upscaling/index.ts +21 -0
- package/src/domains/upscaling/infrastructure/index.ts +3 -0
- package/src/domains/upscaling/infrastructure/providers/gemini-upscale.provider.ts +109 -0
- package/src/domains/upscaling/infrastructure/providers/index.ts +6 -0
- package/src/domains/upscaling/infrastructure/services/index.ts +1 -0
- package/src/domains/upscaling/infrastructure/services/upscale.service.ts +77 -0
- package/src/domains/upscaling/infrastructure/utils/image-preparer.util.ts +55 -0
- package/src/domains/upscaling/infrastructure/utils/index.ts +1 -0
- package/src/index.ts +14 -0
- package/src/infrastructure/utils/image-preparer.util.ts +65 -0
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./types";
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upscale Types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type UpscaleScaleFactor = 2 | 4 | 8;
|
|
6
|
+
|
|
7
|
+
export interface UpscaleOptions {
|
|
8
|
+
scaleFactor?: UpscaleScaleFactor;
|
|
9
|
+
enhanceFaces?: boolean;
|
|
10
|
+
enhanceDetails?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface UpscaleInput {
|
|
14
|
+
base64: string;
|
|
15
|
+
mimeType: string;
|
|
16
|
+
options?: UpscaleOptions;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface UpscaleResult {
|
|
20
|
+
success: boolean;
|
|
21
|
+
imageUrl?: string;
|
|
22
|
+
imageBase64?: string;
|
|
23
|
+
mimeType?: string;
|
|
24
|
+
error?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface PreparedImage {
|
|
28
|
+
base64: string;
|
|
29
|
+
mimeType: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface UseUpscaleConfig {
|
|
33
|
+
defaultScaleFactor?: UpscaleScaleFactor;
|
|
34
|
+
onSuccess?: (result: UpscaleResult) => void;
|
|
35
|
+
onError?: (error: string) => void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface UseUpscaleReturn {
|
|
39
|
+
imageUri: string | null;
|
|
40
|
+
processedUrl: string | null;
|
|
41
|
+
isProcessing: boolean;
|
|
42
|
+
progress: number;
|
|
43
|
+
error: string | null;
|
|
44
|
+
setImageUri: (uri: string) => void;
|
|
45
|
+
process: () => Promise<void>;
|
|
46
|
+
reset: () => void;
|
|
47
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upscaling Domain - Public API
|
|
3
|
+
*
|
|
4
|
+
* Exports GeminiUpscaleProvider for use with content package registry
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Provider (implements IUpscaleProvider)
|
|
8
|
+
export {
|
|
9
|
+
geminiUpscaleProvider,
|
|
10
|
+
type IUpscaleProvider,
|
|
11
|
+
type UpscaleRequest,
|
|
12
|
+
type UpscaleResult,
|
|
13
|
+
} from "./infrastructure/providers";
|
|
14
|
+
|
|
15
|
+
// Internal types (for advanced usage)
|
|
16
|
+
export type {
|
|
17
|
+
UpscaleScaleFactor,
|
|
18
|
+
UpscaleOptions,
|
|
19
|
+
UpscaleInput,
|
|
20
|
+
PreparedImage,
|
|
21
|
+
} from "./domain/types";
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini Upscale Provider
|
|
3
|
+
* Implements IUpscaleProvider for content package integration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { upscaleService } from "../services/upscale.service";
|
|
7
|
+
import { prepareImage } from "../utils/image-preparer.util";
|
|
8
|
+
|
|
9
|
+
declare const __DEV__: boolean;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Provider interface matching content package's IUpscaleProvider
|
|
13
|
+
* Re-defined here to avoid cross-package dependency
|
|
14
|
+
*/
|
|
15
|
+
export interface UpscaleRequest {
|
|
16
|
+
imageUri: string;
|
|
17
|
+
userId: string;
|
|
18
|
+
options?: {
|
|
19
|
+
scaleFactor?: 2 | 4 | 8;
|
|
20
|
+
enhanceFaces?: boolean;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface UpscaleResult {
|
|
25
|
+
success: boolean;
|
|
26
|
+
imageUrl?: string;
|
|
27
|
+
imageBase64?: string;
|
|
28
|
+
error?: string;
|
|
29
|
+
requestId?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface IUpscaleProvider {
|
|
33
|
+
readonly providerId: string;
|
|
34
|
+
readonly providerName: string;
|
|
35
|
+
isAvailable(): boolean;
|
|
36
|
+
upscale(
|
|
37
|
+
request: UpscaleRequest,
|
|
38
|
+
onProgress?: (progress: number) => void
|
|
39
|
+
): Promise<UpscaleResult>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
class GeminiUpscaleProvider implements IUpscaleProvider {
|
|
43
|
+
readonly providerId = "gemini";
|
|
44
|
+
readonly providerName = "Google Gemini";
|
|
45
|
+
|
|
46
|
+
isAvailable(): boolean {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async upscale(
|
|
51
|
+
request: UpscaleRequest,
|
|
52
|
+
onProgress?: (progress: number) => void
|
|
53
|
+
): Promise<UpscaleResult> {
|
|
54
|
+
if (__DEV__) {
|
|
55
|
+
// eslint-disable-next-line no-console
|
|
56
|
+
console.log("[GeminiUpscaleProvider] Starting upscale");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
onProgress?.(10);
|
|
61
|
+
|
|
62
|
+
const prepared = await prepareImage(request.imageUri);
|
|
63
|
+
onProgress?.(30);
|
|
64
|
+
|
|
65
|
+
const result = await upscaleService.upscale({
|
|
66
|
+
base64: prepared.base64,
|
|
67
|
+
mimeType: prepared.mimeType,
|
|
68
|
+
options: {
|
|
69
|
+
scaleFactor: request.options?.scaleFactor || 2,
|
|
70
|
+
enhanceFaces: request.options?.enhanceFaces,
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
onProgress?.(90);
|
|
75
|
+
|
|
76
|
+
if (!result.success) {
|
|
77
|
+
return { success: false, error: result.error };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const imageUrl = result.imageBase64
|
|
81
|
+
? `data:${result.mimeType || "image/png"};base64,${result.imageBase64}`
|
|
82
|
+
: result.imageUrl;
|
|
83
|
+
|
|
84
|
+
onProgress?.(100);
|
|
85
|
+
|
|
86
|
+
if (__DEV__) {
|
|
87
|
+
// eslint-disable-next-line no-console
|
|
88
|
+
console.log("[GeminiUpscaleProvider] Completed");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
success: true,
|
|
93
|
+
imageUrl,
|
|
94
|
+
imageBase64: result.imageBase64,
|
|
95
|
+
};
|
|
96
|
+
} catch (error) {
|
|
97
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
98
|
+
|
|
99
|
+
if (__DEV__) {
|
|
100
|
+
// eslint-disable-next-line no-console
|
|
101
|
+
console.error("[GeminiUpscaleProvider] Error:", message);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return { success: false, error: message };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export const geminiUpscaleProvider = new GeminiUpscaleProvider();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { upscaleService } from "./upscale.service";
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upscale Service
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { geminiImageEditService } from "../../../../infrastructure/services/gemini-image-edit.service";
|
|
6
|
+
import type {
|
|
7
|
+
UpscaleInput,
|
|
8
|
+
UpscaleResult,
|
|
9
|
+
UpscaleOptions,
|
|
10
|
+
} from "../../domain/types";
|
|
11
|
+
|
|
12
|
+
declare const __DEV__: boolean;
|
|
13
|
+
|
|
14
|
+
function buildPrompt(options?: UpscaleOptions): string {
|
|
15
|
+
const scale = options?.scaleFactor || 2;
|
|
16
|
+
const parts = [
|
|
17
|
+
`Upscale this image by ${scale}x while maintaining quality.`,
|
|
18
|
+
"Preserve all original details and colors.",
|
|
19
|
+
"Enhance sharpness and clarity.",
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
if (options?.enhanceFaces) {
|
|
23
|
+
parts.push("Pay special attention to enhancing facial features.");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (options?.enhanceDetails) {
|
|
27
|
+
parts.push("Enhance fine details and textures.");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return parts.join(" ");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
class UpscaleService {
|
|
34
|
+
async upscale(input: UpscaleInput): Promise<UpscaleResult> {
|
|
35
|
+
if (__DEV__) {
|
|
36
|
+
// eslint-disable-next-line no-console
|
|
37
|
+
console.log("[UpscaleService] Starting", {
|
|
38
|
+
scaleFactor: input.options?.scaleFactor || 2,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const prompt = buildPrompt(input.options);
|
|
44
|
+
|
|
45
|
+
const result = await geminiImageEditService.editImage(prompt, [
|
|
46
|
+
{ base64: input.base64, mimeType: input.mimeType },
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
if (!result.imageBase64 && !result.imageUrl) {
|
|
50
|
+
return { success: false, error: "No image returned" };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (__DEV__) {
|
|
54
|
+
// eslint-disable-next-line no-console
|
|
55
|
+
console.log("[UpscaleService] Completed successfully");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
success: true,
|
|
60
|
+
imageUrl: result.imageUrl,
|
|
61
|
+
imageBase64: result.imageBase64,
|
|
62
|
+
mimeType: result.mimeType,
|
|
63
|
+
};
|
|
64
|
+
} catch (error) {
|
|
65
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
66
|
+
|
|
67
|
+
if (__DEV__) {
|
|
68
|
+
// eslint-disable-next-line no-console
|
|
69
|
+
console.error("[UpscaleService] Error:", message);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return { success: false, error: message };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const upscaleService = new UpscaleService();
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image Preparer Utility
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { PreparedImage } from "../../domain/types";
|
|
6
|
+
|
|
7
|
+
declare const __DEV__: boolean;
|
|
8
|
+
|
|
9
|
+
function getMimeType(uri: string): string {
|
|
10
|
+
const ext = uri.split(".").pop()?.toLowerCase();
|
|
11
|
+
const types: Record<string, string> = {
|
|
12
|
+
jpg: "image/jpeg",
|
|
13
|
+
jpeg: "image/jpeg",
|
|
14
|
+
png: "image/png",
|
|
15
|
+
gif: "image/gif",
|
|
16
|
+
webp: "image/webp",
|
|
17
|
+
};
|
|
18
|
+
return types[ext || ""] || "image/jpeg";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function extractBase64(dataUrl: string): string {
|
|
22
|
+
const match = dataUrl.match(/^data:[^;]+;base64,(.+)$/);
|
|
23
|
+
return match ? match[1] : dataUrl;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function prepareImage(uri: string): Promise<PreparedImage> {
|
|
27
|
+
if (__DEV__) {
|
|
28
|
+
// eslint-disable-next-line no-console
|
|
29
|
+
console.log("[ImagePreparer] Preparing image");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (uri.startsWith("data:")) {
|
|
33
|
+
const mimeMatch = uri.match(/^data:([^;]+);base64,/);
|
|
34
|
+
return {
|
|
35
|
+
base64: extractBase64(uri),
|
|
36
|
+
mimeType: mimeMatch ? mimeMatch[1] : "image/jpeg",
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const response = await fetch(uri);
|
|
41
|
+
const blob = await response.blob();
|
|
42
|
+
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
const reader = new FileReader();
|
|
45
|
+
reader.onloadend = () => {
|
|
46
|
+
const dataUrl = reader.result as string;
|
|
47
|
+
resolve({
|
|
48
|
+
base64: extractBase64(dataUrl),
|
|
49
|
+
mimeType: blob.type || getMimeType(uri),
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
reader.onerror = () => reject(new Error("Failed to read image"));
|
|
53
|
+
reader.readAsDataURL(blob);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { prepareImage } from "./image-preparer.util";
|
package/src/index.ts
CHANGED
|
@@ -109,6 +109,20 @@ export type {
|
|
|
109
109
|
UseGeminiReturn,
|
|
110
110
|
} from "./presentation/hooks";
|
|
111
111
|
|
|
112
|
+
// =============================================================================
|
|
113
|
+
// DOMAINS - DDD-organized features
|
|
114
|
+
// =============================================================================
|
|
115
|
+
|
|
116
|
+
// Upscaling Domain - Provider for content package registry
|
|
117
|
+
export { geminiUpscaleProvider } from "./domains/upscaling";
|
|
118
|
+
|
|
119
|
+
export type {
|
|
120
|
+
IUpscaleProvider,
|
|
121
|
+
UpscaleRequest as GeminiUpscaleRequest,
|
|
122
|
+
UpscaleResult as GeminiUpscaleResult,
|
|
123
|
+
PreparedImage,
|
|
124
|
+
} from "./domains/upscaling";
|
|
125
|
+
|
|
112
126
|
// =============================================================================
|
|
113
127
|
// PROVIDER CONFIGURATION - Tier-based Setup
|
|
114
128
|
// =============================================================================
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image Preparer Utility
|
|
3
|
+
* Prepares images for Gemini API
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { PreparedImage } from "../../domains/upscaling/domain/types";
|
|
7
|
+
|
|
8
|
+
declare const __DEV__: boolean;
|
|
9
|
+
|
|
10
|
+
function getMimeTypeFromUri(uri: string): string {
|
|
11
|
+
const extension = uri.split(".").pop()?.toLowerCase();
|
|
12
|
+
const mimeTypes: Record<string, string> = {
|
|
13
|
+
jpg: "image/jpeg",
|
|
14
|
+
jpeg: "image/jpeg",
|
|
15
|
+
png: "image/png",
|
|
16
|
+
gif: "image/gif",
|
|
17
|
+
webp: "image/webp",
|
|
18
|
+
};
|
|
19
|
+
return mimeTypes[extension || ""] || "image/jpeg";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function extractBase64FromDataUrl(dataUrl: string): string {
|
|
23
|
+
const match = dataUrl.match(/^data:[^;]+;base64,(.+)$/);
|
|
24
|
+
return match ? match[1] : dataUrl;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function prepareImageFromUri(
|
|
28
|
+
uri: string,
|
|
29
|
+
): Promise<PreparedImage> {
|
|
30
|
+
if (__DEV__) {
|
|
31
|
+
// eslint-disable-next-line no-console
|
|
32
|
+
console.log("[ImagePreparer] Preparing image from URI");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (uri.startsWith("data:")) {
|
|
36
|
+
const mimeMatch = uri.match(/^data:([^;]+);base64,/);
|
|
37
|
+
const mimeType = mimeMatch ? mimeMatch[1] : "image/jpeg";
|
|
38
|
+
const base64 = extractBase64FromDataUrl(uri);
|
|
39
|
+
return { base64, mimeType };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const response = await fetch(uri);
|
|
43
|
+
const blob = await response.blob();
|
|
44
|
+
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
const reader = new FileReader();
|
|
47
|
+
reader.onloadend = () => {
|
|
48
|
+
const dataUrl = reader.result as string;
|
|
49
|
+
const base64 = extractBase64FromDataUrl(dataUrl);
|
|
50
|
+
const mimeType = blob.type || getMimeTypeFromUri(uri);
|
|
51
|
+
resolve({ base64, mimeType });
|
|
52
|
+
};
|
|
53
|
+
reader.onerror = () => reject(new Error("Failed to read image"));
|
|
54
|
+
reader.readAsDataURL(blob);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function isValidBase64(str: string): boolean {
|
|
59
|
+
if (!str || str.length === 0) return false;
|
|
60
|
+
try {
|
|
61
|
+
return btoa(atob(str)) === str;
|
|
62
|
+
} catch {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|