apix/lib/providers/meta-crawl-client.ts
Khoa.vo 0f87b8ef99
Some checks are pending
CI / build (18.x) (push) Waiting to run
CI / build (20.x) (push) Waiting to run
feat: add Meta AI video generation
- Add /video/generate endpoint to crawl4ai Python service
- Add VideoGenerateRequest and VideoGenerateResponse models
- Add generateVideo method to MetaCrawlClient TypeScript client
- Add /api/meta/video Next.js API route
- Add 'Video' button in PromptHero UI (visible only for Meta AI provider)
- Blue/cyan gradient styling for Video button to differentiate from Generate
2026-01-06 13:52:31 +07:00

222 lines
5.7 KiB
TypeScript

/**
* Meta AI Crawl4AI Client
*
* TypeScript client for the Python Crawl4AI microservice
* that handles Meta AI image generation with bot detection bypass.
*/
const CRAWL4AI_URL = process.env.CRAWL4AI_URL || 'http://localhost:8000';
export interface MetaCrawlImage {
url: string;
data?: string; // base64
prompt: string;
model: string;
}
export interface MetaCrawlResponse {
success: boolean;
images: MetaCrawlImage[];
error?: string;
task_id?: string;
}
export interface TaskStatusResponse {
task_id: string;
status: 'pending' | 'processing' | 'completed' | 'failed';
images: MetaCrawlImage[];
error?: string;
progress?: number;
}
export class MetaCrawlClient {
private baseUrl: string;
constructor(baseUrl?: string) {
this.baseUrl = baseUrl || CRAWL4AI_URL;
}
/**
* Generate images synchronously (waits for completion)
*/
async generate(
prompt: string,
cookies: string,
numImages: number = 4
): Promise<MetaCrawlImage[]> {
console.log(`[MetaCrawl] Sending request to ${this.baseUrl}/generate/sync`);
const response = await fetch(`${this.baseUrl}/generate/sync`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
prompt,
cookies,
num_images: numImages
})
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`MetaCrawl service error: ${response.status} - ${errorText}`);
}
const data: MetaCrawlResponse = await response.json();
if (!data.success) {
throw new Error(data.error || 'Image generation failed');
}
return data.images;
}
/**
* Start async image generation (returns immediately with task_id)
*/
async generateAsync(
prompt: string,
cookies: string,
numImages: number = 4
): Promise<string> {
const response = await fetch(`${this.baseUrl}/generate`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
prompt,
cookies,
num_images: numImages
})
});
if (!response.ok) {
throw new Error(`MetaCrawl service error: ${response.status}`);
}
const data: MetaCrawlResponse = await response.json();
if (!data.task_id) {
throw new Error('No task_id returned from async generation');
}
return data.task_id;
}
/**
* Get status of an async generation task
*/
async getTaskStatus(taskId: string): Promise<TaskStatusResponse> {
const response = await fetch(`${this.baseUrl}/status/${taskId}`);
if (!response.ok) {
if (response.status === 404) {
throw new Error('Task not found');
}
throw new Error(`MetaCrawl service error: ${response.status}`);
}
return response.json();
}
/**
* Poll for async task completion
*/
async waitForCompletion(
taskId: string,
pollIntervalMs: number = 2000,
timeoutMs: number = 120000
): Promise<MetaCrawlImage[]> {
const startTime = Date.now();
while (Date.now() - startTime < timeoutMs) {
const status = await this.getTaskStatus(taskId);
if (status.status === 'completed') {
return status.images;
}
if (status.status === 'failed') {
throw new Error(status.error || 'Generation failed');
}
await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
}
throw new Error('Task timed out');
}
/**
* Check if the Crawl4AI service is healthy
*/
async healthCheck(): Promise<boolean> {
try {
const response = await fetch(`${this.baseUrl}/health`);
return response.ok;
} catch {
return false;
}
}
/**
* Get current rate limit status
*/
async getRateLimitStatus(): Promise<{
requests_this_hour: number;
max_per_hour: number;
delay_seconds: number;
}> {
const response = await fetch(`${this.baseUrl}/rate-limit`);
return response.json();
}
/**
* Generate video from text prompt using Meta AI
* Video generation takes longer than image generation (30-60+ seconds)
*/
async generateVideo(
prompt: string,
cookies: string
): Promise<MetaCrawlVideoResponse> {
console.log(`[MetaCrawl] Sending video request to ${this.baseUrl}/video/generate`);
const response = await fetch(`${this.baseUrl}/video/generate`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
prompt,
cookies
})
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`MetaCrawl video service error: ${response.status} - ${errorText}`);
}
const data: MetaCrawlVideoResponse = await response.json();
if (!data.success) {
throw new Error(data.error || 'Video generation failed');
}
return data;
}
}
export interface MetaCrawlVideo {
url: string;
prompt: string;
model: string;
}
export interface MetaCrawlVideoResponse {
success: boolean;
videos: MetaCrawlVideo[];
error?: string;
conversation_id?: string;
}