1030 lines
29 KiB
TypeScript
1030 lines
29 KiB
TypeScript
import { useState, useCallback } from 'react';
|
|
import { aiApiClient } from '../api/client';
|
|
|
|
export interface ImageGenerationRequest {
|
|
prompt: string;
|
|
template_id?: string | null;
|
|
provider?: string;
|
|
model?: string | null;
|
|
width?: number | null;
|
|
height?: number | null;
|
|
aspect_ratio?: string | null;
|
|
style_preset?: string | null;
|
|
quality?: 'draft' | 'standard' | 'premium';
|
|
negative_prompt?: string;
|
|
guidance_scale?: number | null;
|
|
steps?: number | null;
|
|
seed?: number | null;
|
|
num_variations?: number;
|
|
enhance_prompt?: boolean;
|
|
use_persona?: boolean;
|
|
persona_id?: string | null;
|
|
}
|
|
|
|
export interface ImageResult {
|
|
image_base64: string;
|
|
width: number;
|
|
height: number;
|
|
provider: string;
|
|
model: string;
|
|
seed?: number;
|
|
variation: number;
|
|
metadata?: any;
|
|
}
|
|
|
|
export interface GenerationResponse {
|
|
success: boolean;
|
|
request: any;
|
|
results: ImageResult[];
|
|
total_generated: number;
|
|
total_failed: number;
|
|
}
|
|
|
|
export interface Template {
|
|
id: string;
|
|
name: string;
|
|
category: string;
|
|
platform?: string;
|
|
aspect_ratio: {
|
|
ratio: string;
|
|
width: number;
|
|
height: number;
|
|
label: string;
|
|
};
|
|
description: string;
|
|
recommended_provider: string;
|
|
style_preset: string;
|
|
quality: string;
|
|
use_cases: string[];
|
|
}
|
|
|
|
export interface Provider {
|
|
name: string;
|
|
models: string[];
|
|
capabilities: string[];
|
|
max_resolution: number[];
|
|
cost_range: string;
|
|
}
|
|
|
|
export interface CostEstimate {
|
|
provider: string;
|
|
model?: string;
|
|
operation: string;
|
|
num_images: number;
|
|
resolution?: string;
|
|
cost_per_image: number;
|
|
total_cost: number;
|
|
currency: string;
|
|
estimated: boolean;
|
|
}
|
|
|
|
export interface CostEstimateRequest {
|
|
provider: string;
|
|
model?: string;
|
|
operation: string;
|
|
num_images: number;
|
|
width?: number;
|
|
height?: number;
|
|
}
|
|
|
|
export interface EditOperationMeta {
|
|
label: string;
|
|
description: string;
|
|
provider: string;
|
|
async?: boolean;
|
|
fields?: {
|
|
prompt?: boolean;
|
|
mask?: boolean;
|
|
negative_prompt?: boolean;
|
|
search_prompt?: boolean;
|
|
select_prompt?: boolean;
|
|
background?: boolean;
|
|
lighting?: boolean;
|
|
expansion?: boolean;
|
|
};
|
|
}
|
|
|
|
export interface EditingModel {
|
|
id: string;
|
|
name: string;
|
|
description: string;
|
|
cost: number;
|
|
cost_8k?: number;
|
|
tier: 'budget' | 'mid' | 'premium';
|
|
max_resolution: [number, number];
|
|
capabilities: string[];
|
|
use_cases: string[];
|
|
features: string[];
|
|
supports_multi_image: boolean;
|
|
supports_controlnet: boolean;
|
|
languages: string[];
|
|
api_params?: {
|
|
uses_size?: boolean;
|
|
uses_aspect_ratio?: boolean;
|
|
uses_resolution?: boolean;
|
|
supports_guidance_scale?: boolean;
|
|
supports_seed?: boolean;
|
|
};
|
|
}
|
|
|
|
export interface ModelRecommendation {
|
|
recommended_model: string;
|
|
reason: string;
|
|
alternatives: Array<{
|
|
model_id: string;
|
|
name: string;
|
|
cost: number;
|
|
reason: string;
|
|
}>;
|
|
}
|
|
|
|
export interface FaceSwapModel {
|
|
id: string;
|
|
name: string;
|
|
description: string;
|
|
cost: number;
|
|
tier: 'budget' | 'mid' | 'premium';
|
|
capabilities: string[];
|
|
use_cases: string[];
|
|
features: string[];
|
|
max_faces: number;
|
|
}
|
|
|
|
export interface FaceSwapModelRecommendation {
|
|
recommended_model: string;
|
|
reason: string;
|
|
alternatives: Array<{
|
|
model_id: string;
|
|
name: string;
|
|
cost: number;
|
|
reason: string;
|
|
}>;
|
|
}
|
|
|
|
export interface FaceSwapRequestPayload {
|
|
base_image_base64: string;
|
|
face_image_base64: string;
|
|
model?: string;
|
|
target_face_index?: number;
|
|
target_gender?: string;
|
|
options?: Record<string, any>;
|
|
}
|
|
|
|
export interface FaceSwapResult {
|
|
success: boolean;
|
|
image_base64: string;
|
|
width: number;
|
|
height: number;
|
|
provider: string;
|
|
model: string;
|
|
metadata: Record<string, any>;
|
|
}
|
|
|
|
// Compression types
|
|
export interface CompressionRequest {
|
|
image_base64: string;
|
|
quality: number;
|
|
format: string;
|
|
target_size_kb?: number;
|
|
strip_metadata: boolean;
|
|
progressive: boolean;
|
|
optimize: boolean;
|
|
}
|
|
|
|
export interface CompressionResult {
|
|
success: boolean;
|
|
image_base64: string;
|
|
original_size_kb: number;
|
|
compressed_size_kb: number;
|
|
compression_ratio: number;
|
|
format: string;
|
|
width: number;
|
|
height: number;
|
|
quality_used: number;
|
|
metadata_stripped: boolean;
|
|
}
|
|
|
|
export interface CompressionFormat {
|
|
id: string;
|
|
name: string;
|
|
extension: string;
|
|
description: string;
|
|
supports_transparency: boolean;
|
|
quality_range: [number, number];
|
|
recommended_quality: number;
|
|
use_cases: string[];
|
|
}
|
|
|
|
export interface CompressionPreset {
|
|
id: string;
|
|
name: string;
|
|
description: string;
|
|
format: string;
|
|
quality: number;
|
|
target_size_kb?: number;
|
|
strip_metadata: boolean;
|
|
}
|
|
|
|
export interface CompressionEstimate {
|
|
original_size_kb: number;
|
|
estimated_size_kb: number;
|
|
estimated_reduction_percent: number;
|
|
width: number;
|
|
height: number;
|
|
format: string;
|
|
}
|
|
|
|
// Format Converter types
|
|
export interface FormatConversionRequest {
|
|
image_base64: string;
|
|
target_format: string;
|
|
preserve_transparency: boolean;
|
|
quality?: number;
|
|
color_space?: string;
|
|
strip_metadata: boolean;
|
|
optimize: boolean;
|
|
progressive: boolean;
|
|
}
|
|
|
|
export interface FormatConversionResult {
|
|
success: boolean;
|
|
image_base64: string;
|
|
original_format: string;
|
|
target_format: string;
|
|
original_size_kb: number;
|
|
converted_size_kb: number;
|
|
width: number;
|
|
height: number;
|
|
transparency_preserved: boolean;
|
|
metadata_preserved: boolean;
|
|
color_space?: string;
|
|
}
|
|
|
|
export interface SupportedFormat {
|
|
id: string;
|
|
name: string;
|
|
description: string;
|
|
supports_transparency: boolean;
|
|
supports_lossy: boolean;
|
|
mime_type: string;
|
|
}
|
|
|
|
export interface FormatRecommendation {
|
|
format: string;
|
|
reason: string;
|
|
}
|
|
|
|
export interface EditImageRequestPayload {
|
|
image_base64: string;
|
|
operation: string;
|
|
prompt?: string;
|
|
negative_prompt?: string;
|
|
mask_base64?: string;
|
|
search_prompt?: string;
|
|
select_prompt?: string;
|
|
background_image_base64?: string;
|
|
lighting_image_base64?: string;
|
|
expand_left?: number;
|
|
expand_right?: number;
|
|
expand_up?: number;
|
|
expand_down?: number;
|
|
provider?: string;
|
|
model?: string;
|
|
style_preset?: string;
|
|
guidance_scale?: number;
|
|
steps?: number;
|
|
seed?: number;
|
|
output_format?: string;
|
|
options?: Record<string, any>;
|
|
}
|
|
|
|
export interface EditResult {
|
|
success: boolean;
|
|
operation: string;
|
|
provider: string;
|
|
image_base64: string;
|
|
width: number;
|
|
height: number;
|
|
metadata: Record<string, any>;
|
|
}
|
|
|
|
export interface UpscaleRequestPayload {
|
|
image_base64: string;
|
|
mode?: 'fast' | 'conservative' | 'creative' | 'auto';
|
|
target_width?: number;
|
|
target_height?: number;
|
|
preset?: string;
|
|
prompt?: string;
|
|
}
|
|
|
|
export interface UpscaleResult {
|
|
success: boolean;
|
|
mode: string;
|
|
image_base64: string;
|
|
width: number;
|
|
height: number;
|
|
metadata: Record<string, any>;
|
|
}
|
|
|
|
export interface ControlOperationMeta {
|
|
label: string;
|
|
description: string;
|
|
provider: string;
|
|
fields?: {
|
|
control_image?: boolean;
|
|
style_image?: boolean;
|
|
control_strength?: boolean;
|
|
fidelity?: boolean;
|
|
style_strength?: boolean;
|
|
aspect_ratio?: boolean;
|
|
};
|
|
}
|
|
|
|
export interface ControlImageRequestPayload {
|
|
control_image_base64: string;
|
|
operation: 'sketch' | 'structure' | 'style' | 'style_transfer';
|
|
prompt: string;
|
|
style_image_base64?: string;
|
|
negative_prompt?: string;
|
|
control_strength?: number;
|
|
fidelity?: number;
|
|
style_strength?: number;
|
|
composition_fidelity?: number;
|
|
change_strength?: number;
|
|
aspect_ratio?: string;
|
|
style_preset?: string;
|
|
seed?: number;
|
|
output_format?: string;
|
|
}
|
|
|
|
export interface ControlResult {
|
|
success: boolean;
|
|
operation: string;
|
|
provider: string;
|
|
image_base64: string;
|
|
width: number;
|
|
height: number;
|
|
metadata: Record<string, any>;
|
|
}
|
|
|
|
export interface SocialOptimizeResult {
|
|
success: boolean;
|
|
results: Array<{
|
|
platform: string;
|
|
format: string;
|
|
width: number;
|
|
height: number;
|
|
ratio: string;
|
|
image_base64: string;
|
|
safe_zone: {
|
|
top: number;
|
|
bottom: number;
|
|
left: number;
|
|
right: number;
|
|
};
|
|
}>;
|
|
total_optimized: number;
|
|
}
|
|
|
|
export interface PlatformFormat {
|
|
name: string;
|
|
width: number;
|
|
height: number;
|
|
ratio: string;
|
|
safe_zone: {
|
|
top: number;
|
|
bottom: number;
|
|
left: number;
|
|
right: number;
|
|
};
|
|
file_type: string;
|
|
max_size_mb: number;
|
|
}
|
|
|
|
export const useImageStudio = () => {
|
|
const [templates, setTemplates] = useState<Template[]>([]);
|
|
const [providers, setProviders] = useState<Record<string, Provider> | null>(null);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [isGenerating, setIsGenerating] = useState(false);
|
|
const [results, setResults] = useState<ImageResult[]>([]);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [costEstimate, setCostEstimate] = useState<CostEstimate | null>(null);
|
|
const [editOperations, setEditOperations] = useState<Record<string, EditOperationMeta>>({});
|
|
const [isLoadingEditOps, setIsLoadingEditOps] = useState(false);
|
|
const [isProcessingEdit, setIsProcessingEdit] = useState(false);
|
|
const [editResult, setEditResult] = useState<EditResult | null>(null);
|
|
const [editError, setEditError] = useState<string | null>(null);
|
|
const [upscaleResult, setUpscaleResult] = useState<UpscaleResult | null>(null);
|
|
const [isUpscaling, setIsUpscaling] = useState(false);
|
|
const [upscaleError, setUpscaleError] = useState<string | null>(null);
|
|
const [controlOperations, setControlOperations] = useState<Record<string, ControlOperationMeta>>({});
|
|
const [isLoadingControlOps, setIsLoadingControlOps] = useState(false);
|
|
const [isProcessingControl, setIsProcessingControl] = useState(false);
|
|
const [controlResult, setControlResult] = useState<ControlResult | null>(null);
|
|
const [controlError, setControlError] = useState<string | null>(null);
|
|
const [isOptimizing, setIsOptimizing] = useState(false);
|
|
const [optimizeResult, setOptimizeResult] = useState<SocialOptimizeResult | null>(null);
|
|
const [optimizeError, setOptimizeError] = useState<string | null>(null);
|
|
|
|
// Load templates
|
|
const loadTemplates = useCallback(async (platform?: string, category?: string) => {
|
|
setIsLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
const params = new URLSearchParams();
|
|
if (platform) params.append('platform', platform);
|
|
if (category) params.append('category', category);
|
|
|
|
const response = await aiApiClient.get(
|
|
`/api/image-studio/templates?${params.toString()}`
|
|
);
|
|
|
|
setTemplates(response.data.templates || []);
|
|
} catch (err: any) {
|
|
console.error('Failed to load templates:', err);
|
|
setError(err.response?.data?.detail || 'Failed to load templates');
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
}, []);
|
|
|
|
// Search templates
|
|
const searchTemplates = useCallback(async (query: string) => {
|
|
setIsLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
const response = await aiApiClient.get(
|
|
`/api/image-studio/templates/search?query=${encodeURIComponent(query)}`
|
|
);
|
|
|
|
setTemplates(response.data.templates || []);
|
|
} catch (err: any) {
|
|
console.error('Failed to search templates:', err);
|
|
setError(err.response?.data?.detail || 'Failed to search templates');
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
}, []);
|
|
|
|
// Load providers
|
|
const loadProviders = useCallback(async () => {
|
|
setIsLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
const response = await aiApiClient.get('/api/image-studio/providers');
|
|
setProviders(response.data.providers || {});
|
|
} catch (err: any) {
|
|
console.error('Failed to load providers:', err);
|
|
setError(err.response?.data?.detail || 'Failed to load providers');
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
}, []);
|
|
|
|
// Generate image
|
|
const generateImage = useCallback(async (request: ImageGenerationRequest): Promise<GenerationResponse | null> => {
|
|
setIsGenerating(true);
|
|
setError(null);
|
|
|
|
try {
|
|
const response = await aiApiClient.post('/api/image-studio/create', request);
|
|
|
|
if (response.data.success) {
|
|
setResults(response.data.results || []);
|
|
return response.data;
|
|
} else {
|
|
throw new Error('Generation failed');
|
|
}
|
|
} catch (err: any) {
|
|
console.error('Failed to generate image:', err);
|
|
const errorMessage = err.response?.data?.detail || 'Failed to generate image';
|
|
setError(errorMessage);
|
|
throw new Error(errorMessage);
|
|
} finally {
|
|
setIsGenerating(false);
|
|
}
|
|
}, []);
|
|
|
|
// Estimate cost
|
|
const estimateCost = useCallback(async (request: CostEstimateRequest) => {
|
|
try {
|
|
const response = await aiApiClient.post('/api/image-studio/estimate-cost', request);
|
|
setCostEstimate(response.data);
|
|
return response.data;
|
|
} catch (err: any) {
|
|
console.error('Failed to estimate cost:', err);
|
|
// Don't set error for cost estimation failures
|
|
return null;
|
|
}
|
|
}, []);
|
|
|
|
// Get platform specs
|
|
const getPlatformSpecs = useCallback(async (platform: string) => {
|
|
try {
|
|
const response = await aiApiClient.get(`/api/image-studio/platform-specs/${platform}`);
|
|
return response.data;
|
|
} catch (err: any) {
|
|
console.error('Failed to get platform specs:', err);
|
|
return null;
|
|
}
|
|
}, []);
|
|
|
|
// Clear results
|
|
const clearResults = useCallback(() => {
|
|
setResults([]);
|
|
setError(null);
|
|
}, []);
|
|
|
|
// Clear error
|
|
const clearError = useCallback(() => {
|
|
setError(null);
|
|
}, []);
|
|
|
|
// Load edit operations metadata
|
|
const loadEditOperations = useCallback(async () => {
|
|
setIsLoadingEditOps(true);
|
|
setEditError(null);
|
|
try {
|
|
const response = await aiApiClient.get('/api/image-studio/edit/operations');
|
|
setEditOperations(response.data.operations || {});
|
|
} catch (err: any) {
|
|
console.error('Failed to load edit operations:', err);
|
|
setEditError(err.response?.data?.detail || 'Failed to load edit operations');
|
|
} finally {
|
|
setIsLoadingEditOps(false);
|
|
}
|
|
}, []);
|
|
|
|
// Process edit request
|
|
const processEdit = useCallback(async (payload: EditImageRequestPayload) => {
|
|
setIsProcessingEdit(true);
|
|
setEditError(null);
|
|
try {
|
|
const response = await aiApiClient.post('/api/image-studio/edit/process', payload);
|
|
setEditResult(response.data);
|
|
return response.data as EditResult;
|
|
} catch (err: any) {
|
|
console.error('Failed to process edit:', err);
|
|
const message = err.response?.data?.detail || 'Failed to process edit';
|
|
setEditError(message);
|
|
throw new Error(message);
|
|
} finally {
|
|
setIsProcessingEdit(false);
|
|
}
|
|
}, []);
|
|
|
|
const clearEditResult = useCallback(() => {
|
|
setEditResult(null);
|
|
setEditError(null);
|
|
}, []);
|
|
|
|
// Edit model selection state
|
|
const [editModels, setEditModels] = useState<EditingModel[]>([]);
|
|
const [isLoadingEditModels, setIsLoadingEditModels] = useState(false);
|
|
const [modelRecommendation, setModelRecommendation] = useState<ModelRecommendation | null>(null);
|
|
const [isLoadingRecommendation, setIsLoadingRecommendation] = useState(false);
|
|
|
|
// Face swap state
|
|
const [faceSwapModels, setFaceSwapModels] = useState<FaceSwapModel[]>([]);
|
|
const [isLoadingFaceSwapModels, setIsLoadingFaceSwapModels] = useState(false);
|
|
const [faceSwapModelRecommendation, setFaceSwapModelRecommendation] = useState<FaceSwapModelRecommendation | null>(null);
|
|
const [isLoadingFaceSwapRecommendation, setIsLoadingFaceSwapRecommendation] = useState(false);
|
|
const [isProcessingFaceSwap, setIsProcessingFaceSwap] = useState(false);
|
|
const [faceSwapResult, setFaceSwapResult] = useState<FaceSwapResult | null>(null);
|
|
const [faceSwapError, setFaceSwapError] = useState<string | null>(null);
|
|
|
|
// Compression state
|
|
const [compressionFormats, setCompressionFormats] = useState<CompressionFormat[]>([]);
|
|
const [compressionPresets, setCompressionPresets] = useState<CompressionPreset[]>([]);
|
|
const [isCompressing, setIsCompressing] = useState(false);
|
|
const [compressionResult, setCompressionResult] = useState<CompressionResult | null>(null);
|
|
const [compressionError, setCompressionError] = useState<string | null>(null);
|
|
const [compressionEstimate, setCompressionEstimate] = useState<CompressionEstimate | null>(null);
|
|
|
|
// Format Converter state
|
|
const [supportedFormats, setSupportedFormats] = useState<SupportedFormat[]>([]);
|
|
const [isConvertingFormat, setIsConvertingFormat] = useState(false);
|
|
const [formatConversionResult, setFormatConversionResult] = useState<FormatConversionResult | null>(null);
|
|
const [formatConversionError, setFormatConversionError] = useState<string | null>(null);
|
|
const [formatRecommendations, setFormatRecommendations] = useState<FormatRecommendation[]>([]);
|
|
|
|
// Load available editing models
|
|
const loadEditModels = useCallback(async (operation?: string, tier?: string) => {
|
|
setIsLoadingEditModels(true);
|
|
try {
|
|
const params = new URLSearchParams();
|
|
if (operation) params.append('operation', operation);
|
|
if (tier) params.append('tier', tier);
|
|
|
|
const response = await aiApiClient.get(`/api/image-studio/edit/models?${params.toString()}`);
|
|
setEditModels(response.data.models || []);
|
|
return response.data.models || [];
|
|
} catch (err: any) {
|
|
console.error('Failed to load edit models:', err);
|
|
return [];
|
|
} finally {
|
|
setIsLoadingEditModels(false);
|
|
}
|
|
}, []);
|
|
|
|
// Get model recommendation
|
|
const getModelRecommendation = useCallback(async (
|
|
operation: string,
|
|
imageResolution?: { width: number; height: number },
|
|
userTier?: string,
|
|
preferences?: { prioritize_cost?: boolean; prioritize_quality?: boolean }
|
|
) => {
|
|
setIsLoadingRecommendation(true);
|
|
try {
|
|
const response = await aiApiClient.post('/api/image-studio/edit/recommend', {
|
|
operation,
|
|
image_resolution: imageResolution,
|
|
user_tier: userTier,
|
|
preferences,
|
|
});
|
|
setModelRecommendation(response.data);
|
|
return response.data;
|
|
} catch (err: any) {
|
|
console.error('Failed to get model recommendation:', err);
|
|
return null;
|
|
} finally {
|
|
setIsLoadingRecommendation(false);
|
|
}
|
|
}, []);
|
|
|
|
// Load face swap models
|
|
const loadFaceSwapModels = useCallback(async (tier?: string) => {
|
|
setIsLoadingFaceSwapModels(true);
|
|
try {
|
|
const params = new URLSearchParams();
|
|
if (tier) params.append('tier', tier);
|
|
|
|
const response = await aiApiClient.get(`/api/image-studio/face-swap/models?${params.toString()}`);
|
|
setFaceSwapModels(response.data.models || []);
|
|
return response.data.models || [];
|
|
} catch (err: any) {
|
|
console.error('Failed to load face swap models:', err);
|
|
return [];
|
|
} finally {
|
|
setIsLoadingFaceSwapModels(false);
|
|
}
|
|
}, []);
|
|
|
|
// Get face swap model recommendation
|
|
const getFaceSwapModelRecommendation = useCallback(async (
|
|
baseImageResolution?: { width: number; height: number },
|
|
faceImageResolution?: { width: number; height: number },
|
|
userTier?: string,
|
|
preferences?: { prioritize_cost?: boolean; prioritize_quality?: boolean }
|
|
) => {
|
|
setIsLoadingFaceSwapRecommendation(true);
|
|
try {
|
|
const response = await aiApiClient.post('/api/image-studio/face-swap/recommend', {
|
|
base_image_resolution: baseImageResolution,
|
|
face_image_resolution: faceImageResolution,
|
|
user_tier: userTier,
|
|
preferences,
|
|
});
|
|
setFaceSwapModelRecommendation(response.data);
|
|
return response.data;
|
|
} catch (err: any) {
|
|
console.error('Failed to get face swap model recommendation:', err);
|
|
return null;
|
|
} finally {
|
|
setIsLoadingFaceSwapRecommendation(false);
|
|
}
|
|
}, []);
|
|
|
|
// Process face swap
|
|
const processFaceSwap = useCallback(async (payload: FaceSwapRequestPayload) => {
|
|
setIsProcessingFaceSwap(true);
|
|
setFaceSwapError(null);
|
|
try {
|
|
const response = await aiApiClient.post('/api/image-studio/face-swap/process', payload);
|
|
setFaceSwapResult(response.data);
|
|
return response.data as FaceSwapResult;
|
|
} catch (err: any) {
|
|
console.error('Failed to process face swap:', err);
|
|
const message = err.response?.data?.detail || 'Failed to process face swap';
|
|
setFaceSwapError(message);
|
|
throw new Error(message);
|
|
} finally {
|
|
setIsProcessingFaceSwap(false);
|
|
}
|
|
}, []);
|
|
|
|
const clearFaceSwapResult = useCallback(() => {
|
|
setFaceSwapResult(null);
|
|
setFaceSwapError(null);
|
|
}, []);
|
|
|
|
// Process upscale
|
|
const processUpscale = useCallback(async (payload: UpscaleRequestPayload) => {
|
|
setIsUpscaling(true);
|
|
setUpscaleError(null);
|
|
try {
|
|
const response = await aiApiClient.post('/api/image-studio/upscale', payload);
|
|
setUpscaleResult(response.data);
|
|
return response.data as UpscaleResult;
|
|
} catch (err: any) {
|
|
console.error('Failed to upscale image:', err);
|
|
const message = err.response?.data?.detail || 'Failed to upscale image';
|
|
setUpscaleError(message);
|
|
throw new Error(message);
|
|
} finally {
|
|
setIsUpscaling(false);
|
|
}
|
|
}, []);
|
|
|
|
const clearUpscaleResult = useCallback(() => {
|
|
setUpscaleResult(null);
|
|
setUpscaleError(null);
|
|
}, []);
|
|
|
|
// Load control operations
|
|
const loadControlOperations = useCallback(async () => {
|
|
setIsLoadingControlOps(true);
|
|
try {
|
|
const response = await aiApiClient.get('/api/image-studio/control/operations');
|
|
setControlOperations(response.data.operations || {});
|
|
} catch (err: any) {
|
|
console.error('Failed to load control operations:', err);
|
|
} finally {
|
|
setIsLoadingControlOps(false);
|
|
}
|
|
}, []);
|
|
|
|
// Process control
|
|
const processControl = useCallback(async (payload: ControlImageRequestPayload) => {
|
|
setIsProcessingControl(true);
|
|
setControlError(null);
|
|
try {
|
|
const response = await aiApiClient.post('/api/image-studio/control/process', payload);
|
|
setControlResult(response.data);
|
|
return response.data as ControlResult;
|
|
} catch (err: any) {
|
|
console.error('Failed to process control:', err);
|
|
const message = err.response?.data?.detail || 'Failed to process control';
|
|
setControlError(message);
|
|
throw new Error(message);
|
|
} finally {
|
|
setIsProcessingControl(false);
|
|
}
|
|
}, []);
|
|
|
|
const clearControlResult = useCallback(() => {
|
|
setControlResult(null);
|
|
setControlError(null);
|
|
}, []);
|
|
|
|
// Social Optimizer
|
|
const optimizeForSocial = useCallback(async (payload: {
|
|
image_base64: string;
|
|
platforms: string[];
|
|
format_names?: Record<string, string>;
|
|
show_safe_zones?: boolean;
|
|
crop_mode?: string;
|
|
focal_point?: { x: number; y: number };
|
|
output_format?: string;
|
|
}) => {
|
|
setIsOptimizing(true);
|
|
setOptimizeError(null);
|
|
try {
|
|
const response = await aiApiClient.post('/api/image-studio/social/optimize', payload);
|
|
setOptimizeResult(response.data);
|
|
return response.data as SocialOptimizeResult;
|
|
} catch (err: any) {
|
|
console.error('Failed to optimize for social:', err);
|
|
const message = err.response?.data?.detail || 'Failed to optimize for social platforms';
|
|
setOptimizeError(message);
|
|
throw new Error(message);
|
|
} finally {
|
|
setIsOptimizing(false);
|
|
}
|
|
}, []);
|
|
|
|
const getPlatformFormats = useCallback(async (platform: string): Promise<PlatformFormat[]> => {
|
|
try {
|
|
const response = await aiApiClient.get(`/api/image-studio/social/platforms/${platform}/formats`);
|
|
return response.data.formats || [];
|
|
} catch (err: any) {
|
|
console.error(`Failed to load formats for ${platform}:`, err);
|
|
return [];
|
|
}
|
|
}, []);
|
|
|
|
const clearOptimizeResult = useCallback(() => {
|
|
setOptimizeResult(null);
|
|
setOptimizeError(null);
|
|
}, []);
|
|
|
|
// Load compression formats
|
|
const loadCompressionFormats = useCallback(async () => {
|
|
try {
|
|
const response = await aiApiClient.get('/api/image-studio/compress/formats');
|
|
setCompressionFormats(response.data.formats || []);
|
|
return response.data.formats || [];
|
|
} catch (err: any) {
|
|
console.error('Failed to load compression formats:', err);
|
|
return [];
|
|
}
|
|
}, []);
|
|
|
|
// Load compression presets
|
|
const loadCompressionPresets = useCallback(async () => {
|
|
try {
|
|
const response = await aiApiClient.get('/api/image-studio/compress/presets');
|
|
setCompressionPresets(response.data.presets || []);
|
|
return response.data.presets || [];
|
|
} catch (err: any) {
|
|
console.error('Failed to load compression presets:', err);
|
|
return [];
|
|
}
|
|
}, []);
|
|
|
|
// Estimate compression
|
|
const estimateCompression = useCallback(async (
|
|
image_base64: string,
|
|
format: string = 'jpeg',
|
|
quality: number = 85,
|
|
): Promise<CompressionEstimate | null> => {
|
|
try {
|
|
const response = await aiApiClient.post('/api/image-studio/compress/estimate', {
|
|
image_base64,
|
|
format,
|
|
quality,
|
|
});
|
|
setCompressionEstimate(response.data);
|
|
return response.data;
|
|
} catch (err: any) {
|
|
console.error('Failed to estimate compression:', err);
|
|
return null;
|
|
}
|
|
}, []);
|
|
|
|
// Process compression
|
|
const processCompression = useCallback(async (request: CompressionRequest): Promise<CompressionResult> => {
|
|
setIsCompressing(true);
|
|
setCompressionError(null);
|
|
try {
|
|
const response = await aiApiClient.post('/api/image-studio/compress', request);
|
|
setCompressionResult(response.data);
|
|
return response.data;
|
|
} catch (err: any) {
|
|
console.error('Failed to compress image:', err);
|
|
const message = err.response?.data?.detail || 'Failed to compress image';
|
|
setCompressionError(message);
|
|
throw new Error(message);
|
|
} finally {
|
|
setIsCompressing(false);
|
|
}
|
|
}, []);
|
|
|
|
const clearCompressionResult = useCallback(() => {
|
|
setCompressionResult(null);
|
|
setCompressionError(null);
|
|
setCompressionEstimate(null);
|
|
}, []);
|
|
|
|
// Load supported formats
|
|
const loadSupportedFormats = useCallback(async () => {
|
|
try {
|
|
const response = await aiApiClient.get('/api/image-studio/convert-format/supported');
|
|
setSupportedFormats(response.data.formats || []);
|
|
return response.data.formats || [];
|
|
} catch (err: any) {
|
|
console.error('Failed to load supported formats:', err);
|
|
return [];
|
|
}
|
|
}, []);
|
|
|
|
// Get format recommendations
|
|
const getFormatRecommendations = useCallback(async (sourceFormat: string): Promise<FormatRecommendation[]> => {
|
|
try {
|
|
const response = await aiApiClient.get(`/api/image-studio/convert-format/recommendations?source_format=${sourceFormat}`);
|
|
setFormatRecommendations(response.data.recommendations || []);
|
|
return response.data.recommendations || [];
|
|
} catch (err: any) {
|
|
console.error('Failed to get format recommendations:', err);
|
|
return [];
|
|
}
|
|
}, []);
|
|
|
|
// Process format conversion
|
|
const processFormatConversion = useCallback(async (request: FormatConversionRequest): Promise<FormatConversionResult> => {
|
|
setIsConvertingFormat(true);
|
|
setFormatConversionError(null);
|
|
try {
|
|
const response = await aiApiClient.post('/api/image-studio/convert-format', request);
|
|
setFormatConversionResult(response.data);
|
|
return response.data;
|
|
} catch (err: any) {
|
|
console.error('Failed to convert format:', err);
|
|
const message = err.response?.data?.detail || 'Failed to convert image format';
|
|
setFormatConversionError(message);
|
|
throw new Error(message);
|
|
} finally {
|
|
setIsConvertingFormat(false);
|
|
}
|
|
}, []);
|
|
|
|
const clearFormatConversionResult = useCallback(() => {
|
|
setFormatConversionResult(null);
|
|
setFormatConversionError(null);
|
|
setFormatRecommendations([]);
|
|
}, []);
|
|
|
|
return {
|
|
// State
|
|
templates,
|
|
providers,
|
|
isLoading,
|
|
isGenerating,
|
|
results,
|
|
error,
|
|
costEstimate,
|
|
editOperations,
|
|
isLoadingEditOps,
|
|
isProcessingEdit,
|
|
editResult,
|
|
editError,
|
|
upscaleResult,
|
|
isUpscaling,
|
|
upscaleError,
|
|
controlOperations,
|
|
isLoadingControlOps,
|
|
isProcessingControl,
|
|
controlResult,
|
|
controlError,
|
|
|
|
// Actions
|
|
loadTemplates,
|
|
searchTemplates,
|
|
loadProviders,
|
|
generateImage,
|
|
estimateCost,
|
|
getPlatformSpecs,
|
|
clearResults,
|
|
clearError,
|
|
loadEditOperations,
|
|
processEdit,
|
|
clearEditResult,
|
|
processUpscale,
|
|
clearUpscaleResult,
|
|
loadControlOperations,
|
|
processControl,
|
|
clearControlResult,
|
|
optimizeForSocial,
|
|
getPlatformFormats,
|
|
isOptimizing,
|
|
optimizeResult,
|
|
optimizeError,
|
|
clearOptimizeResult,
|
|
// Edit model selection
|
|
editModels,
|
|
isLoadingEditModels,
|
|
loadEditModels,
|
|
modelRecommendation,
|
|
isLoadingRecommendation,
|
|
getModelRecommendation,
|
|
// Face swap
|
|
faceSwapModels,
|
|
isLoadingFaceSwapModels,
|
|
loadFaceSwapModels,
|
|
faceSwapModelRecommendation,
|
|
isLoadingFaceSwapRecommendation,
|
|
getFaceSwapModelRecommendation,
|
|
processFaceSwap,
|
|
isProcessingFaceSwap,
|
|
faceSwapResult,
|
|
faceSwapError,
|
|
clearFaceSwapResult,
|
|
// Compression
|
|
compressionFormats,
|
|
compressionPresets,
|
|
loadCompressionFormats,
|
|
loadCompressionPresets,
|
|
estimateCompression,
|
|
processCompression,
|
|
isCompressing,
|
|
compressionResult,
|
|
compressionError,
|
|
compressionEstimate,
|
|
clearCompressionResult,
|
|
// Format Converter
|
|
supportedFormats,
|
|
loadSupportedFormats,
|
|
getFormatRecommendations,
|
|
processFormatConversion,
|
|
isConvertingFormat,
|
|
formatConversionResult,
|
|
formatConversionError,
|
|
formatRecommendations,
|
|
clearFormatConversionResult,
|
|
};
|
|
};
|
|
|