/** * 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 { 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 { 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 { 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 { 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 { 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 (and optionally an image) using Meta AI * - Text-to-Video: Just provide prompt and cookies * - Image-to-Video: Also provide imageBase64 * Video generation takes longer than image generation (30-60+ seconds) */ async generateVideo( prompt: string, cookies: string, imageBase64?: string ): Promise { const mode = imageBase64 ? 'image-to-video' : 'text-to-video'; console.log(`[MetaCrawl] Sending ${mode} 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, image_base64: imageBase64 }) }); 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; }