| #!/usr/bin/env node |
|
|
| |
| |
| |
| |
| |
| |
|
|
| const axios = require('axios'); |
| const FormData = require('form-data'); |
| const fs = require('fs'); |
| const path = require('path'); |
| const WebSocket = require('ws'); |
|
|
| |
| const API_BASE_URL = process.env.BACKGROUNDFX_API_URL || 'https://api.backgroundfx.pro/v1'; |
| const API_KEY = process.env.BACKGROUNDFX_API_KEY || 'your-api-key-here'; |
| const WS_URL = process.env.BACKGROUNDFX_WS_URL || 'wss://ws.backgroundfx.pro'; |
|
|
| |
| |
| |
| class BackgroundFXClient { |
| constructor(apiKey, baseUrl = API_BASE_URL) { |
| this.apiKey = apiKey; |
| this.baseUrl = baseUrl.replace(/\/$/, ''); |
| |
| |
| this.client = axios.create({ |
| baseURL: this.baseUrl, |
| headers: { |
| 'Authorization': `Bearer ${apiKey}`, |
| 'User-Agent': 'BackgroundFX-Node-Client/1.0' |
| } |
| }); |
| } |
|
|
| |
| |
| |
| async removeBackground(imagePath, options = {}) { |
| const { |
| quality = 'high', |
| model = 'auto', |
| returnMask = false, |
| edgeRefinement = 50 |
| } = options; |
|
|
| |
| if (!fs.existsSync(imagePath)) { |
| throw new Error(`File not found: ${imagePath}`); |
| } |
|
|
| |
| const formData = new FormData(); |
| formData.append('file', fs.createReadStream(imagePath)); |
| formData.append('quality', quality); |
| formData.append('model', model); |
| formData.append('return_mask', returnMask.toString()); |
| formData.append('edge_refinement', edgeRefinement.toString()); |
|
|
| try { |
| console.log(`🔄 Processing image: ${path.basename(imagePath)}`); |
| |
| const response = await this.client.post('/process/remove-background', formData, { |
| headers: formData.getHeaders(), |
| maxContentLength: Infinity, |
| maxBodyLength: Infinity |
| }); |
|
|
| console.log('✅ Background removed successfully!'); |
| return response.data; |
| } catch (error) { |
| console.error('❌ Error processing image:', error.response?.data || error.message); |
| throw error; |
| } |
| } |
|
|
| |
| |
| |
| async processBatch(imagePaths, options = {}) { |
| const formData = new FormData(); |
| |
| |
| for (const imagePath of imagePaths) { |
| if (!fs.existsSync(imagePath)) { |
| console.warn(`⚠️ Skipping missing file: ${imagePath}`); |
| continue; |
| } |
| formData.append('files', fs.createReadStream(imagePath)); |
| } |
| |
| |
| formData.append('options', JSON.stringify(options)); |
|
|
| try { |
| console.log(`🔄 Processing batch of ${imagePaths.length} images...`); |
| |
| const response = await this.client.post('/process/batch', formData, { |
| headers: formData.getHeaders() |
| }); |
|
|
| const jobId = response.data.id; |
| console.log(`✅ Batch job created: ${jobId}`); |
| |
| |
| return await this.monitorJob(jobId); |
| } catch (error) { |
| console.error('❌ Batch processing failed:', error.message); |
| throw error; |
| } |
| } |
|
|
| |
| |
| |
| async monitorJob(jobId, pollInterval = 2000) { |
| console.log(`📊 Monitoring job: ${jobId}`); |
| |
| while (true) { |
| try { |
| const response = await this.client.get(`/process/jobs/${jobId}`); |
| const job = response.data; |
| |
| console.log(` Status: ${job.status} | Progress: ${job.progress}%`); |
| |
| if (job.status === 'completed') { |
| console.log('✅ Job completed!'); |
| return job; |
| } else if (job.status === 'failed') { |
| throw new Error(`Job failed: ${job.error}`); |
| } |
| |
| |
| await new Promise(resolve => setTimeout(resolve, pollInterval)); |
| } catch (error) { |
| console.error('❌ Error monitoring job:', error.message); |
| throw error; |
| } |
| } |
| } |
|
|
| |
| |
| |
| async replaceBackground(imageId, background, blendMode = 'normal') { |
| try { |
| const response = await this.client.post('/process/replace-background', { |
| image_id: imageId, |
| background: background, |
| blend_mode: blendMode |
| }); |
|
|
| console.log('✅ Background replaced!'); |
| return response.data; |
| } catch (error) { |
| console.error('❌ Error replacing background:', error.message); |
| throw error; |
| } |
| } |
|
|
| |
| |
| |
| async downloadResult(url, outputPath) { |
| const writer = fs.createWriteStream(outputPath); |
| |
| const response = await axios({ |
| url, |
| method: 'GET', |
| responseType: 'stream' |
| }); |
| |
| response.data.pipe(writer); |
| |
| return new Promise((resolve, reject) => { |
| writer.on('finish', () => { |
| console.log(`💾 Saved to: ${outputPath}`); |
| resolve(outputPath); |
| }); |
| writer.on('error', reject); |
| }); |
| } |
|
|
| |
| |
| |
| connectWebSocket(jobId) { |
| return new Promise((resolve, reject) => { |
| const ws = new WebSocket(`${WS_URL}?job_id=${jobId}`, { |
| headers: { |
| 'Authorization': `Bearer ${this.apiKey}` |
| } |
| }); |
|
|
| ws.on('open', () => { |
| console.log('🔌 WebSocket connected'); |
| ws.send(JSON.stringify({ action: 'subscribe', job_id: jobId })); |
| }); |
|
|
| ws.on('message', (data) => { |
| const message = JSON.parse(data); |
| console.log('📨 WebSocket message:', message); |
| |
| if (message.type === 'job:complete') { |
| ws.close(); |
| resolve(message.data); |
| } else if (message.type === 'job:error') { |
| ws.close(); |
| reject(new Error(message.error)); |
| } else if (message.type === 'job:progress') { |
| console.log(` Progress: ${message.progress}%`); |
| } |
| }); |
|
|
| ws.on('error', (error) => { |
| console.error('❌ WebSocket error:', error); |
| reject(error); |
| }); |
|
|
| ws.on('close', () => { |
| console.log('🔌 WebSocket disconnected'); |
| }); |
| }); |
| } |
| } |
|
|
| |
| |
| |
|
|
| |
| |
| |
| async function exampleBasicUsage() { |
| console.log('\n' + '='.repeat(60)); |
| console.log('EXAMPLE 1: Basic Background Removal'); |
| console.log('='.repeat(60)); |
|
|
| const client = new BackgroundFXClient(API_KEY); |
| |
| try { |
| |
| const result = await client.removeBackground('sample_images/portrait.jpg', { |
| quality: 'high' |
| }); |
| |
| |
| await client.downloadResult( |
| result.image, |
| 'output/portrait_no_bg.png' |
| ); |
| |
| console.log('✨ Basic processing complete!'); |
| } catch (error) { |
| console.error('Failed:', error.message); |
| } |
| } |
|
|
| |
| |
| |
| async function exampleBatchProcessing() { |
| console.log('\n' + '='.repeat(60)); |
| console.log('EXAMPLE 2: Batch Processing'); |
| console.log('='.repeat(60)); |
|
|
| const client = new BackgroundFXClient(API_KEY); |
| |
| const images = [ |
| 'sample_images/product1.jpg', |
| 'sample_images/product2.jpg', |
| 'sample_images/product3.jpg' |
| ]; |
| |
| try { |
| const job = await client.processBatch(images, { |
| quality: 'medium', |
| model: 'rembg' |
| }); |
| |
| |
| for (const [index, result] of job.results.entries()) { |
| await client.downloadResult( |
| result.image, |
| `output/batch/product${index + 1}_no_bg.png` |
| ); |
| } |
| |
| console.log('✨ Batch processing complete!'); |
| } catch (error) { |
| console.error('Failed:', error.message); |
| } |
| } |
|
|
| |
| |
| |
| async function exampleWebSocketMonitoring() { |
| console.log('\n' + '='.repeat(60)); |
| console.log('EXAMPLE 3: WebSocket Real-time Monitoring'); |
| console.log('='.repeat(60)); |
|
|
| const client = new BackgroundFXClient(API_KEY); |
| |
| try { |
| |
| const formData = new FormData(); |
| formData.append('files', fs.createReadStream('sample_images/large1.jpg')); |
| formData.append('files', fs.createReadStream('sample_images/large2.jpg')); |
| |
| const response = await client.client.post('/process/batch', formData, { |
| headers: formData.getHeaders() |
| }); |
| |
| const jobId = response.data.id; |
| console.log(`📋 Job ID: ${jobId}`); |
| |
| |
| const result = await client.connectWebSocket(jobId); |
| console.log('✨ Processing complete via WebSocket!'); |
| |
| } catch (error) { |
| console.error('Failed:', error.message); |
| } |
| } |
|
|
| |
| |
| |
| async function exampleBackgroundReplacement() { |
| console.log('\n' + '='.repeat(60)); |
| console.log('EXAMPLE 4: Background Replacement'); |
| console.log('='.repeat(60)); |
|
|
| const client = new BackgroundFXClient(API_KEY); |
| |
| try { |
| |
| const result = await client.removeBackground('sample_images/person.jpg'); |
| const imageId = result.id; |
| |
| |
| const backgrounds = [ |
| { type: 'color', value: '#3498db', name: 'blue' }, |
| { type: 'gradient', value: 'linear-gradient(45deg, #ff6b6b, #4ecdc4)', name: 'gradient' }, |
| { type: 'blur', value: 'blur:20', name: 'blurred' } |
| ]; |
| |
| for (const bg of backgrounds) { |
| console.log(`🎨 Applying ${bg.name} background...`); |
| const replaced = await client.replaceBackground(imageId, bg.value); |
| await client.downloadResult( |
| replaced.image, |
| `output/person_${bg.name}_bg.png` |
| ); |
| } |
| |
| console.log('✨ Background replacement complete!'); |
| } catch (error) { |
| console.error('Failed:', error.message); |
| } |
| } |
|
|
| |
| |
| |
| async function exampleErrorHandling() { |
| console.log('\n' + '='.repeat(60)); |
| console.log('EXAMPLE 5: Error Handling'); |
| console.log('='.repeat(60)); |
|
|
| const client = new BackgroundFXClient(API_KEY); |
| |
| |
| async function withRetry(fn, maxRetries = 3) { |
| for (let i = 0; i < maxRetries; i++) { |
| try { |
| return await fn(); |
| } catch (error) { |
| console.log(`⚠️ Attempt ${i + 1} failed: ${error.message}`); |
| if (i === maxRetries - 1) throw error; |
| |
| |
| const delay = Math.pow(2, i) * 1000; |
| console.log(`⏳ Waiting ${delay}ms before retry...`); |
| await new Promise(resolve => setTimeout(resolve, delay)); |
| } |
| } |
| } |
| |
| try { |
| const result = await withRetry(() => |
| client.removeBackground('sample_images/test.jpg') |
| ); |
| console.log('✅ Success after retries'); |
| } catch (error) { |
| console.error('❌ Failed after all retries:', error.message); |
| } |
| } |
|
|
| |
| |
| |
| async function exampleParallelProcessing() { |
| console.log('\n' + '='.repeat(60)); |
| console.log('EXAMPLE 6: Parallel Processing'); |
| console.log('='.repeat(60)); |
|
|
| const client = new BackgroundFXClient(API_KEY); |
| |
| const images = [ |
| 'sample_images/img1.jpg', |
| 'sample_images/img2.jpg', |
| 'sample_images/img3.jpg', |
| 'sample_images/img4.jpg' |
| ]; |
| |
| try { |
| console.log(`🚀 Processing ${images.length} images in parallel...`); |
| const startTime = Date.now(); |
| |
| |
| const promises = images.map(imagePath => |
| client.removeBackground(imagePath, { quality: 'medium' }) |
| .catch(err => ({ error: err.message, path: imagePath })) |
| ); |
| |
| const results = await Promise.all(promises); |
| |
| const elapsed = (Date.now() - startTime) / 1000; |
| console.log(`✅ Processed ${results.length} images in ${elapsed.toFixed(2)}s`); |
| |
| |
| const successes = results.filter(r => !r.error).length; |
| const failures = results.filter(r => r.error).length; |
| |
| console.log(` Successes: ${successes}`); |
| console.log(` Failures: ${failures}`); |
| |
| } catch (error) { |
| console.error('Failed:', error.message); |
| } |
| } |
|
|
| |
| |
| |
|
|
| async function main() { |
| console.log('\n' + '#'.repeat(60)); |
| console.log('# BackgroundFX Pro - Node.js Examples'); |
| console.log('#'.repeat(60)); |
| |
| |
| if (API_KEY === 'your-api-key-here') { |
| console.error('\n⚠️ Please set your API key in BACKGROUNDFX_API_KEY environment variable'); |
| process.exit(1); |
| } |
| |
| |
| const dirs = ['output', 'output/batch', 'sample_images']; |
| dirs.forEach(dir => { |
| if (!fs.existsSync(dir)) { |
| fs.mkdirSync(dir, { recursive: true }); |
| } |
| }); |
| |
| |
| const examples = [ |
| exampleBasicUsage, |
| exampleBatchProcessing, |
| exampleWebSocketMonitoring, |
| exampleBackgroundReplacement, |
| exampleErrorHandling, |
| exampleParallelProcessing |
| ]; |
| |
| for (const example of examples) { |
| try { |
| await example(); |
| } catch (error) { |
| console.error(`\n❌ Example failed: ${error.message}`); |
| } |
| } |
| |
| console.log('\n' + '#'.repeat(60)); |
| console.log('# All examples complete!'); |
| console.log('#'.repeat(60)); |
| } |
|
|
| |
| if (require.main === module) { |
| main().catch(console.error); |
| } |
|
|
| module.exports = { BackgroundFXClient }; |