Files
storycove/ASYNC_IMAGE_PROCESSING.md
Stefan Hardegger c291559366 Fix Image Processing
2025-09-28 20:06:52 +02:00

6.2 KiB

Async Image Processing Implementation

Overview

The image processing system has been updated to handle external images asynchronously, preventing timeouts when processing stories with many images. This provides real-time progress updates to users showing which images are being processed.

Backend Components

1. ImageProcessingProgressService

  • Tracks progress for individual story image processing sessions
  • Thread-safe with ConcurrentHashMap for multi-user support
  • Provides progress information: total images, processed count, current image, status, errors

2. AsyncImageProcessingService

  • Handles asynchronous image processing using Spring's @Async annotation
  • Counts external images before processing
  • Provides progress callbacks during processing
  • Updates story content when processing completes
  • Automatic cleanup of progress data after completion

3. Enhanced ImageService

  • Added processContentImagesWithProgress() method with callback support
  • Progress callbacks provide real-time updates during image download/processing
  • Maintains compatibility with existing synchronous processing

4. Updated StoryController

  • POST /api/stories and PUT /api/stories/{id} now trigger async image processing
  • GET /api/stories/{id}/image-processing-progress endpoint for progress polling
  • Processing starts immediately after story save and returns control to user

Frontend Components

1. ImageProcessingProgressTracker (Utility Class)

const tracker = new ImageProcessingProgressTracker(storyId);
tracker.onProgress((progress) => {
  console.log(`Processing ${progress.processedImages}/${progress.totalImages}`);
});
tracker.onComplete(() => console.log('Done!'));
tracker.start();

2. ImageProcessingProgressComponent (React Component)

<ImageProcessingProgressComponent
  storyId={storyId}
  autoStart={true}
  onComplete={() => refreshStory()}
/>

User Experience

Before (Synchronous)

  1. User saves story with external images
  2. Request hangs for 30+ seconds processing images
  3. Browser may timeout
  4. No feedback about progress
  5. User doesn't know if it's working

After (Asynchronous)

  1. User saves story with external images
  2. Save completes immediately
  3. Progress indicator appears: "Processing 5 images. Currently image 2 of 5..."
  4. User can continue using the application
  5. Progress updates every second
  6. Story automatically refreshes when processing completes

API Endpoints

Progress Endpoint

GET /api/stories/{id}/image-processing-progress

Response when processing:

{
  "isProcessing": true,
  "totalImages": 5,
  "processedImages": 2,
  "currentImageUrl": "https://example.com/image.jpg",
  "status": "Processing image 3 of 5",
  "progressPercentage": 40.0,
  "completed": false,
  "error": ""
}

Response when completed:

{
  "isProcessing": false,
  "totalImages": 5,
  "processedImages": 5,
  "currentImageUrl": "",
  "status": "Completed: 5 images processed",
  "progressPercentage": 100.0,
  "completed": true,
  "error": ""
}

Response when no processing:

{
  "isProcessing": false,
  "message": "No active image processing"
}

Integration Examples

React Hook Usage

import { useImageProcessingProgress } from '../utils/imageProcessingProgress';

function StoryEditor({ storyId }) {
  const { progress, isTracking, startTracking } = useImageProcessingProgress(storyId);

  const handleSave = async () => {
    await saveStory();
    startTracking(); // Start monitoring progress
  };

  return (
    <div>
      {isTracking && progress && (
        <div className="progress-indicator">
          Processing {progress.processedImages}/{progress.totalImages} images...
        </div>
      )}
      <button onClick={handleSave}>Save Story</button>
    </div>
  );
}

Manual Progress Tracking

// After saving a story with external images
const tracker = new ImageProcessingProgressTracker(storyId);

tracker.onProgress((progress) => {
  updateProgressBar(progress.progressPercentage);
  showStatus(progress.status);
  if (progress.currentImageUrl) {
    showCurrentImage(progress.currentImageUrl);
  }
});

tracker.onComplete((finalProgress) => {
  hideProgressBar();
  showNotification('Image processing completed!');
  refreshStoryContent(); // Reload story with processed images
});

tracker.onError((error) => {
  hideProgressBar();
  showError(`Image processing failed: ${error}`);
});

tracker.start();

Configuration

Polling Interval

Default: 1 second (1000ms)

const tracker = new ImageProcessingProgressTracker(storyId, 500); // Poll every 500ms

Timeout

Default: 5 minutes (300000ms)

const tracker = new ImageProcessingProgressTracker(storyId, 1000, 600000); // 10 minute timeout

Spring Async Configuration

The backend uses Spring's default async executor. For production, consider configuring a custom thread pool in your application properties:

spring:
  task:
    execution:
      pool:
        core-size: 4
        max-size: 8
        queue-capacity: 100

Error Handling

Backend Errors

  • Network timeouts downloading images
  • Invalid image formats
  • Disk space issues
  • All errors are logged and returned in progress status

Frontend Errors

  • Network failures during progress polling
  • Timeout if processing takes too long
  • Graceful degradation - user can continue working

Benefits

  1. No More Timeouts: Large image processing operations won't timeout HTTP requests
  2. Better UX: Users get real-time feedback about processing progress
  3. Improved Performance: Users can continue using the app while images process
  4. Error Visibility: Clear error messages when image processing fails
  5. Scalability: Multiple users can process images simultaneously without blocking

Future Enhancements

  1. WebSocket Support: Replace polling with WebSocket for real-time push updates
  2. Batch Processing: Queue multiple stories for batch image processing
  3. Retry Logic: Automatic retry for failed image downloads
  4. Progress Persistence: Save progress to database for recovery after server restart
  5. Image Optimization: Automatic resize/compress images during processing