AI Image Studio, AI podcast Maker, AI product Marketing
This commit is contained in:
@@ -15,7 +15,7 @@ export interface ContentAsset {
|
||||
description?: string;
|
||||
prompt?: string;
|
||||
tags: string[];
|
||||
metadata: Record<string, any>;
|
||||
asset_metadata: Record<string, any>;
|
||||
provider?: string;
|
||||
model?: string;
|
||||
cost: number;
|
||||
@@ -44,7 +44,7 @@ export interface AssetListResponse {
|
||||
offset: number;
|
||||
}
|
||||
|
||||
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000';
|
||||
const API_BASE_URL = process.env.REACT_APP_API_BASE_URL || 'http://localhost:8000';
|
||||
|
||||
export const useContentAssets = (filters: AssetFilters = {}) => {
|
||||
const { getToken } = useAuth();
|
||||
|
||||
496
frontend/src/hooks/useProductMarketing.ts
Normal file
496
frontend/src/hooks/useProductMarketing.ts
Normal file
@@ -0,0 +1,496 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { aiApiClient } from '../api/client';
|
||||
|
||||
export interface CampaignCreateRequest {
|
||||
campaign_name: string;
|
||||
goal: string;
|
||||
kpi?: string;
|
||||
channels: string[];
|
||||
product_context?: {
|
||||
product_description?: string;
|
||||
product_name?: string;
|
||||
marketing_goal?: string;
|
||||
[key: string]: any;
|
||||
};
|
||||
}
|
||||
|
||||
export interface CampaignBlueprint {
|
||||
campaign_id: string;
|
||||
campaign_name: string;
|
||||
goal: string;
|
||||
kpi?: string;
|
||||
phases: Array<{
|
||||
name: string;
|
||||
duration_days: number;
|
||||
purpose: string;
|
||||
}>;
|
||||
asset_nodes: Array<{
|
||||
asset_id: string;
|
||||
asset_type: string;
|
||||
channel: string;
|
||||
status: string;
|
||||
}>;
|
||||
channels: string[];
|
||||
status: string;
|
||||
}
|
||||
|
||||
export interface AssetProposal {
|
||||
asset_id: string;
|
||||
asset_type: string;
|
||||
channel: string;
|
||||
proposed_prompt: string;
|
||||
recommended_template?: string;
|
||||
recommended_provider?: string;
|
||||
cost_estimate: number;
|
||||
concept_summary: string;
|
||||
}
|
||||
|
||||
export interface AssetProposalsResponse {
|
||||
proposals: Record<string, AssetProposal>;
|
||||
total_assets: number;
|
||||
}
|
||||
|
||||
export interface BrandDNATokens {
|
||||
writing_style: {
|
||||
tone: string;
|
||||
voice: string;
|
||||
complexity: string;
|
||||
engagement_level: string;
|
||||
};
|
||||
target_audience: {
|
||||
demographics: string[];
|
||||
industry_focus: string;
|
||||
expertise_level: string;
|
||||
};
|
||||
visual_identity: {
|
||||
color_palette?: string[];
|
||||
brand_values?: string[];
|
||||
positioning?: string;
|
||||
style_guidelines?: any;
|
||||
};
|
||||
persona: {
|
||||
persona_name?: string;
|
||||
archetype?: string;
|
||||
core_belief?: string;
|
||||
linguistic_fingerprint?: any;
|
||||
platform_personas?: any;
|
||||
};
|
||||
competitive_positioning: {
|
||||
differentiators: string[];
|
||||
unique_value_props: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface ChannelPack {
|
||||
channel: string;
|
||||
platform: string;
|
||||
asset_type: string;
|
||||
templates: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
dimensions: string;
|
||||
aspect_ratio: string;
|
||||
recommended_provider: string;
|
||||
quality: string;
|
||||
}>;
|
||||
formats: Array<{
|
||||
name: string;
|
||||
width: number;
|
||||
height: number;
|
||||
ratio: string;
|
||||
safe_zone?: any;
|
||||
}>;
|
||||
copy_framework: Record<string, any>;
|
||||
optimization_tips: string[];
|
||||
}
|
||||
|
||||
export interface AssetAuditResult {
|
||||
asset_info: {
|
||||
width: number;
|
||||
height: number;
|
||||
format: string;
|
||||
mode: string;
|
||||
quality_score: number;
|
||||
};
|
||||
recommendations: Array<{
|
||||
operation: string;
|
||||
priority: string;
|
||||
reason: string;
|
||||
suggested_mode?: string;
|
||||
suggested_format?: string;
|
||||
suggested_operations?: string[];
|
||||
}>;
|
||||
status: 'usable' | 'needs_enhancement' | 'error';
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface PreflightValidationResult {
|
||||
can_proceed: boolean;
|
||||
message?: string;
|
||||
error_details?: Record<string, any>;
|
||||
summary: {
|
||||
total_assets: number;
|
||||
image_count: number;
|
||||
text_count: number;
|
||||
estimated_cost: number;
|
||||
};
|
||||
}
|
||||
|
||||
export const useProductMarketing = () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Campaign Blueprint
|
||||
const [blueprint, setBlueprint] = useState<CampaignBlueprint | null>(null);
|
||||
const [isCreatingBlueprint, setIsCreatingBlueprint] = useState(false);
|
||||
|
||||
// Asset Proposals
|
||||
const [proposals, setProposals] = useState<AssetProposalsResponse | null>(null);
|
||||
const [isGeneratingProposals, setIsGeneratingProposals] = useState(false);
|
||||
|
||||
// Brand DNA
|
||||
const [brandDNA, setBrandDNA] = useState<BrandDNATokens | null>(null);
|
||||
const [isLoadingBrandDNA, setIsLoadingBrandDNA] = useState(false);
|
||||
|
||||
// Channel Packs
|
||||
const [channelPack, setChannelPack] = useState<ChannelPack | null>(null);
|
||||
const [isLoadingChannelPack, setIsLoadingChannelPack] = useState(false);
|
||||
|
||||
// Asset Audit
|
||||
const [auditResult, setAuditResult] = useState<AssetAuditResult | null>(null);
|
||||
const [isAuditing, setIsAuditing] = useState(false);
|
||||
|
||||
// Asset Generation
|
||||
const [isGeneratingAsset, setIsGeneratingAsset] = useState(false);
|
||||
const [generatedAsset, setGeneratedAsset] = useState<any>(null);
|
||||
|
||||
// Pre-flight Validation
|
||||
const [preflightResult, setPreflightResult] = useState<PreflightValidationResult | null>(null);
|
||||
const [isValidatingPreflight, setIsValidatingPreflight] = useState(false);
|
||||
|
||||
const createCampaignBlueprint = useCallback(
|
||||
async (request: CampaignCreateRequest): Promise<CampaignBlueprint> => {
|
||||
setIsCreatingBlueprint(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await aiApiClient.post<CampaignBlueprint>(
|
||||
'/api/product-marketing/campaigns/create-blueprint',
|
||||
request
|
||||
);
|
||||
setBlueprint(response.data);
|
||||
return response.data;
|
||||
} catch (err: any) {
|
||||
const errorMessage = err.response?.data?.detail || err.message || 'Failed to create campaign blueprint';
|
||||
setError(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
} finally {
|
||||
setIsCreatingBlueprint(false);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const generateAssetProposals = useCallback(
|
||||
async (campaignId: string, productContext?: any): Promise<AssetProposalsResponse> => {
|
||||
setIsGeneratingProposals(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await aiApiClient.post<AssetProposalsResponse>(
|
||||
`/api/product-marketing/campaigns/${campaignId}/generate-proposals`,
|
||||
{
|
||||
campaign_id: campaignId,
|
||||
product_context: productContext,
|
||||
}
|
||||
);
|
||||
setProposals(response.data);
|
||||
return response.data;
|
||||
} catch (err: any) {
|
||||
const errorMessage = err.response?.data?.detail || err.message || 'Failed to generate asset proposals';
|
||||
setError(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
} finally {
|
||||
setIsGeneratingProposals(false);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const generateAsset = useCallback(
|
||||
async (assetProposal: AssetProposal, productContext?: any): Promise<any> => {
|
||||
setIsGeneratingAsset(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await aiApiClient.post('/api/product-marketing/assets/generate', {
|
||||
asset_proposal: assetProposal,
|
||||
product_context: productContext,
|
||||
});
|
||||
setGeneratedAsset(response.data);
|
||||
return response.data;
|
||||
} catch (err: any) {
|
||||
const errorMessage = err.response?.data?.detail || err.message || 'Failed to generate asset';
|
||||
setError(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
} finally {
|
||||
setIsGeneratingAsset(false);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const getBrandDNA = useCallback(async (): Promise<BrandDNATokens> => {
|
||||
setIsLoadingBrandDNA(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await aiApiClient.get<{ brand_dna: BrandDNATokens }>('/api/product-marketing/brand-dna');
|
||||
setBrandDNA(response.data.brand_dna);
|
||||
return response.data.brand_dna;
|
||||
} catch (err: any) {
|
||||
const errorMessage = err.response?.data?.detail || err.message || 'Failed to get brand DNA';
|
||||
setError(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
} finally {
|
||||
setIsLoadingBrandDNA(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const getChannelBrandDNA = useCallback(
|
||||
async (channel: string): Promise<BrandDNATokens> => {
|
||||
setIsLoadingBrandDNA(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await aiApiClient.get<{ channel: string; brand_dna: BrandDNATokens }>(
|
||||
`/api/product-marketing/brand-dna/channel/${channel}`
|
||||
);
|
||||
return response.data.brand_dna;
|
||||
} catch (err: any) {
|
||||
const errorMessage = err.response?.data?.detail || err.message || 'Failed to get channel brand DNA';
|
||||
setError(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
} finally {
|
||||
setIsLoadingBrandDNA(false);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const getChannelPack = useCallback(
|
||||
async (channel: string, assetType: string = 'social_post'): Promise<ChannelPack> => {
|
||||
setIsLoadingChannelPack(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await aiApiClient.get<ChannelPack>(
|
||||
`/api/product-marketing/channels/${channel}/pack?asset_type=${assetType}`
|
||||
);
|
||||
setChannelPack(response.data);
|
||||
return response.data;
|
||||
} catch (err: any) {
|
||||
const errorMessage = err.response?.data?.detail || err.message || 'Failed to get channel pack';
|
||||
setError(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
} finally {
|
||||
setIsLoadingChannelPack(false);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const auditAsset = useCallback(
|
||||
async (imageBase64: string, assetMetadata?: any): Promise<AssetAuditResult> => {
|
||||
setIsAuditing(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await aiApiClient.post<AssetAuditResult>('/api/product-marketing/assets/audit', {
|
||||
image_base64: imageBase64,
|
||||
asset_metadata: assetMetadata,
|
||||
});
|
||||
setAuditResult(response.data);
|
||||
return response.data;
|
||||
} catch (err: any) {
|
||||
const errorMessage = err.response?.data?.detail || err.message || 'Failed to audit asset';
|
||||
setError(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
} finally {
|
||||
setIsAuditing(false);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const clearError = useCallback(() => {
|
||||
setError(null);
|
||||
}, []);
|
||||
|
||||
const clearBlueprint = useCallback(() => {
|
||||
setBlueprint(null);
|
||||
}, []);
|
||||
|
||||
const clearProposals = useCallback(() => {
|
||||
setProposals(null);
|
||||
}, []);
|
||||
|
||||
const clearAuditResult = useCallback(() => {
|
||||
setAuditResult(null);
|
||||
}, []);
|
||||
|
||||
// Campaign listing
|
||||
const [campaigns, setCampaigns] = useState<CampaignBlueprint[]>([]);
|
||||
const [isLoadingCampaigns, setIsLoadingCampaigns] = useState(false);
|
||||
|
||||
const listCampaigns = useCallback(async (status?: string): Promise<CampaignBlueprint[]> => {
|
||||
setIsLoadingCampaigns(true);
|
||||
setError(null);
|
||||
try {
|
||||
const url = status ? `/api/product-marketing/campaigns?status=${status}` : '/api/product-marketing/campaigns';
|
||||
const response = await aiApiClient.get<{ campaigns: CampaignBlueprint[]; total: number }>(url);
|
||||
setCampaigns(response.data.campaigns);
|
||||
return response.data.campaigns;
|
||||
} catch (err: any) {
|
||||
const errorMessage = err.response?.data?.detail || err.message || 'Failed to list campaigns';
|
||||
setError(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
} finally {
|
||||
setIsLoadingCampaigns(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const getCampaign = useCallback(async (campaignId: string): Promise<CampaignBlueprint> => {
|
||||
setError(null);
|
||||
try {
|
||||
const response = await aiApiClient.get<CampaignBlueprint>(`/api/product-marketing/campaigns/${campaignId}`);
|
||||
return response.data;
|
||||
} catch (err: any) {
|
||||
const errorMessage = err.response?.data?.detail || err.message || 'Failed to get campaign';
|
||||
setError(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const getCampaignProposals = useCallback(async (campaignId: string): Promise<AssetProposalsResponse> => {
|
||||
setError(null);
|
||||
try {
|
||||
const response = await aiApiClient.get<AssetProposalsResponse>(`/api/product-marketing/campaigns/${campaignId}/proposals`);
|
||||
return response.data;
|
||||
} catch (err: any) {
|
||||
const errorMessage = err.response?.data?.detail || err.message || 'Failed to get proposals';
|
||||
setError(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const validateCampaignPreflight = useCallback(
|
||||
async (request: CampaignCreateRequest): Promise<PreflightValidationResult> => {
|
||||
setIsValidatingPreflight(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await aiApiClient.post<PreflightValidationResult>(
|
||||
'/api/product-marketing/campaigns/validate-preflight',
|
||||
request
|
||||
);
|
||||
setPreflightResult(response.data);
|
||||
return response.data;
|
||||
} catch (err: any) {
|
||||
const errorMessage = err.response?.data?.detail || err.message || 'Failed to validate campaign pre-flight';
|
||||
setError(errorMessage);
|
||||
// Return error result
|
||||
const errorResult: PreflightValidationResult = {
|
||||
can_proceed: false,
|
||||
message: errorMessage,
|
||||
summary: {
|
||||
total_assets: 0,
|
||||
image_count: 0,
|
||||
text_count: 0,
|
||||
estimated_cost: 0,
|
||||
},
|
||||
};
|
||||
setPreflightResult(errorResult);
|
||||
return errorResult;
|
||||
} finally {
|
||||
setIsValidatingPreflight(false);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
// Product Image Generation (Product Marketing Suite - Product Assets)
|
||||
const [isGeneratingProductImage, setIsGeneratingProductImage] = useState(false);
|
||||
const [generatedProductImage, setGeneratedProductImage] = useState<any>(null);
|
||||
const [productImageError, setProductImageError] = useState<string | null>(null);
|
||||
|
||||
const generateProductImage = useCallback(
|
||||
async (request: {
|
||||
product_name: string;
|
||||
product_description: string;
|
||||
environment?: string;
|
||||
background_style?: string;
|
||||
lighting?: string;
|
||||
product_variant?: string;
|
||||
angle?: string;
|
||||
style?: string;
|
||||
resolution?: string;
|
||||
num_variations?: number;
|
||||
brand_colors?: string[];
|
||||
additional_context?: string;
|
||||
}): Promise<any> => {
|
||||
setIsGeneratingProductImage(true);
|
||||
setProductImageError(null);
|
||||
try {
|
||||
const response = await aiApiClient.post('/api/product-marketing/products/photoshoot', request);
|
||||
setGeneratedProductImage(response.data);
|
||||
return response.data;
|
||||
} catch (err: any) {
|
||||
const errorMessage = err.response?.data?.detail || err.message || 'Failed to generate product image';
|
||||
setProductImageError(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
} finally {
|
||||
setIsGeneratingProductImage(false);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
return {
|
||||
// State
|
||||
isLoading,
|
||||
error,
|
||||
blueprint,
|
||||
isCreatingBlueprint,
|
||||
proposals,
|
||||
isGeneratingProposals,
|
||||
brandDNA,
|
||||
isLoadingBrandDNA,
|
||||
channelPack,
|
||||
isLoadingChannelPack,
|
||||
auditResult,
|
||||
isAuditing,
|
||||
isGeneratingAsset,
|
||||
generatedAsset,
|
||||
preflightResult,
|
||||
isValidatingPreflight,
|
||||
|
||||
// Actions
|
||||
createCampaignBlueprint,
|
||||
generateAssetProposals,
|
||||
generateAsset,
|
||||
getBrandDNA,
|
||||
getChannelBrandDNA,
|
||||
getChannelPack,
|
||||
auditAsset,
|
||||
clearError,
|
||||
clearBlueprint,
|
||||
clearProposals,
|
||||
clearAuditResult,
|
||||
campaigns,
|
||||
isLoadingCampaigns,
|
||||
listCampaigns,
|
||||
getCampaign,
|
||||
getCampaignProposals,
|
||||
validateCampaignPreflight,
|
||||
|
||||
// Product Image Generation (Product Marketing Suite)
|
||||
generateProductImage,
|
||||
isGeneratingProductImage,
|
||||
generatedProductImage,
|
||||
productImageError,
|
||||
};
|
||||
};
|
||||
|
||||
153
frontend/src/hooks/useTransformStudio.ts
Normal file
153
frontend/src/hooks/useTransformStudio.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { aiApiClient } from '../api/client';
|
||||
|
||||
export interface TransformImageToVideoRequest {
|
||||
image_base64: string;
|
||||
prompt: string;
|
||||
audio_base64?: string;
|
||||
resolution?: '480p' | '720p' | '1080p';
|
||||
duration?: 5 | 10;
|
||||
negative_prompt?: string;
|
||||
seed?: number;
|
||||
enable_prompt_expansion?: boolean;
|
||||
}
|
||||
|
||||
export interface TalkingAvatarRequest {
|
||||
image_base64: string;
|
||||
audio_base64: string;
|
||||
resolution?: '480p' | '720p';
|
||||
prompt?: string;
|
||||
mask_image_base64?: string;
|
||||
seed?: number;
|
||||
}
|
||||
|
||||
export interface TransformVideoResponse {
|
||||
success: boolean;
|
||||
video_url: string;
|
||||
video_base64?: string;
|
||||
duration: number;
|
||||
resolution: string;
|
||||
width: number;
|
||||
height: number;
|
||||
file_size: number;
|
||||
cost: number;
|
||||
provider: string;
|
||||
model: string;
|
||||
metadata: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface CostEstimateRequest {
|
||||
operation: 'image-to-video' | 'talking-avatar';
|
||||
resolution: string;
|
||||
duration?: number;
|
||||
}
|
||||
|
||||
export interface CostEstimateResponse {
|
||||
estimated_cost: number;
|
||||
breakdown: {
|
||||
base_cost: number;
|
||||
per_second: number;
|
||||
duration: number;
|
||||
total: number;
|
||||
};
|
||||
currency: string;
|
||||
provider: string;
|
||||
model: string;
|
||||
}
|
||||
|
||||
export const useTransformStudio = () => {
|
||||
const [isGenerating, setIsGenerating] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [result, setResult] = useState<TransformVideoResponse | null>(null);
|
||||
const [costEstimate, setCostEstimate] = useState<CostEstimateResponse | null>(null);
|
||||
|
||||
const transformImageToVideo = useCallback(
|
||||
async (request: TransformImageToVideoRequest): Promise<TransformVideoResponse> => {
|
||||
setIsGenerating(true);
|
||||
setError(null);
|
||||
setResult(null);
|
||||
|
||||
try {
|
||||
const response = await aiApiClient.post<TransformVideoResponse>(
|
||||
'/api/image-studio/transform/image-to-video',
|
||||
request
|
||||
);
|
||||
setResult(response.data);
|
||||
return response.data;
|
||||
} catch (err: any) {
|
||||
const errorMessage =
|
||||
err.response?.data?.detail || err.message || 'Failed to generate video';
|
||||
setError(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
} finally {
|
||||
setIsGenerating(false);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const createTalkingAvatar = useCallback(
|
||||
async (request: TalkingAvatarRequest): Promise<TransformVideoResponse> => {
|
||||
setIsGenerating(true);
|
||||
setError(null);
|
||||
setResult(null);
|
||||
|
||||
try {
|
||||
const response = await aiApiClient.post<TransformVideoResponse>(
|
||||
'/api/image-studio/transform/talking-avatar',
|
||||
request
|
||||
);
|
||||
setResult(response.data);
|
||||
return response.data;
|
||||
} catch (err: any) {
|
||||
const errorMessage =
|
||||
err.response?.data?.detail || err.message || 'Failed to create talking avatar';
|
||||
setError(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
} finally {
|
||||
setIsGenerating(false);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const estimateCost = useCallback(
|
||||
async (request: CostEstimateRequest): Promise<CostEstimateResponse> => {
|
||||
try {
|
||||
const response = await aiApiClient.post<CostEstimateResponse>(
|
||||
'/api/image-studio/transform/estimate-cost',
|
||||
request
|
||||
);
|
||||
setCostEstimate(response.data);
|
||||
return response.data;
|
||||
} catch (err: any) {
|
||||
const errorMessage =
|
||||
err.response?.data?.detail || err.message || 'Failed to estimate cost';
|
||||
setError(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const clearError = useCallback(() => {
|
||||
setError(null);
|
||||
}, []);
|
||||
|
||||
const clearResult = useCallback(() => {
|
||||
setResult(null);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
isGenerating,
|
||||
error,
|
||||
result,
|
||||
costEstimate,
|
||||
transformImageToVideo,
|
||||
createTalkingAvatar,
|
||||
estimateCost,
|
||||
clearError,
|
||||
clearResult,
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user