MogensR commited on
Commit
5d187ed
·
1 Parent(s): 75fd3ca

Create web/src/lib/api/processing.ts

Browse files
Files changed (1) hide show
  1. web/src/lib/api/processing.ts +299 -0
web/src/lib/api/processing.ts ADDED
@@ -0,0 +1,299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { apiClient } from './client'
2
+ import { io, Socket } from 'socket.io-client'
3
+
4
+ export interface ProcessingOptions {
5
+ model?: 'rembg' | 'u2net' | 'deeplab' | 'custom'
6
+ quality?: 'low' | 'medium' | 'high' | 'ultra'
7
+ returnMask?: boolean
8
+ edgeRefinement?: number
9
+ feather?: number
10
+ tolerance?: number
11
+ preserveDetails?: boolean
12
+ }
13
+
14
+ export interface ProcessingResult {
15
+ id: string
16
+ image: string
17
+ mask?: string
18
+ metadata: {
19
+ width: number
20
+ height: number
21
+ format: string
22
+ processingTime: number
23
+ }
24
+ }
25
+
26
+ export interface BatchJob {
27
+ id: string
28
+ status: 'pending' | 'processing' | 'completed' | 'failed'
29
+ progress: number
30
+ totalFiles: number
31
+ processedFiles: number
32
+ results?: ProcessingResult[]
33
+ error?: string
34
+ createdAt: string
35
+ completedAt?: string
36
+ }
37
+
38
+ class ProcessingAPI {
39
+ private socket: Socket | null = null
40
+ private listeners: Map<string, Set<Function>> = new Map()
41
+
42
+ /**
43
+ * Process a single image
44
+ */
45
+ async processImage(
46
+ file: File,
47
+ options: ProcessingOptions = {},
48
+ onProgress?: (progress: number) => void
49
+ ): Promise<ProcessingResult> {
50
+ const formData = new FormData()
51
+ formData.append('file', file)
52
+ formData.append('options', JSON.stringify(options))
53
+
54
+ return apiClient.upload<ProcessingResult>(
55
+ '/api/v1/process/remove-background',
56
+ file,
57
+ onProgress
58
+ )
59
+ }
60
+
61
+ /**
62
+ * Process multiple images in batch
63
+ */
64
+ async processBatch(
65
+ files: File[],
66
+ options: ProcessingOptions = {}
67
+ ): Promise<BatchJob> {
68
+ const formData = new FormData()
69
+ files.forEach((file) => formData.append('files', file))
70
+ formData.append('options', JSON.stringify(options))
71
+
72
+ const response = await apiClient.post<BatchJob>('/api/v1/process/batch', formData, {
73
+ headers: { 'Content-Type': 'multipart/form-data' },
74
+ })
75
+
76
+ // Connect to WebSocket for real-time updates
77
+ this.connectWebSocket(response.id)
78
+
79
+ return response
80
+ }
81
+
82
+ /**
83
+ * Get batch job status
84
+ */
85
+ async getJobStatus(jobId: string): Promise<BatchJob> {
86
+ return apiClient.get<BatchJob>(`/api/v1/process/jobs/${jobId}`)
87
+ }
88
+
89
+ /**
90
+ * Cancel a batch job
91
+ */
92
+ async cancelJob(jobId: string): Promise<void> {
93
+ await apiClient.post(`/api/v1/process/jobs/${jobId}/cancel`)
94
+ }
95
+
96
+ /**
97
+ * Replace background with a new one
98
+ */
99
+ async replaceBackground(
100
+ imageId: string,
101
+ background: string | File
102
+ ): Promise<ProcessingResult> {
103
+ const formData = new FormData()
104
+ formData.append('imageId', imageId)
105
+
106
+ if (background instanceof File) {
107
+ formData.append('background', background)
108
+ } else {
109
+ formData.append('backgroundUrl', background)
110
+ }
111
+
112
+ return apiClient.post<ProcessingResult>(
113
+ '/api/v1/process/replace-background',
114
+ formData,
115
+ { headers: { 'Content-Type': 'multipart/form-data' } }
116
+ )
117
+ }
118
+
119
+ /**
120
+ * Apply AI-powered enhancements
121
+ */
122
+ async enhanceImage(
123
+ imageId: string,
124
+ enhancements: {
125
+ sharpness?: number
126
+ contrast?: number
127
+ brightness?: number
128
+ saturation?: number
129
+ denoise?: boolean
130
+ upscale?: 2 | 4
131
+ }
132
+ ): Promise<ProcessingResult> {
133
+ return apiClient.post<ProcessingResult>('/api/v1/process/enhance', {
134
+ imageId,
135
+ enhancements,
136
+ })
137
+ }
138
+
139
+ /**
140
+ * Generate AI background
141
+ */
142
+ async generateBackground(
143
+ prompt: string,
144
+ style?: 'realistic' | 'artistic' | 'abstract' | 'gradient'
145
+ ): Promise<{ id: string; url: string }> {
146
+ return apiClient.post('/api/v1/backgrounds/generate', {
147
+ prompt,
148
+ style,
149
+ })
150
+ }
151
+
152
+ /**
153
+ * Smart edge detection and refinement
154
+ */
155
+ async refineEdges(
156
+ imageId: string,
157
+ maskId: string,
158
+ options: {
159
+ mode: 'hair' | 'fur' | 'smooth' | 'detailed'
160
+ strength: number
161
+ }
162
+ ): Promise<ProcessingResult> {
163
+ return apiClient.post<ProcessingResult>('/api/v1/process/refine-edges', {
164
+ imageId,
165
+ maskId,
166
+ ...options,
167
+ })
168
+ }
169
+
170
+ /**
171
+ * Connect to WebSocket for real-time updates
172
+ */
173
+ private connectWebSocket(jobId: string) {
174
+ if (this.socket?.connected) {
175
+ this.socket.emit('subscribe', { jobId })
176
+ return
177
+ }
178
+
179
+ const wsUrl = process.env.NEXT_PUBLIC_WS_URL || 'ws://localhost:8000'
180
+ this.socket = io(wsUrl, {
181
+ transports: ['websocket'],
182
+ query: { jobId },
183
+ })
184
+
185
+ this.socket.on('connect', () => {
186
+ console.log('WebSocket connected')
187
+ this.socket?.emit('subscribe', { jobId })
188
+ })
189
+
190
+ this.socket.on('job:progress', (data) => {
191
+ this.emit('progress', data)
192
+ })
193
+
194
+ this.socket.on('job:complete', (data) => {
195
+ this.emit('complete', data)
196
+ this.disconnectWebSocket()
197
+ })
198
+
199
+ this.socket.on('job:error', (data) => {
200
+ this.emit('error', data)
201
+ this.disconnectWebSocket()
202
+ })
203
+
204
+ this.socket.on('disconnect', () => {
205
+ console.log('WebSocket disconnected')
206
+ })
207
+ }
208
+
209
+ /**
210
+ * Disconnect WebSocket
211
+ */
212
+ private disconnectWebSocket() {
213
+ if (this.socket) {
214
+ this.socket.disconnect()
215
+ this.socket = null
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Subscribe to events
221
+ */
222
+ on(event: string, callback: Function) {
223
+ if (!this.listeners.has(event)) {
224
+ this.listeners.set(event, new Set())
225
+ }
226
+ this.listeners.get(event)?.add(callback)
227
+ }
228
+
229
+ /**
230
+ * Unsubscribe from events
231
+ */
232
+ off(event: string, callback: Function) {
233
+ this.listeners.get(event)?.delete(callback)
234
+ }
235
+
236
+ /**
237
+ * Emit events
238
+ */
239
+ private emit(event: string, data: any) {
240
+ this.listeners.get(event)?.forEach((callback) => callback(data))
241
+ }
242
+
243
+ /**
244
+ * Estimate processing time
245
+ */
246
+ estimateProcessingTime(
247
+ fileSize: number,
248
+ options: ProcessingOptions
249
+ ): number {
250
+ const basetime = 1000 // 1 second base
251
+ const sizeMultiplier = fileSize / (1024 * 1024) // MB
252
+ const qualityMultiplier = {
253
+ low: 0.5,
254
+ medium: 1,
255
+ high: 1.5,
256
+ ultra: 2,
257
+ }[options.quality || 'medium']
258
+
259
+ return Math.round(basetime * sizeMultiplier * qualityMultiplier)
260
+ }
261
+
262
+ /**
263
+ * Validate image before processing
264
+ */
265
+ validateImage(file: File): { valid: boolean; error?: string } {
266
+ const maxSize = 50 * 1024 * 1024 // 50MB
267
+ const allowedTypes = ['image/png', 'image/jpeg', 'image/webp', 'image/gif']
268
+
269
+ if (file.size > maxSize) {
270
+ return { valid: false, error: 'File size exceeds 50MB limit' }
271
+ }
272
+
273
+ if (!allowedTypes.includes(file.type)) {
274
+ return { valid: false, error: 'Unsupported file type' }
275
+ }
276
+
277
+ return { valid: true }
278
+ }
279
+ }
280
+
281
+ export const processingAPI = new ProcessingAPI()
282
+
283
+ // Helper functions
284
+ export async function processImage(
285
+ file: File,
286
+ options?: ProcessingOptions,
287
+ onProgress?: (progress: number) => void
288
+ ): Promise<ProcessingResult> {
289
+ return processingAPI.processImage(file, options, onProgress)
290
+ }
291
+
292
+ export async function processBatch(
293
+ files: File[],
294
+ options?: ProcessingOptions
295
+ ): Promise<BatchJob> {
296
+ return processingAPI.processBatch(files, options)
297
+ }
298
+
299
+ export default processingAPI