feat: Enhance image handling in post generation and display logic
Browse files- backend/api/posts.py +20 -6
- frontend/src/pages/Posts.jsx +31 -27
- frontend/src/services/postService.js +11 -1
backend/api/posts.py
CHANGED
|
@@ -246,25 +246,36 @@ def get_job_status(job_id):
|
|
| 246 |
# Handle image_data which could be bytes or a URL string
|
| 247 |
image_data = job['result'].get('image_data')
|
| 248 |
if isinstance(image_data, bytes):
|
| 249 |
-
#
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 254 |
elif isinstance(image_data, dict):
|
| 255 |
# Handle the case where image_data is a dict from Gradio API
|
| 256 |
# The dict may contain 'path', 'url', or other keys
|
| 257 |
if image_data.get('url'):
|
| 258 |
response_data['image_url'] = image_data['url']
|
| 259 |
response_data['has_image_data'] = True
|
|
|
|
| 260 |
elif image_data.get('path'):
|
| 261 |
# If we have a path, we might need to serve it or convert it to a URL
|
| 262 |
# For now, we'll just indicate that image data exists
|
| 263 |
response_data['image_url'] = None
|
| 264 |
response_data['has_image_data'] = True
|
|
|
|
| 265 |
else:
|
| 266 |
response_data['image_url'] = None
|
| 267 |
response_data['has_image_data'] = image_data is not None
|
|
|
|
| 268 |
elif isinstance(image_data, str):
|
| 269 |
# Check if it's a local file path
|
| 270 |
if os.path.exists(image_data):
|
|
@@ -272,14 +283,17 @@ def get_job_status(job_id):
|
|
| 272 |
job['image_file_path'] = image_data
|
| 273 |
response_data['image_url'] = f"/api/posts/image/{job_id}" # API endpoint to serve the image
|
| 274 |
response_data['has_image_data'] = True
|
|
|
|
| 275 |
else:
|
| 276 |
-
# If it's a URL, use it directly
|
| 277 |
response_data['image_url'] = image_data
|
| 278 |
response_data['has_image_data'] = True
|
|
|
|
| 279 |
else:
|
| 280 |
# If it's None or other type
|
| 281 |
response_data['image_url'] = None
|
| 282 |
response_data['has_image_data'] = image_data is not None
|
|
|
|
| 283 |
else:
|
| 284 |
response_data['content'] = job['result']
|
| 285 |
response_data['image_url'] = None
|
|
|
|
| 246 |
# Handle image_data which could be bytes or a URL string
|
| 247 |
image_data = job['result'].get('image_data')
|
| 248 |
if isinstance(image_data, bytes):
|
| 249 |
+
# Convert bytes to base64 for sending in JSON
|
| 250 |
+
try:
|
| 251 |
+
# Encode bytes to base64 string
|
| 252 |
+
base64_image = base64.b64encode(image_data).decode('utf-8')
|
| 253 |
+
# Create a data URL for the image
|
| 254 |
+
response_data['image_url'] = f"data:image/png;base64,{base64_image}"
|
| 255 |
+
response_data['has_image_data'] = True
|
| 256 |
+
# Also include the raw image_data for the frontend
|
| 257 |
+
response_data['image_data'] = response_data['image_url']
|
| 258 |
+
except Exception as e:
|
| 259 |
+
current_app.logger.error(f"Error encoding image to base64: {str(e)}")
|
| 260 |
+
response_data['image_url'] = None
|
| 261 |
+
response_data['has_image_data'] = True
|
| 262 |
elif isinstance(image_data, dict):
|
| 263 |
# Handle the case where image_data is a dict from Gradio API
|
| 264 |
# The dict may contain 'path', 'url', or other keys
|
| 265 |
if image_data.get('url'):
|
| 266 |
response_data['image_url'] = image_data['url']
|
| 267 |
response_data['has_image_data'] = True
|
| 268 |
+
response_data['image_data'] = image_data['url']
|
| 269 |
elif image_data.get('path'):
|
| 270 |
# If we have a path, we might need to serve it or convert it to a URL
|
| 271 |
# For now, we'll just indicate that image data exists
|
| 272 |
response_data['image_url'] = None
|
| 273 |
response_data['has_image_data'] = True
|
| 274 |
+
response_data['image_data'] = image_data['path']
|
| 275 |
else:
|
| 276 |
response_data['image_url'] = None
|
| 277 |
response_data['has_image_data'] = image_data is not None
|
| 278 |
+
response_data['image_data'] = image_data
|
| 279 |
elif isinstance(image_data, str):
|
| 280 |
# Check if it's a local file path
|
| 281 |
if os.path.exists(image_data):
|
|
|
|
| 283 |
job['image_file_path'] = image_data
|
| 284 |
response_data['image_url'] = f"/api/posts/image/{job_id}" # API endpoint to serve the image
|
| 285 |
response_data['has_image_data'] = True
|
| 286 |
+
response_data['image_data'] = image_data
|
| 287 |
else:
|
| 288 |
+
# If it's a URL or base64 data, use it directly
|
| 289 |
response_data['image_url'] = image_data
|
| 290 |
response_data['has_image_data'] = True
|
| 291 |
+
response_data['image_data'] = image_data
|
| 292 |
else:
|
| 293 |
# If it's None or other type
|
| 294 |
response_data['image_url'] = None
|
| 295 |
response_data['has_image_data'] = image_data is not None
|
| 296 |
+
response_data['image_data'] = image_data
|
| 297 |
else:
|
| 298 |
response_data['content'] = job['result']
|
| 299 |
response_data['image_url'] = None
|
frontend/src/pages/Posts.jsx
CHANGED
|
@@ -344,20 +344,40 @@ const Posts = () => {
|
|
| 344 |
</div>
|
| 345 |
|
| 346 |
{/* Image Preview */}
|
| 347 |
-
{postImage &&
|
| 348 |
<div className="space-y-3 sm:space-y-4">
|
| 349 |
<label className="block text-xs sm:text-sm font-semibold text-gray-700">Generated Image</label>
|
| 350 |
<div className="relative border border-gray-300 rounded-xl overflow-hidden bg-gray-50">
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 361 |
<div className="absolute top-2 right-2 bg-black/50 text-white text-xs px-2 py-1 rounded">
|
| 362 |
AI Generated
|
| 363 |
</div>
|
|
@@ -365,22 +385,6 @@ const Posts = () => {
|
|
| 365 |
</div>
|
| 366 |
)}
|
| 367 |
|
| 368 |
-
{/* Image Data Exists Message */}
|
| 369 |
-
{postImage === 'HAS_IMAGE_DATA_BUT_NOT_URL' && (
|
| 370 |
-
<div className="space-y-3 sm:space-y-4">
|
| 371 |
-
<label className="block text-xs sm:text-sm font-semibold text-gray-700">Generated Image</label>
|
| 372 |
-
<div className="relative border border-gray-300 rounded-xl overflow-hidden bg-gray-50 p-4">
|
| 373 |
-
<div className="text-center text-gray-500">
|
| 374 |
-
<svg className="w-12 h-12 mx-auto mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 375 |
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
| 376 |
-
</svg>
|
| 377 |
-
<p className="text-sm">Image data exists but cannot be displayed directly</p>
|
| 378 |
-
<p className="text-xs mt-1">The image will be included when you publish the post</p>
|
| 379 |
-
</div>
|
| 380 |
-
</div>
|
| 381 |
-
</div>
|
| 382 |
-
)}
|
| 383 |
-
|
| 384 |
{/* Create Post Form */}
|
| 385 |
<form onSubmit={handleCreatePost} className="create-post-form grid grid-cols-1 sm:grid-cols-3 gap-3 sm:gap-4">
|
| 386 |
<div className="form-field sm:col-span-2">
|
|
|
|
| 344 |
</div>
|
| 345 |
|
| 346 |
{/* Image Preview */}
|
| 347 |
+
{postImage && (
|
| 348 |
<div className="space-y-3 sm:space-y-4">
|
| 349 |
<label className="block text-xs sm:text-sm font-semibold text-gray-700">Generated Image</label>
|
| 350 |
<div className="relative border border-gray-300 rounded-xl overflow-hidden bg-gray-50">
|
| 351 |
+
{/* Check if postImage is a data URL (base64) or a regular URL */}
|
| 352 |
+
{postImage.startsWith('data:image') ? (
|
| 353 |
+
// Display base64 image directly
|
| 354 |
+
<img
|
| 355 |
+
src={postImage}
|
| 356 |
+
alt="Generated post content"
|
| 357 |
+
className="w-full max-h-96 object-contain"
|
| 358 |
+
/>
|
| 359 |
+
) : postImage !== 'HAS_IMAGE_DATA_BUT_NOT_URL' ? (
|
| 360 |
+
// Display regular URL image
|
| 361 |
+
<img
|
| 362 |
+
src={postImage}
|
| 363 |
+
alt="Generated post content"
|
| 364 |
+
className="w-full max-h-96 object-contain"
|
| 365 |
+
onError={(e) => {
|
| 366 |
+
// If image fails to load, remove it from state
|
| 367 |
+
setPostImage(null);
|
| 368 |
+
e.target.style.display = 'none';
|
| 369 |
+
}}
|
| 370 |
+
/>
|
| 371 |
+
) : (
|
| 372 |
+
// Display message for image data that exists but can't be displayed directly
|
| 373 |
+
<div className="text-center text-gray-500 p-4">
|
| 374 |
+
<svg className="w-12 h-12 mx-auto mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 375 |
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
| 376 |
+
</svg>
|
| 377 |
+
<p className="text-sm">Image data exists but cannot be displayed directly</p>
|
| 378 |
+
<p className="text-xs mt-1">The image will be included when you publish the post</p>
|
| 379 |
+
</div>
|
| 380 |
+
)}
|
| 381 |
<div className="absolute top-2 right-2 bg-black/50 text-white text-xs px-2 py-1 rounded">
|
| 382 |
AI Generated
|
| 383 |
</div>
|
|
|
|
| 385 |
</div>
|
| 386 |
)}
|
| 387 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 388 |
{/* Create Post Form */}
|
| 389 |
<form onSubmit={handleCreatePost} className="create-post-form grid grid-cols-1 sm:grid-cols-3 gap-3 sm:gap-4">
|
| 390 |
<div className="form-field sm:col-span-2">
|
frontend/src/services/postService.js
CHANGED
|
@@ -94,7 +94,17 @@ class PostService {
|
|
| 94 |
imageData = jobData.image_url;
|
| 95 |
} else if (jobData.has_image_data) {
|
| 96 |
// Special marker for image data that exists but can't be displayed directly
|
| 97 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
}
|
| 99 |
|
| 100 |
// If content is an array, take the first element
|
|
|
|
| 94 |
imageData = jobData.image_url;
|
| 95 |
} else if (jobData.has_image_data) {
|
| 96 |
// Special marker for image data that exists but can't be displayed directly
|
| 97 |
+
// If we have actual image data (possibly base64), we should handle it
|
| 98 |
+
if (jobData.image_data) {
|
| 99 |
+
// Check if it's base64 encoded data
|
| 100 |
+
if (typeof jobData.image_data === 'string' && jobData.image_data.startsWith('data:image')) {
|
| 101 |
+
imageData = jobData.image_data; // Use the base64 data directly
|
| 102 |
+
} else {
|
| 103 |
+
imageData = 'HAS_IMAGE_DATA_BUT_NOT_URL';
|
| 104 |
+
}
|
| 105 |
+
} else {
|
| 106 |
+
imageData = 'HAS_IMAGE_DATA_BUT_NOT_URL';
|
| 107 |
+
}
|
| 108 |
}
|
| 109 |
|
| 110 |
// If content is an array, take the first element
|