154 lines
4.5 KiB
Python
154 lines
4.5 KiB
Python
# app/tasks/thumbnail.py
|
|
"""
|
|
Thumbnail generation tasks.
|
|
|
|
Handles generating thumbnails for images and videos,
|
|
both individually and in bulk.
|
|
"""
|
|
import logging
|
|
from datetime import datetime
|
|
|
|
from app.celery_app import celery
|
|
from app import db
|
|
from app.models import ImageRecord, ImportTask
|
|
|
|
log = logging.getLogger('celery.tasks.thumbnail')
|
|
|
|
|
|
@celery.task(bind=True, name='app.tasks.thumbnail.generate_thumbnail_task',
|
|
max_retries=2, default_retry_delay=30)
|
|
def generate_thumbnail_task(self, image_id: int, overwrite: bool = False):
|
|
"""
|
|
Generate thumbnail for a single image/video.
|
|
|
|
Args:
|
|
image_id: ID of the ImageRecord
|
|
overwrite: Whether to overwrite existing thumbnail
|
|
|
|
Returns:
|
|
dict with status and thumbnail path
|
|
"""
|
|
from app.utils.image_importer import generate_thumbnail, generate_video_thumbnail_mirrored
|
|
|
|
record = ImageRecord.query.get(image_id)
|
|
if not record:
|
|
log.warning(f"Image {image_id} not found")
|
|
return {'error': 'Image not found'}
|
|
|
|
try:
|
|
filepath = record.filepath
|
|
if not filepath:
|
|
return {'error': 'No filepath'}
|
|
|
|
# Check if thumbnail already exists and we're not overwriting
|
|
if record.thumb_path and not overwrite:
|
|
import os
|
|
if os.path.exists(record.thumb_path):
|
|
return {'status': 'exists', 'thumb_path': record.thumb_path}
|
|
|
|
log.debug(f"Generating thumbnail for: {filepath}")
|
|
|
|
is_video = filepath.lower().endswith(('.mp4', '.mov', '.webm', '.avi', '.mkv'))
|
|
|
|
if is_video:
|
|
thumb_path = generate_video_thumbnail_mirrored(filepath)
|
|
else:
|
|
thumb_path = generate_thumbnail(filepath, overwrite=overwrite)
|
|
|
|
if thumb_path:
|
|
record.thumb_path = thumb_path
|
|
db.session.commit()
|
|
log.info(f"Generated thumbnail: {thumb_path}")
|
|
return {'status': 'success', 'thumb_path': thumb_path}
|
|
else:
|
|
return {'status': 'failed', 'error': 'Thumbnail generation returned None'}
|
|
|
|
except Exception as e:
|
|
db.session.rollback()
|
|
log.error(f"Thumbnail generation failed for {image_id}: {e}", exc_info=True)
|
|
raise self.retry(exc=e)
|
|
|
|
|
|
@celery.task(name='app.tasks.thumbnail.regenerate_all_thumbnails')
|
|
def regenerate_all_thumbnails(batch_size: int = 100, overwrite: bool = True):
|
|
"""
|
|
Queue thumbnail regeneration for all images.
|
|
|
|
Processes images in batches to avoid memory issues.
|
|
|
|
Args:
|
|
batch_size: Number of images to process per batch
|
|
overwrite: Whether to overwrite existing thumbnails
|
|
|
|
Returns:
|
|
dict with count of queued tasks
|
|
"""
|
|
log.info("Starting bulk thumbnail regeneration")
|
|
|
|
count = 0
|
|
last_id = 0
|
|
|
|
while True:
|
|
records = ImageRecord.query.filter(
|
|
ImageRecord.id > last_id,
|
|
ImageRecord.filepath.isnot(None)
|
|
).order_by(ImageRecord.id.asc()).limit(batch_size).all()
|
|
|
|
if not records:
|
|
break
|
|
|
|
for record in records:
|
|
generate_thumbnail_task.delay(record.id, overwrite=overwrite)
|
|
count += 1
|
|
|
|
last_id = records[-1].id
|
|
log.info(f"Queued {count} thumbnail tasks so far...")
|
|
|
|
log.info(f"Thumbnail regeneration complete: queued {count} tasks")
|
|
return {'queued': count}
|
|
|
|
|
|
@celery.task(name='app.tasks.thumbnail.regenerate_missing_thumbnails')
|
|
def regenerate_missing_thumbnails(batch_size: int = 100):
|
|
"""
|
|
Generate thumbnails only for images that don't have them.
|
|
|
|
Args:
|
|
batch_size: Number of images to process per batch
|
|
|
|
Returns:
|
|
dict with count of queued tasks
|
|
"""
|
|
import os
|
|
|
|
log.info("Checking for missing thumbnails")
|
|
|
|
count = 0
|
|
last_id = 0
|
|
|
|
while True:
|
|
records = ImageRecord.query.filter(
|
|
ImageRecord.id > last_id,
|
|
ImageRecord.filepath.isnot(None)
|
|
).order_by(ImageRecord.id.asc()).limit(batch_size).all()
|
|
|
|
if not records:
|
|
break
|
|
|
|
for record in records:
|
|
# Check if thumbnail is missing
|
|
needs_thumb = False
|
|
if not record.thumb_path:
|
|
needs_thumb = True
|
|
elif not os.path.exists(record.thumb_path):
|
|
needs_thumb = True
|
|
|
|
if needs_thumb:
|
|
generate_thumbnail_task.delay(record.id, overwrite=False)
|
|
count += 1
|
|
|
|
last_id = records[-1].id
|
|
|
|
log.info(f"Missing thumbnail check complete: queued {count} tasks")
|
|
return {'queued': count}
|